Merge branches '1.16.2' and 'master' of https://github.com/Minestom/Minestom

 Conflicts:
	src/main/java/net/minestom/server/UpdateManager.java
	src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java
	src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java
This commit is contained in:
Eoghanmc22 2020-08-14 20:52:34 -04:00
commit 1b7641c0e1
13 changed files with 118 additions and 121 deletions

View File

@ -61,8 +61,13 @@ public final class UpdateManager {
//Tick Callbacks
tickStartCallbacks.forEach(Runnable::run);
ArrayList<Future<?>> futures;
// Server tick (instance/chunk/entity)
ArrayList<Future<?>> futures = threadProvider.update(time);
// Synchronize with the update manager instance, like the signal for chunk load/unload
synchronized (this) {
futures = threadProvider.update(time);
}
// Waiting players update (newly connected waiting to get into the server)
entityManager.updateWaitingPlayers();

View File

@ -16,7 +16,7 @@ public abstract class DataType<T> {
/**
* Decode the data type
*
* @param packetReader the data readerr
* @param packetReader the data reader
* @return the decoded value
*/
public abstract T decode(PacketReader packetReader);

View File

@ -5,18 +5,19 @@ import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryType;
import net.minestom.server.network.packet.PacketReader;
import net.minestom.server.network.packet.PacketWriter;
import org.jglrxavpok.hephaistos.nbt.NBTException;
public class InventoryData extends DataType<Inventory> {
@Override
public void encode(PacketWriter packetWriter, Inventory value) {
InventoryType inventoryType = value.getInventoryType();
int size = inventoryType.getAdditionalSlot();
final InventoryType inventoryType = value.getInventoryType();
final int size = inventoryType.getAdditionalSlot();
// Inventory title & type
packetWriter.writeSizedString(value.getTitle());
packetWriter.writeSizedString(inventoryType.name());
// Write all item stacks
for (int i = 0; i < size; i++) {
packetWriter.writeItemStack(value.getItemStack(i));
}
@ -24,12 +25,13 @@ public class InventoryData extends DataType<Inventory> {
@Override
public Inventory decode(PacketReader packetReader) {
String title = packetReader.readSizedString();
InventoryType inventoryType = InventoryType.valueOf(packetReader.readSizedString());
int size = inventoryType.getAdditionalSlot();
final String title = packetReader.readSizedString();
final InventoryType inventoryType = InventoryType.valueOf(packetReader.readSizedString());
final int size = inventoryType.getAdditionalSlot();
Inventory inventory = new Inventory(inventoryType, title);
// Read all item stacks
for (int i = 0; i < size; i++) {
inventory.setItemStack(i, packetReader.readSlot());
}

View File

@ -85,31 +85,27 @@ public class ItemEntity extends ObjectEntity {
if (getDistance(itemEntity) > mergeRange)
continue;
// Use the class as a monitor to prevent deadlock
// Shouldn't happen too often to be an issue
synchronized (ItemEntity.class) {
final ItemStack itemStackEntity = itemEntity.getItemStack();
final ItemStack itemStackEntity = itemEntity.getItemStack();
final StackingRule stackingRule = itemStack.getStackingRule();
final boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity);
final StackingRule stackingRule = itemStack.getStackingRule();
final boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity);
if (!canStack)
continue;
if (!canStack)
continue;
final int totalAmount = stackingRule.getAmount(itemStack) + stackingRule.getAmount(itemStackEntity);
final boolean canApply = stackingRule.canApply(itemStack, totalAmount);
final int totalAmount = stackingRule.getAmount(itemStack) + stackingRule.getAmount(itemStackEntity);
final boolean canApply = stackingRule.canApply(itemStack, totalAmount);
if (!canApply)
continue;
if (!canApply)
continue;
final ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount);
final ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount);
EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result);
callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> {
setItemStack(entityItemMergeEvent.getResult());
itemEntity.remove();
});
}
EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result);
callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> {
setItemStack(entityItemMergeEvent.getResult());
itemEntity.remove();
});
}
}

View File

