diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index 9e22b9d1a..728ad460e 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -61,8 +61,13 @@ public final class UpdateManager { //Tick Callbacks tickStartCallbacks.forEach(Runnable::run); + ArrayList> futures; + // Server tick (instance/chunk/entity) - ArrayList> 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(); diff --git a/src/main/java/net/minestom/server/data/DataType.java b/src/main/java/net/minestom/server/data/DataType.java index 9136290c7..73fb1e3b5 100644 --- a/src/main/java/net/minestom/server/data/DataType.java +++ b/src/main/java/net/minestom/server/data/DataType.java @@ -16,7 +16,7 @@ public abstract class DataType { /** * Decode the data type * - * @param packetReader the data readerr + * @param packetReader the data reader * @return the decoded value */ public abstract T decode(PacketReader packetReader); diff --git a/src/main/java/net/minestom/server/data/type/InventoryData.java b/src/main/java/net/minestom/server/data/type/InventoryData.java index c9c873e8e..d1d9f56be 100644 --- a/src/main/java/net/minestom/server/data/type/InventoryData.java +++ b/src/main/java/net/minestom/server/data/type/InventoryData.java @@ -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 { @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 { @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()); } diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index 6b7fbcab3..c2dddd553 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -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(); + }); } } diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index f95b63792..0014b7ddb 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -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(); + }); } } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 44f591eb3..9d3cba702 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -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(); + }); } } } diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index b4b1c6b3a..e48b82235 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -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)); diff --git a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java index 916532af0..50dbdeb7f 100644 --- a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java +++ b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java @@ -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); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TagsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TagsPacket.java index 080a47f61..2f97da3ba 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TagsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TagsPacket.java @@ -33,7 +33,7 @@ public class TagsPacket implements ServerPacket { // name writer.writeSizedString(tag.getName().toString()); - Set values = tag.getValues(); + final Set values = tag.getValues(); // count writer.writeVarInt(values.size()); // entries diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index d0a5e34aa..f37ded7f2 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -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 { diff --git a/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java b/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java index ab53b1c6b..00a2fe55c 100644 --- a/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java +++ b/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java @@ -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> instanceChunksGroupMap = new ConcurrentHashMap<>(); + private final Map> instanceChunksGroupMap = new ConcurrentHashMap<>(); /** * Used to know to which instance is linked a Set of chunks */ - private Map> instanceInstanceMap = new ConcurrentHashMap<>(); + private final Map> 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 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 getInstanceMap(Instance instance) { - return instanceInstanceMap.computeIfAbsent(instance, inst -> new ConcurrentHashMap<>()); + return instanceInstanceMap.computeIfAbsent(instance, inst -> new HashMap<>()); } } diff --git a/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java b/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java index 0b14d072a..a5c086cb3 100644 --- a/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java @@ -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 instanceChunkMap = new HashMap<>(); + private Map 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> update(long time) { - ArrayList> futures = new ArrayList<>(); + @Override + public ArrayList> update(long time) { + ArrayList> futures = new ArrayList<>(); - for (Map.Entry 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()); + } } diff --git a/src/main/java/net/minestom/server/thread/ThreadProvider.java b/src/main/java/net/minestom/server/thread/ThreadProvider.java index 5c20943dd..f1bbd4e98 100644 --- a/src/main/java/net/minestom/server/thread/ThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/ThreadProvider.java @@ -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 *