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 //Tick Callbacks
tickStartCallbacks.forEach(Runnable::run); tickStartCallbacks.forEach(Runnable::run);
ArrayList<Future<?>> futures;
// Server tick (instance/chunk/entity) // 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) // Waiting players update (newly connected waiting to get into the server)
entityManager.updateWaitingPlayers(); entityManager.updateWaitingPlayers();

View File

@ -16,7 +16,7 @@ public abstract class DataType<T> {
/** /**
* Decode the data type * Decode the data type
* *
* @param packetReader the data readerr * @param packetReader the data reader
* @return the decoded value * @return the decoded value
*/ */
public abstract T decode(PacketReader packetReader); 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.inventory.InventoryType;
import net.minestom.server.network.packet.PacketReader; import net.minestom.server.network.packet.PacketReader;
import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.PacketWriter;
import org.jglrxavpok.hephaistos.nbt.NBTException;
public class InventoryData extends DataType<Inventory> { public class InventoryData extends DataType<Inventory> {
@Override @Override
public void encode(PacketWriter packetWriter, Inventory value) { public void encode(PacketWriter packetWriter, Inventory value) {
InventoryType inventoryType = value.getInventoryType(); final InventoryType inventoryType = value.getInventoryType();
int size = inventoryType.getAdditionalSlot(); final int size = inventoryType.getAdditionalSlot();
// Inventory title & type
packetWriter.writeSizedString(value.getTitle()); packetWriter.writeSizedString(value.getTitle());
packetWriter.writeSizedString(inventoryType.name()); packetWriter.writeSizedString(inventoryType.name());
// Write all item stacks
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
packetWriter.writeItemStack(value.getItemStack(i)); packetWriter.writeItemStack(value.getItemStack(i));
} }
@ -24,12 +25,13 @@ public class InventoryData extends DataType<Inventory> {
@Override @Override
public Inventory decode(PacketReader packetReader) { public Inventory decode(PacketReader packetReader) {
String title = packetReader.readSizedString(); final String title = packetReader.readSizedString();
InventoryType inventoryType = InventoryType.valueOf(packetReader.readSizedString()); final InventoryType inventoryType = InventoryType.valueOf(packetReader.readSizedString());
int size = inventoryType.getAdditionalSlot(); final int size = inventoryType.getAdditionalSlot();
Inventory inventory = new Inventory(inventoryType, title); Inventory inventory = new Inventory(inventoryType, title);
// Read all item stacks
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
inventory.setItemStack(i, packetReader.readSlot()); inventory.setItemStack(i, packetReader.readSlot());
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,9 +49,9 @@ public abstract class PlayerConnection {
tickCounter++; tickCounter++;
if (tickCounter % 20 == 0 && tickCounter > 0) { if (tickCounter % 20 == 0 && tickCounter > 0) {
tickCounter = 0; tickCounter = 0;
int i = packetCounter.get(); final int count = packetCounter.get();
packetCounter.set(0); packetCounter.set(0);
if (i > MinecraftServer.getRateLimit()) { if (count > MinecraftServer.getRateLimit()) {
if (connectionState == ConnectionState.LOGIN) { if (connectionState == ConnectionState.LOGIN) {
sendPacket(new LoginDisconnect("Too Many Packets")); sendPacket(new LoginDisconnect("Too Many Packets"));
} else { } 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.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.chunk.ChunkUtils;
@ -23,12 +22,12 @@ public class PerGroupChunkProvider extends ThreadProvider {
/** /**
* Chunk -> its chunk group * 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 * 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 @Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) { public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
@ -62,8 +61,11 @@ public class PerGroupChunkProvider extends ThreadProvider {
return; 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 // 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 // Add the newly loaded chunk to the group
final long chunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ); final long chunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ);
@ -115,8 +117,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
AtomicBoolean instanceUpdated = new AtomicBoolean(false); AtomicBoolean instanceUpdated = new AtomicBoolean(false);
// Update all the chunks + instances // Update all the chunks + instances
for (Map.Entry<LongSet, Instance> ent : instanceMap.entrySet()) { instanceMap.keySet().forEach(chunksIndexes -> {
final LongSet chunksIndexes = ent.getKey();
final boolean shouldUpdateInstance = updatedInstance.add(instance); final boolean shouldUpdateInstance = updatedInstance.add(instance);
futures.add(pool.submit(() -> { futures.add(pool.submit(() -> {
@ -131,21 +132,11 @@ public class PerGroupChunkProvider extends ThreadProvider {
while (!instanceUpdated.get()) { while (!instanceUpdated.get()) {
} }
for (long chunkIndex : chunksIndexes) { // Tick all this chunk group
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex); chunksIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
final Chunk chunk = instance.getChunk(chunkCoordinates[0], chunkCoordinates[1]); });
if (!ChunkUtils.isLoaded(chunk)) {
continue;
}
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
}));
}
});
}); });
return futures; return futures;
} }
@ -155,7 +146,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
} }
private Map<LongSet, Instance> getInstanceMap(Instance instance) { 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.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.chunk.ChunkUtils;
@ -16,54 +15,43 @@ import java.util.concurrent.Future;
*/ */
public class PerInstanceThreadProvider extends ThreadProvider { public class PerInstanceThreadProvider extends ThreadProvider {
private Map<Instance, LongSet> instanceChunkMap = new HashMap<>(); private Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
@Override @Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) { public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
// Add the loaded chunk to the instance chunks list // Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance); LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ); final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index); chunkCoordinates.add(index);
} }
@Override @Override
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) { public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance); LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ); final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list // Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index); chunkCoordinates.remove(index);
} }
@Override @Override
public ArrayList<Future<?>> update(long time) { public ArrayList<Future<?>> update(long time) {
ArrayList<Future<?>> futures = new ArrayList<>(); ArrayList<Future<?>> futures = new ArrayList<>();
for (Map.Entry<Instance, LongSet> entry : instanceChunkMap.entrySet()) { instanceChunkMap.forEach((instance, chunkIndexes) -> {
final Instance instance = entry.getKey();
final LongSet chunkIndexes = entry.getValue();
futures.add(pool.submit(() -> { futures.add(pool.submit(() -> {
updateInstance(instance, time); // Tick instance
updateInstance(instance, time);
// Tick chunks
chunkIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
});
});
return futures;
}
for (long chunkIndex : chunkIndexes) { private LongSet getChunkCoordinates(Instance instance) {
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex); return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
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());
}
} }

View File

@ -4,6 +4,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.*; import net.minestom.server.entity.*;
import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.thread.MinestomThread; import net.minestom.server.utils.thread.MinestomThread;
import java.util.ArrayList; import java.util.ArrayList;
@ -85,6 +86,24 @@ public abstract class ThreadProvider {
* INSTANCE UPDATE * 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 * Execute an instance tick
* *