mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-07 19:31:37 +01:00
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:
commit
1b7641c0e1
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user