@ -100,20 +100,18 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
final BoundingBox itemBoundingBox = itemEntity.getBoundingBox();
if (expandedBoundingBox.intersect(itemBoundingBox)) {
synchronized (itemEntity) {
if (itemEntity.shouldRemove() || itemEntity.isRemoveScheduled())
continue;
final ItemStack item = itemEntity.getItemStack();
PickupItemEvent pickupItemEvent = new PickupItemEvent(item);
callCancellableEvent(PickupItemEvent.class, pickupItemEvent, () -> {
CollectItemPacket collectItemPacket = new CollectItemPacket();
collectItemPacket.collectedEntityId = itemEntity.getEntityId();
collectItemPacket.collectorEntityId = getEntityId();
collectItemPacket.pickupItemCount = item.getAmount();
sendPacketToViewersAndSelf(collectItemPacket);
entity.remove();
});
}
if (itemEntity.shouldRemove() || itemEntity.isRemoveScheduled())
continue;
final ItemStack item = itemEntity.getItemStack();
PickupItemEvent pickupItemEvent = new PickupItemEvent(item);
callCancellableEvent(PickupItemEvent.class, pickupItemEvent, () -> {
CollectItemPacket collectItemPacket = new CollectItemPacket();
collectItemPacket.collectedEntityId = itemEntity.getEntityId();
collectItemPacket.collectorEntityId = getEntityId();
collectItemPacket.pickupItemCount = item.getAmount();
sendPacketToViewersAndSelf(collectItemPacket);
entity.remove();
});
}
}
}

View File

@ -227,7 +227,7 @@ public class Player extends LivingEntity implements CommandSender {
recipesIdentifier.add(recipe.getRecipeId());
}
if (!recipesIdentifier.isEmpty()) {
String[] identifiers = recipesIdentifier.toArray(new String[0]);
final String[] identifiers = recipesIdentifier.toArray(new String[0]);
UnlockRecipesPacket unlockRecipesPacket = new UnlockRecipesPacket();
unlockRecipesPacket.mode = 0;
unlockRecipesPacket.recipesId = identifiers;
@ -321,15 +321,13 @@ public class Player extends LivingEntity implements CommandSender {
final ExperienceOrb experienceOrb = (ExperienceOrb) entity;
final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox();
if (expandedBoundingBox.intersect(itemBoundingBox)) {
synchronized (experienceOrb) {
if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled())
continue;
PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb);
callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> {
short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player
entity.remove();
});
}
if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled())
continue;
PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb);
callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> {
short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player
entity.remove();
});
}
}
}

View File

@ -128,8 +128,8 @@ public class ItemStack implements DataContainer {
* @param itemStack The ItemStack to compare to
* @return true if both items are similar
*/
public synchronized boolean isSimilar(ItemStack itemStack) {
synchronized (itemStack) {
public boolean isSimilar(ItemStack itemStack) {
synchronized (ItemStack.class) {
final ColoredText itemDisplayName = itemStack.getDisplayName();
final boolean displayNameCheck = (displayName == null && itemDisplayName == null) ||
(displayName != null && itemDisplayName != null && displayName.equals(itemDisplayName));

View File

@ -99,7 +99,7 @@ public class PlayerDiggingListener {
if (itemUpdateStateEvent == null) {
player.refreshActiveHand(true, false, false);
} else {
boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF;
final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF;
player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), isOffHand, false);
}

View File

@ -33,7 +33,7 @@ public class TagsPacket implements ServerPacket {
// name
writer.writeSizedString(tag.getName().toString());
Set<NamespaceID> values = tag.getValues();
final Set<NamespaceID> values = tag.getValues();
// count
writer.writeVarInt(values.size());
// entries

View File

@ -49,9 +49,9 @@ public abstract class PlayerConnection {
tickCounter++;
if (tickCounter % 20 == 0 && tickCounter > 0) {
tickCounter = 0;
int i = packetCounter.get();
final int count = packetCounter.get();
packetCounter.set(0);
if (i > MinecraftServer.getRateLimit()) {
if (count > MinecraftServer.getRateLimit()) {
if (connectionState == ConnectionState.LOGIN) {
sendPacket(new LoginDisconnect("Too Many Packets"));
} else {

View File

@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
@ -23,12 +22,12 @@ public class PerGroupChunkProvider extends ThreadProvider {
/**
* Chunk -> its chunk group
*/
private Map<Instance, Long2ObjectMap<LongSet>> instanceChunksGroupMap = new ConcurrentHashMap<>();
private final Map<Instance, Long2ObjectMap<LongSet>> instanceChunksGroupMap = new ConcurrentHashMap<>();
/**
* Used to know to which instance is linked a Set of chunks
*/
private Map<Instance, Map<LongSet, Instance>> instanceInstanceMap = new ConcurrentHashMap<>();
private final Map<Instance, Map<LongSet, Instance>> instanceInstanceMap = new ConcurrentHashMap<>();
@Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
@ -62,8 +61,11 @@ public class PerGroupChunkProvider extends ThreadProvider {
return;
}
// The size of the final list, used as the initial capacity
final int size = neighboursGroups.stream().mapToInt(value -> value.size()).sum() + 1;
// Represent the merged group of all the neighbours
LongSet finalGroup = new LongArraySet();
LongSet finalGroup = new LongArraySet(size);
// Add the newly loaded chunk to the group
final long chunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ);
@ -115,8 +117,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
AtomicBoolean instanceUpdated = new AtomicBoolean(false);
// Update all the chunks + instances
for (Map.Entry<LongSet, Instance> ent : instanceMap.entrySet()) {
final LongSet chunksIndexes = ent.getKey();
instanceMap.keySet().forEach(chunksIndexes -> {
final boolean shouldUpdateInstance = updatedInstance.add(instance);
futures.add(pool.submit(() -> {
@ -131,21 +132,11 @@ public class PerGroupChunkProvider extends ThreadProvider {
while (!instanceUpdated.get()) {
}
for (long chunkIndex : chunksIndexes) {
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex);
final Chunk chunk = instance.getChunk(chunkCoordinates[0], chunkCoordinates[1]);
if (!ChunkUtils.isLoaded(chunk)) {
continue;
}
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
}));
}
// Tick all this chunk group
chunksIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
});
});
});
return futures;
}
@ -155,7 +146,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
}
private Map<LongSet, Instance> getInstanceMap(Instance instance) {
return instanceInstanceMap.computeIfAbsent(instance, inst -> new ConcurrentHashMap<>());
return instanceInstanceMap.computeIfAbsent(instance, inst -> new HashMap<>());
}
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.thread;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
@ -16,54 +15,43 @@ import java.util.concurrent.Future;
*/
public class PerInstanceThreadProvider extends ThreadProvider {
private Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
private Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
@Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
// Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index);
}
@Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
// Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index);
}
@Override
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index);
@Override
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index);
}
}
@Override
public ArrayList<Future<?>> update(long time) {
ArrayList<Future<?>> futures = new ArrayList<>();
@Override
public ArrayList<Future<?>> update(long time) {
ArrayList<Future<?>> futures = new ArrayList<>();
for (Map.Entry<Instance, LongSet> entry : instanceChunkMap.entrySet()) {
final Instance instance = entry.getKey();
final LongSet chunkIndexes = entry.getValue();
instanceChunkMap.forEach((instance, chunkIndexes) -> {
futures.add(pool.submit(() -> {
updateInstance(instance, time);
futures.add(pool.submit(() -> {
// Tick instance
updateInstance(instance, time);
// Tick chunks
chunkIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
});
});
return futures;
}
for (long chunkIndex : chunkIndexes) {
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex);
final Chunk chunk = instance.getChunk(chunkCoordinates[0], chunkCoordinates[1]);
if (!ChunkUtils.isLoaded(chunk))
continue;
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
}));
}
return futures;
}
private LongSet getChunkCoordinates(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
}
private LongSet getChunkCoordinates(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
}
}

View File

@ -4,6 +4,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.*;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.thread.MinestomThread;
import java.util.ArrayList;
@ -85,6 +86,24 @@ public abstract class ThreadProvider {
* INSTANCE UPDATE
*/
/**
* Process a whole tick for a chunk
*
* @param instance the instance of the chunk
* @param chunkIndex the index of the chunk {@link ChunkUtils#getChunkIndex(int, int)}
* @param time the time of the update in milliseconds
*/
protected void processChunkTick(Instance instance, long chunkIndex, long time) {
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex);
final Chunk chunk = instance.getChunk(chunkCoordinates[0], chunkCoordinates[1]);
if (!ChunkUtils.isLoaded(chunk))
return;
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
/**
* Execute an instance tick
*