diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index a3fbac535..95858fe6b 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -9,9 +9,8 @@ import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceManager; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.packet.server.play.KeepAlivePacket; -import net.minestom.server.thread.DefaultThreadProvider; +import net.minestom.server.thread.PerInstanceThreadProvider; import net.minestom.server.thread.ThreadProvider; -import net.minestom.server.timer.SchedulerManager; import net.minestom.server.utils.thread.MinestomThread; import net.minestom.server.utils.validate.Check; @@ -25,7 +24,12 @@ public final class UpdateManager { private ExecutorService mainUpdate = new MinestomThread(1, MinecraftServer.THREAD_NAME_MAIN_UPDATE); private boolean stopRequested; - private ThreadProvider threadProvider = new DefaultThreadProvider(); + private ThreadProvider threadProvider; + + { + threadProvider = new PerInstanceThreadProvider(); + //threadProvider = new PerGroupChunkProvider(); + } /** * Should only be created in MinecraftServer @@ -46,6 +50,7 @@ public final class UpdateManager { currentTime = System.nanoTime(); // Server tick + //long testTime = System.nanoTime(); threadProvider.start(); for (Instance instance : instanceManager.getInstances()) { for (Chunk chunk : instance.getChunks()) { @@ -53,6 +58,8 @@ public final class UpdateManager { } } threadProvider.end(); + threadProvider.update(); + //System.out.println("time: " + (System.nanoTime() - testTime)); // Waiting players update entityManager.updateWaitingPlayers(); diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index 01743a618..4540ec3bb 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -145,24 +145,49 @@ public class ItemEntity extends ObjectEntity { return 1; } + /** + * Get the item stack on ground + * + * @return the item stack + */ public ItemStack getItemStack() { return itemStack; } + /** + * Change the item stack on ground + * + * @param itemStack the item stack + */ public void setItemStack(ItemStack itemStack) { this.itemStack = itemStack; sendMetadataIndex(7); // Refresh the ItemStack for viewers } + /** + * Get if the item is currently pickable + *

+ * {@link #setPickable(boolean)} needs to be true and the delay {@link #getPickupDelay()} + * to be long gone + * + * @return true if the item is pickable, false otherwise + */ public boolean isPickable() { return pickable && (System.currentTimeMillis() - getSpawnTime() >= pickupDelay); } + /** + * Make the item pickable + * + * @param pickable true to make the item pickable, false otherwise + */ public void setPickable(boolean pickable) { this.pickable = pickable; } /** + * Get if the item is mergeable + * * @return true if the entity is mergeable, false otherwise */ public boolean isMergeable() { @@ -179,16 +204,28 @@ public class ItemEntity extends ObjectEntity { this.mergeable = mergeable; } + /** + * Get the merge range + * + * @return the merge range + */ public float getMergeRange() { return mergeRange; } + /** + * Change the merge range + * + * @param mergeRange the merge range + */ public void setMergeRange(float mergeRange) { this.mergeRange = mergeRange; } /** - * @return the pickup delay in milliseconds, defined by {@link #setPickupDelay(long, TimeUnit)} + * Get the pickup delay in milliseconds, defined by {@link #setPickupDelay(long, TimeUnit)} + * + * @return the pickup delay */ public long getPickupDelay() { return pickupDelay; diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 4cd389c0f..2591da4cf 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -220,6 +220,8 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { } /** + * Damage the entity by a value, the type of the damage also has to be specified + * * @param type the damage type * @param value the amount of damage * @return true if damage has been applied, false if it didn't @@ -348,7 +350,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { } // Equipments - public void syncEquipments(PlayerConnection connection) { + protected void syncEquipments(PlayerConnection connection) { for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) { final EntityEquipmentPacket entityEquipmentPacket = getEquipmentPacket(slot); if (entityEquipmentPacket == null) @@ -357,7 +359,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { } } - public void syncEquipments() { + protected void syncEquipments() { for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) { syncEquipment(slot); } diff --git a/src/main/java/net/minestom/server/entity/type/EntityBat.java b/src/main/java/net/minestom/server/entity/type/EntityBat.java index a47c5cd55..7dce1bb10 100644 --- a/src/main/java/net/minestom/server/entity/type/EntityBat.java +++ b/src/main/java/net/minestom/server/entity/type/EntityBat.java @@ -34,10 +34,20 @@ public class EntityBat extends EntityCreature { } } + /** + * Get if the bat is hanging + * + * @return true if the bat is hanging, false otherwise + */ public boolean isHanging() { return hanging; } + /** + * Make the bat hanging or cancel + * + * @param hanging true to make the bat hanging, false otherwise + */ public void setHanging(boolean hanging) { this.hanging = hanging; } diff --git a/src/main/java/net/minestom/server/entity/type/EntityBoat.java b/src/main/java/net/minestom/server/entity/type/EntityBoat.java index 32b6fc2b9..5bba17117 100644 --- a/src/main/java/net/minestom/server/entity/type/EntityBoat.java +++ b/src/main/java/net/minestom/server/entity/type/EntityBoat.java @@ -4,17 +4,20 @@ import net.minestom.server.entity.EntityType; import net.minestom.server.entity.ObjectEntity; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.utils.Position; +import net.minestom.server.utils.validate.Check; import java.util.function.Consumer; public class EntityBoat extends ObjectEntity { + private BoatType boatType; private boolean leftPaddleTurning; private boolean rightPaddleTurning; public EntityBoat(Position spawnPosition) { super(EntityType.BOAT, spawnPosition); setBoundingBox(1.375f, 0.5625f, 1.375f); + this.boatType = BoatType.OAK; } @Override @@ -26,6 +29,7 @@ public class EntityBoat extends ObjectEntity { public Consumer getMetadataConsumer() { return packet -> { super.getMetadataConsumer().accept(packet); + fillMetadataIndex(packet, 10); fillMetadataIndex(packet, 11); fillMetadataIndex(packet, 12); @@ -36,7 +40,11 @@ public class EntityBoat extends ObjectEntity { @Override protected void fillMetadataIndex(PacketWriter packet, int index) { super.fillMetadataIndex(packet, index); - if (index == 11) { + if (index == 10) { + packet.writeByte((byte) 10); + packet.writeByte(METADATA_VARINT); + packet.writeVarInt(boatType.ordinal()); + } else if (index == 11) { packet.writeByte((byte) 11); packet.writeByte(METADATA_BOOLEAN); packet.writeBoolean(leftPaddleTurning); @@ -47,6 +55,26 @@ public class EntityBoat extends ObjectEntity { } } + /** + * Get the boat type + * + * @return the boat type + */ + public BoatType getBoatType() { + return boatType; + } + + /** + * Change the boat type + * + * @param boatType the new boat type + */ + public void setBoatType(BoatType boatType) { + Check.notNull(boatType, "The boat type cannot be null"); + this.boatType = boatType; + sendMetadataIndex(10); + } + public void refreshPaddle(boolean left, boolean right) { this.leftPaddleTurning = left; this.rightPaddleTurning = right; @@ -54,4 +82,9 @@ public class EntityBoat extends ObjectEntity { sendMetadataIndex(12); } + public enum BoatType { + OAK, SPRUCE, BIRCH, JUNGLE, ACACIA, DARK_OAK + } + + } diff --git a/src/main/java/net/minestom/server/entity/type/EntityItemFrame.java b/src/main/java/net/minestom/server/entity/type/EntityItemFrame.java new file mode 100644 index 000000000..79155e424 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/type/EntityItemFrame.java @@ -0,0 +1,100 @@ +package net.minestom.server.entity.type; + +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.ObjectEntity; +import net.minestom.server.item.ItemStack; +import net.minestom.server.network.packet.PacketWriter; +import net.minestom.server.utils.Position; +import net.minestom.server.utils.Rotation; +import net.minestom.server.utils.item.ItemStackUtils; + +import java.util.function.Consumer; + +// FIXME: https://wiki.vg/Object_Data#Item_Frame_.28id_71.29 +// "You have to set both Orientation and Yaw/Pitch accordingly, otherwise it will not work." +public class EntityItemFrame extends ObjectEntity { + + private ItemFrameOrientation orientation; + private ItemStack itemStack; + private Rotation rotation; + + public EntityItemFrame(Position spawnPosition, ItemFrameOrientation orientation) { + super(EntityType.ITEM_FRAME, spawnPosition); + this.orientation = orientation; + this.rotation = Rotation.NONE; + } + + @Override + public Consumer getMetadataConsumer() { + return packet -> { + super.getMetadataConsumer().accept(packet); + fillMetadataIndex(packet, 7); + fillMetadataIndex(packet, 8); + }; + } + + @Override + protected void fillMetadataIndex(PacketWriter packet, int index) { + super.fillMetadataIndex(packet, index); + if (index == 7) { + packet.writeByte((byte) 7); + packet.writeByte(METADATA_SLOT); + packet.writeItemStack(ItemStackUtils.notNull(itemStack)); + } else if (index == 8) { + packet.writeByte((byte) 8); + packet.writeByte(METADATA_VARINT); + packet.writeVarInt(rotation.ordinal()); + } + } + + @Override + public int getObjectData() { + return orientation.ordinal(); + } + + /** + * Get the item stack in the frame + * + * @return the item stack in the frame + */ + public ItemStack getItemStack() { + return itemStack; + } + + /** + * Change the item stack in the frame + * + * @param itemStack the new item stack in the frame + */ + public void setItemStack(ItemStack itemStack) { + this.itemStack = itemStack; + sendMetadataIndex(7); + } + + /** + * Get the item rotation + * + * @return the item rotation + */ + public Rotation getRotation() { + return rotation; + } + + /** + * Change the item rotation + * + * @param rotation the new item rotation + */ + public void setRotation(Rotation rotation) { + this.rotation = rotation; + sendMetadataIndex(8); + } + + /** + * Represent the orientation of the frame + */ + public enum ItemFrameOrientation { + DOWN, UP, NORTH, SOUTH, WEST, EAST + } + +} diff --git a/src/main/java/net/minestom/server/entity/type/EntityPig.java b/src/main/java/net/minestom/server/entity/type/EntityPig.java index e992205a3..50690b409 100644 --- a/src/main/java/net/minestom/server/entity/type/EntityPig.java +++ b/src/main/java/net/minestom/server/entity/type/EntityPig.java @@ -34,10 +34,20 @@ public class EntityPig extends EntityCreature { } } + /** + * Get if the pig has a saddle + * + * @return true if the pig has a saddle, false otherwise + */ public boolean hasSaddle() { return saddle; } + /** + * Set a saddle to the pig + * + * @param saddle true to add a saddle, false to remove it + */ public void setSaddle(boolean saddle) { this.saddle = saddle; sendMetadataIndex(16); diff --git a/src/main/java/net/minestom/server/entity/type/EntityPigZombie.java b/src/main/java/net/minestom/server/entity/type/EntityPigZombie.java index dd6397d6e..cb79e4933 100644 --- a/src/main/java/net/minestom/server/entity/type/EntityPigZombie.java +++ b/src/main/java/net/minestom/server/entity/type/EntityPigZombie.java @@ -40,10 +40,20 @@ public class EntityPigZombie extends EntityCreature { } } + /** + * Get if the pig zombie is a baby + * + * @return true if it is a baby, false otherwise + */ public boolean isBaby() { return baby; } + /** + * Set the pig zombie a baby or adult + * + * @param baby true to make it a baby, false otherwise + */ public void setBaby(boolean baby) { this.baby = baby; sendMetadataIndex(15); diff --git a/src/main/java/net/minestom/server/entity/type/EntitySpider.java b/src/main/java/net/minestom/server/entity/type/EntitySpider.java index ea8cf6d8b..f2b214f39 100644 --- a/src/main/java/net/minestom/server/entity/type/EntitySpider.java +++ b/src/main/java/net/minestom/server/entity/type/EntitySpider.java @@ -34,10 +34,20 @@ public class EntitySpider extends EntityCreature { } } + /** + * Get if the spider is climbing + * + * @return true if the spider is climbing, false otherwise + */ public boolean isClimbing() { return climbing; } + /** + * Make the spider climbs + * + * @param climbing true to make the spider climbs, false otherwise + */ public void setClimbing(boolean climbing) { this.climbing = climbing; sendMetadataIndex(15); diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 0051792dc..1018881f0 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -365,6 +365,15 @@ public class InstanceContainer extends Instance { saveChunksToStorageFolder(callback); } + /** + * Save the instance without callback + * + * @see #saveInstance(Runnable) + */ + public void saveInstance() { + saveInstance(null); + } + @Override public void saveChunkToStorageFolder(Chunk chunk, Runnable callback) { Check.notNull(getStorageFolder(), "You cannot save the chunk if no StorageFolder has been defined"); diff --git a/src/main/java/net/minestom/server/inventory/Inventory.java b/src/main/java/net/minestom/server/inventory/Inventory.java index dfd98db8d..6f1455992 100644 --- a/src/main/java/net/minestom/server/inventory/Inventory.java +++ b/src/main/java/net/minestom/server/inventory/Inventory.java @@ -160,6 +160,16 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View return false; } + @Override + public void clear() { + // Clear the item array + for (int i = 0; i < getSize(); i++) { + setItemStackInternal(i, ItemStack.getAirItem()); + } + // Send the cleared inventory to viewers + update(); + } + @Override public ItemStack getItemStack(int slot) { return itemStacks[slot]; @@ -286,6 +296,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View protected void setItemStackInternal(int slot, ItemStack itemStack) { itemStacks[slot] = itemStack; + this.windowItemsBufferUpdated = false; } /** @@ -606,11 +617,4 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View update(player); } } - - public void clear() { - // TODO: optimize by sending whole inventory at once? (will need to change to setItemStackInternal) - for (int i = 0; i < getSize(); i++) { - setItemStack(i, ItemStack.getAirItem()); - } - } } diff --git a/src/main/java/net/minestom/server/inventory/InventoryModifier.java b/src/main/java/net/minestom/server/inventory/InventoryModifier.java index eede7e99a..31afc4606 100644 --- a/src/main/java/net/minestom/server/inventory/InventoryModifier.java +++ b/src/main/java/net/minestom/server/inventory/InventoryModifier.java @@ -26,6 +26,11 @@ public interface InventoryModifier { */ boolean addItemStack(ItemStack itemStack); + /** + * Clear the inventory + */ + void clear(); + /** * Get the item at the specified slot * diff --git a/src/main/java/net/minestom/server/inventory/PlayerInventory.java b/src/main/java/net/minestom/server/inventory/PlayerInventory.java index 23775bfe6..27bda5169 100644 --- a/src/main/java/net/minestom/server/inventory/PlayerInventory.java +++ b/src/main/java/net/minestom/server/inventory/PlayerInventory.java @@ -121,6 +121,16 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler return false; } + @Override + public void clear() { + // Clear the item array + for (int i = 0; i < getSize(); i++) { + setItemStackInternal(i, ItemStack.getAirItem()); + } + // Send the cleared inventory to the inventory's owner + update(); + } + @Override public int getSize() { return INVENTORY_SIZE; @@ -282,6 +292,10 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler //refreshSlot(slot); } + protected void setItemStackInternal(int slot, ItemStack itemStack) { + items[slot] = itemStack; + } + /** * Set an item from a packet slot * diff --git a/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java b/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java index 5cfa8618f..7ef7f2aae 100644 --- a/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java @@ -6,11 +6,28 @@ import net.minestom.server.inventory.InventoryType; public class AnvilInventory extends Inventory { + private short repairCost; + public AnvilInventory(String title) { super(InventoryType.ANVIL, title); } + /** + * Get the anvil repair cost + * + * @return the repair cost + */ + public short getRepairCost() { + return repairCost; + } + + /** + * Set the anvil repair cost + * + * @param cost the new anvil repair cost + */ public void setRepairCost(short cost) { + this.repairCost = repairCost; sendProperty(InventoryProperty.ANVIL_REPAIR_COST, cost); } } diff --git a/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java b/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java index 6904bcc23..4aefda2a6 100644 --- a/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java @@ -7,19 +7,68 @@ import net.minestom.server.potion.PotionType; public class BeaconInventory extends Inventory { + private short powerLevel; + private PotionType firstPotionEffect; + private PotionType secondPotionEffect; + public BeaconInventory(String title) { super(InventoryType.BEACON, title); } + /** + * Get the beacon power level + * + * @return the power level + */ + public short getPowerLevel() { + return powerLevel; + } + + /** + * Change the beacon power level + * + * @param powerLevel the new beacon power level + */ public void setPowerLevel(short powerLevel) { + this.powerLevel = powerLevel; sendProperty(InventoryProperty.BEACON_POWER_LEVEL, powerLevel); } + /** + * Get the first potion effect + * + * @return the first potion effect, can be null + */ + public PotionType getFirstPotionEffect() { + return firstPotionEffect; + } + + /** + * Change the first potion effect + * + * @param firstPotionEffect the new first potion effect, can be null + */ public void setFirstPotionEffect(PotionType firstPotionEffect) { + this.firstPotionEffect = firstPotionEffect; sendProperty(InventoryProperty.BEACON_FIRST_POTION, (short) firstPotionEffect.getId()); } + /** + * Get the second potion effect + * + * @return the second potion effect, can be null + */ + public PotionType getSecondPotionEffect() { + return secondPotionEffect; + } + + /** + * Change the second potion effect + * + * @param secondPotionEffect the new second potion effect, can be null + */ public void setSecondPotionEffect(PotionType secondPotionEffect) { + this.secondPotionEffect = secondPotionEffect; sendProperty(InventoryProperty.BEACON_SECOND_POTION, (short) secondPotionEffect.getId()); } diff --git a/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java b/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java index e35d0637f..7f9540e07 100644 --- a/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java @@ -6,15 +6,48 @@ import net.minestom.server.inventory.InventoryType; public class BrewingStandInventory extends Inventory { + private short brewTime; + private short fuelTime; + public BrewingStandInventory(String title) { super(InventoryType.BREWING_STAND, title); } + /** + * Get the brewing stand brew time + * + * @return the brew time in tick + */ + public short getBrewTime() { + return brewTime; + } + + /** + * Change the brew time + * + * @param brewTime the new brew time in tick + */ public void setBrewTime(short brewTime) { + this.brewTime = brewTime; sendProperty(InventoryProperty.BREWING_STAND_BREW_TIME, brewTime); } + /** + * Get the brewing stand fuel time + * + * @return the fuel time in tick + */ + public short getFuelTime() { + return fuelTime; + } + + /** + * Change the fuel time + * + * @param fuelTime the new fuel time in tick + */ public void setFuelTime(short fuelTime) { + this.fuelTime = fuelTime; sendProperty(InventoryProperty.BREWING_STAND_FUEL_TIME, fuelTime); } diff --git a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java index 60f38d4f0..54453c4ad 100644 --- a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java @@ -1,11 +1,143 @@ package net.minestom.server.inventory.type; import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.Enchantment; public class EnchantmentTableInventory extends Inventory { + private short[] levelRequirements = new short[EnchantmentSlot.values().length]; + private short seed; + private short[] enchantmentShown = new short[EnchantmentSlot.values().length]; + private short[] enchantmentLevel = new short[EnchantmentSlot.values().length]; + public EnchantmentTableInventory(String title) { super(InventoryType.ENCHANTMENT, title); } + + /** + * Get the level requirement in a slot + * + * @param enchantmentSlot the slot to check the level requirement + * @return the level requirement of the slot + */ + public short getLevelRequirement(EnchantmentSlot enchantmentSlot) { + return levelRequirements[enchantmentSlot.ordinal()]; + } + + /** + * Set the level requirement of a slot + * + * @param enchantmentSlot the slot + * @param level the level + */ + public void setLevelRequirement(EnchantmentSlot enchantmentSlot, short level) { + switch (enchantmentSlot) { + case TOP: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_TOP, level); + break; + case MIDDLE: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_MIDDLE, level); + break; + case BOTTOM: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_BOTTOM, level); + break; + } + this.levelRequirements[enchantmentSlot.ordinal()] = level; + } + + /** + * Get the enchantment seed + * + * @return the enchantment seed + */ + public short getSeed() { + return seed; + } + + /** + * Set the enchantment seed + * + * @param seed the enchantment seed + */ + public void setSeed(short seed) { + this.seed = seed; + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_SEED, seed); + } + + /** + * Get the enchantment shown in a slot + * + * @param enchantmentSlot the enchantment slot + * @return the enchantment shown in the slot, null if it is hidden + */ + public Enchantment getEnchantmentShown(EnchantmentSlot enchantmentSlot) { + final short id = enchantmentShown[enchantmentSlot.ordinal()]; + if (id == -1) + return null; + return Enchantment.fromId(id); + } + + /** + * Set the enchantment shown in a slot + *

+ * Can be set to null to hide it + * + * @param enchantmentSlot the enchantment slot + * @param enchantment the enchantment + */ + public void setEnchantmentShown(EnchantmentSlot enchantmentSlot, Enchantment enchantment) { + final short id = enchantment == null ? -1 : (short) enchantment.getId(); + switch (enchantmentSlot) { + case TOP: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_TOP, id); + break; + case MIDDLE: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_MIDDLE, id); + break; + case BOTTOM: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_BOTTOM, id); + break; + } + this.enchantmentShown[enchantmentSlot.ordinal()] = id; + } + + /** + * Get the enchantment level shown on mouse hover + * + * @param enchantmentSlot the enchantment slot + * @return the level shown, -1 if no enchant + */ + public short getEnchantmentLevel(EnchantmentSlot enchantmentSlot) { + return enchantmentLevel[enchantmentSlot.ordinal()]; + } + + /** + * Set the enchantment level shown on mouse hover + *

+ * Can be set to -1 if no enchant + * + * @param enchantmentSlot the enchantment slot + * @param level the level shown + */ + public void setEnchantmentLevel(EnchantmentSlot enchantmentSlot, short level) { + switch (enchantmentSlot) { + case TOP: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_LEVEL_TOP, level); + break; + case MIDDLE: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_LEVEL_MIDDLE, level); + break; + case BOTTOM: + sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_LEVEL_BOTTOM, level); + break; + } + this.enchantmentLevel[enchantmentSlot.ordinal()] = level; + } + + public enum EnchantmentSlot { + TOP, MIDDLE, BOTTOM + } + } diff --git a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java index c7f8e6129..a7b37d89d 100644 --- a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java @@ -6,28 +6,58 @@ import net.minestom.server.inventory.InventoryType; public class FurnaceInventory extends Inventory { + private short remainingFuelTick; + private short maximumFuelBurnTime; + private short progressArrow; + private short maximumProgress; + public FurnaceInventory(String title) { super(InventoryType.FURNACE, title); } /** - * Represent the amount of tick until the fire icon come empty` + * Represent the amount of tick until the fire icon come empty + * + * @return the amount of tick until the fire icon come empty + */ + public short getRemainingFuelTick() { + return remainingFuelTick; + } + + /** + * Represent the amount of tick until the fire icon come empty * * @param remainingFuelTick */ public void setRemainingFuelTick(short remainingFuelTick) { + this.remainingFuelTick = remainingFuelTick; sendProperty(InventoryProperty.FURNACE_FIRE_ICON, remainingFuelTick); } + public short getMaximumFuelBurnTime() { + return maximumFuelBurnTime; + } + public void setMaximumFuelBurnTime(short maximumFuelBurnTime) { + this.maximumFuelBurnTime = maximumFuelBurnTime; sendProperty(InventoryProperty.FURNACE_MAXIMUM_FUEL_BURN_TIME, maximumFuelBurnTime); } + public short getProgressArrow() { + return progressArrow; + } + public void setProgressArrow(short progressArrow) { + this.progressArrow = progressArrow; sendProperty(InventoryProperty.FURNACE_PROGRESS_ARROW, progressArrow); } + public short getMaximumProgress() { + return maximumProgress; + } + public void setMaximumProgress(short maximumProgress) { + this.maximumProgress = maximumProgress; sendProperty(InventoryProperty.FURNACE_MAXIMUM_PROGRESS, maximumProgress); } } diff --git a/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java b/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java index 0286677b5..4812fe9e9 100644 --- a/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java @@ -20,7 +20,7 @@ public class VillagerInventory extends Inventory { public void addTrade(TradeListPacket.Trade trade) { TradeListPacket.Trade[] oldTrades = getTrades(); - int length = oldTrades.length + 1; + final int length = oldTrades.length + 1; TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length]; System.arraycopy(oldTrades, 0, trades, 0, oldTrades.length); trades[length] = trade; @@ -30,7 +30,7 @@ public class VillagerInventory extends Inventory { public void removeTrade(int index) { TradeListPacket.Trade[] oldTrades = getTrades(); - int length = oldTrades.length - 1; + final int length = oldTrades.length - 1; TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length]; ArrayUtils.removeElement(trades, index); this.tradeListPacket.trades = trades; diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index 9f65cfcad..b0d5d99bf 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -596,6 +596,9 @@ public class ItemStack implements DataContainer { if (material == Material.FIREWORK_ROCKET) return new FireworkMeta(); + if (material == Material.PLAYER_HEAD) + return new PlayerHeadMeta(); + if (material == Material.LEATHER_HELMET || material == Material.LEATHER_CHESTPLATE || material == Material.LEATHER_LEGGINGS || diff --git a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java b/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java index 0f496c68b..69843f476 100644 --- a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java @@ -82,6 +82,24 @@ public class CrossbowMeta implements ItemMeta { return projectile3; } + /** + * Get if the crossbow is currently charged + * + * @return true if the crossbow is charged, false otherwise + */ + public boolean isCharged() { + return charged; + } + + /** + * Make the bow charged or uncharged + * + * @param charged true to make the crossbow charged, false otherwise + */ + public void setCharged(boolean charged) { + this.charged = charged; + } + @Override public boolean hasNbt() { return ItemStackUtils.isVisible(projectile1); diff --git a/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java b/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java new file mode 100644 index 000000000..97a50b83c --- /dev/null +++ b/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java @@ -0,0 +1,38 @@ +package net.minestom.server.item.metadata; + +import net.minestom.server.entity.PlayerSkin; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +public class PlayerHeadMeta implements ItemMeta { + + private String playerName; + private PlayerSkin playerSkin; + + @Override + public boolean hasNbt() { + return playerSkin != null; + } + + @Override + public boolean isSimilar(ItemMeta itemMeta) { + if (!(itemMeta instanceof PlayerHeadMeta)) + return false; + final PlayerHeadMeta playerHeadMeta = (PlayerHeadMeta) itemMeta; + return playerHeadMeta.playerSkin == playerSkin; + } + + @Override + public void read(NBTCompound compound) { + + } + + @Override + public void write(NBTCompound compound) { + + } + + @Override + public ItemMeta clone() { + return null; + } +} diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java index f48a278ab..a3b23c1fd 100644 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java @@ -4,71 +4,56 @@ import net.minestom.server.potion.PotionType; import net.minestom.server.registry.Registries; import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - public class PotionMeta implements ItemMeta { - private Set potionTypes = new HashSet<>(); + private PotionType potionType; /** - * Get the item potion types + * Get the potion type * - * @return an unmodifiable {@link Set} containing the item potion types + * @return the potion type */ - public Set getPotionTypes() { - return Collections.unmodifiableSet(potionTypes); + public PotionType getPotionType() { + return potionType; } /** - * Add a potion type to the item + * Change the potion type * - * @param potionType the potion type to add + * @param potionType the new potion type */ - public void addPotionType(PotionType potionType) { - this.potionTypes.add(potionType); - } - - /** - * Remove a potion type to the item - * - * @param potionType the potion type to remove - */ - public void removePotionType(PotionType potionType) { - this.potionTypes.remove(potionType); + public void setPotionType(PotionType potionType) { + this.potionType = potionType; } @Override public boolean hasNbt() { - return !potionTypes.isEmpty(); + return potionType != null; } @Override public boolean isSimilar(ItemMeta itemMeta) { - return itemMeta instanceof PotionMeta && ((PotionMeta) itemMeta).potionTypes.equals(potionTypes); + return itemMeta instanceof PotionMeta && ((PotionMeta) itemMeta).potionType == potionType; } @Override public void read(NBTCompound compound) { if (compound.containsKey("Potion")) { - addPotionType(Registries.getPotionType(compound.getString("Potion"))); + this.potionType = Registries.getPotionType(compound.getString("Potion")); } } @Override public void write(NBTCompound compound) { - if (!potionTypes.isEmpty()) { - for (PotionType potionType : potionTypes) { - compound.setString("Potion", potionType.getNamespaceID()); - } + if (potionType != null) { + compound.setString("Potion", potionType.getNamespaceID()); } } @Override public ItemMeta clone() { PotionMeta potionMeta = new PotionMeta(); - potionMeta.potionTypes.addAll(potionTypes); + potionMeta.potionType = potionType; return potionMeta; } diff --git a/src/main/java/net/minestom/server/item/metadata/SpawnEggMeta.java b/src/main/java/net/minestom/server/item/metadata/SpawnEggMeta.java new file mode 100644 index 000000000..9fafb9cb5 --- /dev/null +++ b/src/main/java/net/minestom/server/item/metadata/SpawnEggMeta.java @@ -0,0 +1,48 @@ +package net.minestom.server.item.metadata; + +import net.minestom.server.entity.EntityType; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +// TODO for which item +public class SpawnEggMeta implements ItemMeta { + + private EntityType entityType; + + @Override + public boolean hasNbt() { + return entityType != null; + } + + @Override + public boolean isSimilar(ItemMeta itemMeta) { + if (!(itemMeta instanceof SpawnEggMeta)) + return false; + final SpawnEggMeta spawnEggMeta = (SpawnEggMeta) itemMeta; + return spawnEggMeta.entityType == entityType; + } + + @Override + public void read(NBTCompound compound) { + if (compound.containsKey("EntityTag")) { + + } + } + + @Override + public void write(NBTCompound compound) { + if (!hasNbt()) + return; + NBTCompound entityCompound = new NBTCompound(); + if (entityType != null) { + entityCompound.setString("id", entityType.getNamespaceID()); + } + + } + + @Override + public ItemMeta clone() { + SpawnEggMeta spawnEggMeta = new SpawnEggMeta(); + spawnEggMeta.entityType = entityType; + return spawnEggMeta; + } +} diff --git a/src/main/java/net/minestom/server/storage/StorageFolder.java b/src/main/java/net/minestom/server/storage/StorageFolder.java index a3360becc..d770fea2a 100644 --- a/src/main/java/net/minestom/server/storage/StorageFolder.java +++ b/src/main/java/net/minestom/server/storage/StorageFolder.java @@ -24,15 +24,15 @@ public class StorageFolder { private Map cachedData; - protected StorageFolder(StorageSystem storageSystem, String folderPath) { + protected StorageFolder(StorageSystem storageSystem, String folderPath, StorageOptions storageOptions) { this.storageSystem = storageSystem; this.folderPath = folderPath; this.cachedData = new HashMap<>(); - this.storageSystem.open(folderPath); + this.storageSystem.open(folderPath, storageOptions); } - + public byte[] get(String key) { return storageSystem.get(key); } diff --git a/src/main/java/net/minestom/server/storage/StorageManager.java b/src/main/java/net/minestom/server/storage/StorageManager.java index 297872a36..ca872f0cd 100644 --- a/src/main/java/net/minestom/server/storage/StorageManager.java +++ b/src/main/java/net/minestom/server/storage/StorageManager.java @@ -23,16 +23,34 @@ public class StorageManager { * WARNING: a storage folder needs to be created with an unique storage system linked * you cannot open the save folder with two or more different StorageSystem implementation * - * @param folderPath the path to the folder - * @param storageSystem the storage system used in the specified folder + * @param folderPath the path to the folder + * @param storageOptions the storage option + * @param storageSystem the storage system used in the specified folder * @return the specified storage folder */ - public StorageFolder getFolder(String folderPath, StorageSystem storageSystem) { + public StorageFolder getFolder(String folderPath, StorageOptions storageOptions, StorageSystem storageSystem) { + Check.notNull(storageOptions, "The storage option cannot be null"); StorageFolder storageFolder = - folderMap.computeIfAbsent(folderPath, s -> new StorageFolder(storageSystem, folderPath)); + folderMap.computeIfAbsent(folderPath, s -> new StorageFolder(storageSystem, folderPath, storageOptions)); return storageFolder; } + /** + * Used to get an access to the specified folder + * The default StorageSystem provider will be used + * + * @param folderPath the path to the folder + * @param storageOptions the storage option + * @return the specified storage default with the default + * @throws NullPointerException if no default StorageSystem is defined {@link #defineDefaultStorageSystem(Supplier)} + */ + public StorageFolder getFolder(String folderPath, StorageOptions storageOptions) { + Check.notNull(defaultStorageSystemSupplier, + "You need to either define a default storage system or specify your storage system for this specific folder"); + StorageSystem storageSystem = defaultStorageSystemSupplier.get(); + return getFolder(folderPath, storageOptions, storageSystem); + } + /** * Used to get an access to the specified folder * The default StorageSystem provider will be used @@ -42,11 +60,7 @@ public class StorageManager { * @throws NullPointerException if no default StorageSystem is defined {@link #defineDefaultStorageSystem(Supplier)} */ public StorageFolder getFolder(String folderPath) { - Check.notNull(defaultStorageSystemSupplier, - "You need to either define a default storage system or specify your storage system for this specific folder"); - - StorageSystem storageSystem = defaultStorageSystemSupplier.get(); - return getFolder(folderPath, storageSystem); + return getFolder(folderPath, new StorageOptions()); } /** @@ -71,7 +85,7 @@ public class StorageManager { } /** - * Get all folders which have been loaded by {@link #getFolder(String)} or {@link #getFolder(String, StorageSystem)} + * Get all folders which have been loaded by {@link #getFolder(String)} or {@link #getFolder(String, StorageOptions, StorageSystem)} * * @return an unmodifiable list of all the loaded StorageFolder */ diff --git a/src/main/java/net/minestom/server/storage/StorageOptions.java b/src/main/java/net/minestom/server/storage/StorageOptions.java new file mode 100644 index 000000000..2e63e99cb --- /dev/null +++ b/src/main/java/net/minestom/server/storage/StorageOptions.java @@ -0,0 +1,26 @@ +package net.minestom.server.storage; + +public class StorageOptions { + + private boolean compression; + + /** + * Get if compression should be enabled + * + * @return true if compression should be enabled, false otherwise + */ + public boolean hasCompression() { + return compression; + } + + /** + * Define if the storage solution should use compression + * + * @param compression true to enable compression, false otherwise + * @return the reference to the current options + */ + public StorageOptions setCompression(boolean compression) { + this.compression = compression; + return this; + } +} diff --git a/src/main/java/net/minestom/server/storage/StorageSystem.java b/src/main/java/net/minestom/server/storage/StorageSystem.java index 0a9569f50..82f6f8714 100644 --- a/src/main/java/net/minestom/server/storage/StorageSystem.java +++ b/src/main/java/net/minestom/server/storage/StorageSystem.java @@ -11,9 +11,10 @@ public interface StorageSystem { /** * Called when a folder is opened with this StorageSystem * - * @param folderPath the name of the folder + * @param folderPath the name of the folder + * @param storageOptions the storage option */ - void open(String folderPath); + void open(String folderPath, StorageOptions storageOptions); /** * @param key diff --git a/src/main/java/net/minestom/server/storage/systems/FileStorageSystem.java b/src/main/java/net/minestom/server/storage/systems/FileStorageSystem.java index d8031adef..0bd3a6074 100644 --- a/src/main/java/net/minestom/server/storage/systems/FileStorageSystem.java +++ b/src/main/java/net/minestom/server/storage/systems/FileStorageSystem.java @@ -1,9 +1,8 @@ package net.minestom.server.storage.systems; +import net.minestom.server.storage.StorageOptions; import net.minestom.server.storage.StorageSystem; -import org.rocksdb.Options; -import org.rocksdb.RocksDB; -import org.rocksdb.RocksDBException; +import org.rocksdb.*; import java.io.File; @@ -25,9 +24,14 @@ public class FileStorageSystem implements StorageSystem { } @Override - public void open(String folderPath) { + public void open(String folderPath, StorageOptions storageOptions) { Options options = new Options().setCreateIfMissing(true); + if (storageOptions.hasCompression()) { + options.setCompressionType(CompressionType.ZSTD_COMPRESSION); + options.setCompressionOptions(new CompressionOptions().setLevel(1)); + } + try { this.rocksDB = RocksDB.open(options, folderPath); } catch (RocksDBException e) { diff --git a/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java b/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java new file mode 100644 index 000000000..688d24d5e --- /dev/null +++ b/src/main/java/net/minestom/server/thread/PerGroupChunkProvider.java @@ -0,0 +1,129 @@ +package net.minestom.server.thread; + +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Separate chunks into group of linked chunks + *

+ * (1 chunks group = 1 thread execution) + */ +// FIXME: unusable at the moment, too much overhead because groups need to be created every tick +// Should have a callback for when a chunk is loaded and unloaded, so groups are updated only once +public class PerGroupChunkProvider extends ThreadProvider { + + /** + * Here are stored all cached chunks waiting for a ChunkGroup + */ + private Map> cachedChunks = new HashMap<>(); + + /** + * Used to know to which instance is linked a Set of chunks + */ + private Map, Instance> instanceMap = new HashMap<>(); + + @Override + public void start() { + this.cachedChunks.clear(); + this.instanceMap.clear(); + } + + @Override + public void linkThread(Instance instance, Chunk chunk) { + startChunkQuery(instance, chunk.getChunkX(), chunk.getChunkZ()); + } + + @Override + public void end() { + + } + + @Override + public void update() { + // The time of the tick + final long time = System.currentTimeMillis(); + + // Set of already-updated instances + final Set updatedInstance = new HashSet<>(); + + // Update all the chunks + for (Map.Entry, Instance> entry : instanceMap.entrySet()) { + Set chunks = entry.getKey(); + Instance instance = entry.getValue(); + + final boolean updateInstance = updatedInstance.add(instance); + pool.execute(() -> { + /*if (updateInstance) { + updateInstance(instance, time); + } + + for (Chunk chunk : chunks) { + + updateChunk(instance, chunk, time); + + updateEntities(instance, chunk, time); + }*/ + }); + + } + } + + /** + * Check the four chunk neighbors (up/down/left/right) + * and add them to the cache list + * + * @param instance the instance which is checked + * @param chunkX the chunk X + * @param chunkZ the chunk Z + */ + private void startChunkQuery(Instance instance, int chunkX, int chunkZ) { + // Constants used to loop through the neighbors + final int[] posX = {1, 0, -1}; + final int[] posZ = {1, 0, -1}; + + // The cache which will contain all the current chunk group + final Set cache = new HashSet<>(); + + for (int x : posX) { + for (int z : posZ) { + + // No diagonal check + if ((Math.abs(x) + Math.abs(z)) == 2) + continue; + + final int targetX = chunkX + x; + final int targetZ = chunkZ + z; + + final Chunk chunk = instance.getChunk(targetX, targetZ); + if (cache.contains(chunk)) { + continue; + } + + if (chunk != null) { + // If loaded, check if the chunk is already associated with a Set + if (cachedChunks.containsKey(chunk)) { + // Chunk is associated with a Set, add all them to the updated cache Set + Set oldCache = cachedChunks.get(chunk); + cache.addAll(oldCache); + this.instanceMap.remove(oldCache); + } else { + // Chunk is alone, add it to the cache list + cache.add(chunk); + } + } + } + } + + // Add cached chunks into a cache list + for (Chunk cachedChunk : cache) { + this.cachedChunks.put(cachedChunk, cache); + } + this.instanceMap.put(cache, instance); + } + +} diff --git a/src/main/java/net/minestom/server/thread/DefaultThreadProvider.java b/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java similarity index 74% rename from src/main/java/net/minestom/server/thread/DefaultThreadProvider.java rename to src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java index 01738f217..baed22b9b 100644 --- a/src/main/java/net/minestom/server/thread/DefaultThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/PerInstanceThreadProvider.java @@ -1,24 +1,17 @@ package net.minestom.server.thread; -import net.minestom.server.MinecraftServer; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; -import net.minestom.server.utils.thread.MinestomThread; import java.util.*; -import java.util.concurrent.ExecutorService; -public class DefaultThreadProvider extends ThreadProvider { - - private ExecutorService pool; - private int threadCount; +/** + * Separate work between instance (1 instance = 1 thread execution) + */ +public class PerInstanceThreadProvider extends ThreadProvider { private Map groupMap = new HashMap<>(); - { - setThreadCount(5); - } - @Override public void start() { this.groupMap.clear(); @@ -28,14 +21,17 @@ public class DefaultThreadProvider extends ThreadProvider { public void linkThread(Instance instance, Chunk chunk) { InstanceChunk instanceChunk = new InstanceChunk(instance, chunk); - GroupedInstanceChunk groupedInstanceChunk = groupMap.getOrDefault(instance, new GroupedInstanceChunk()); + GroupedInstanceChunk groupedInstanceChunk = groupMap.computeIfAbsent(instance, inst -> new GroupedInstanceChunk()); groupedInstanceChunk.instanceChunks.add(instanceChunk); - - this.groupMap.put(instance, groupedInstanceChunk); } @Override public void end() { + + } + + @Override + public void update() { final long time = System.currentTimeMillis(); for (Map.Entry entry : groupMap.entrySet()) { @@ -54,20 +50,6 @@ public class DefaultThreadProvider extends ThreadProvider { } }); } - - } - - public int getThreadCount() { - return threadCount; - } - - public synchronized void setThreadCount(int threadCount) { - this.threadCount = threadCount; - refreshPool(); - } - - private void refreshPool() { - this.pool = new MinestomThread(threadCount, MinecraftServer.THREAD_NAME_TICK); } /** diff --git a/src/main/java/net/minestom/server/thread/ThreadProvider.java b/src/main/java/net/minestom/server/thread/ThreadProvider.java index 1d268ea20..326a32a23 100644 --- a/src/main/java/net/minestom/server/thread/ThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/ThreadProvider.java @@ -1,17 +1,35 @@ package net.minestom.server.thread; +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.thread.MinestomThread; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.function.Function; /** - * Used to link chunks to a certain thread + * Used to link chunks into multiple groups + * Then executed into a thread pool */ public abstract class ThreadProvider { + /** + * The thread pool of this thread provider + */ + protected ExecutorService pool; + /** + * The amount of threads in the thread pool + */ + private int threadCount; + + { + // Default thread count in the pool + setThreadCount(5); + } + /** * Called to prepare the thread provider, to provide threads for the next server tick */ @@ -27,11 +45,37 @@ public abstract class ThreadProvider { /** * Inform the server that all chunks have been assigned to a thread - *

- * Should execute the server tick for all chunks based on their linked thread */ public abstract void end(); + /** + * Perform a server tick for all chunks based on their linked thread + */ + public abstract void update(); + + /** + * Get the current size of the thread pool + * + * @return the thread pool's size + */ + public int getThreadCount() { + return threadCount; + } + + /** + * Change the amount of threads in the thread pool + * + * @param threadCount the new amount of threads + */ + public synchronized void setThreadCount(int threadCount) { + this.threadCount = threadCount; + refreshPool(); + } + + private void refreshPool() { + this.pool = new MinestomThread(threadCount, MinecraftServer.THREAD_NAME_TICK); + } + /** * INSTANCE UPDATE */ diff --git a/src/main/java/net/minestom/server/utils/ArrayUtils.java b/src/main/java/net/minestom/server/utils/ArrayUtils.java index 6c1034293..19750a5ea 100644 --- a/src/main/java/net/minestom/server/utils/ArrayUtils.java +++ b/src/main/java/net/minestom/server/utils/ArrayUtils.java @@ -34,10 +34,10 @@ public class ArrayUtils { int[] indexes = new int[Math.max(a.length, b.length)]; for (int i = 0; i < a.length; i++) { - long aValue = a[i]; + final long aValue = a[i]; boolean contains = false; for (int n = 0; n < b.length; n++) { - long bValue = b[n]; + final long bValue = b[n]; if (bValue == aValue) { contains = true; break; diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index 06b657e46..0d26d5b90 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -136,15 +136,14 @@ public class NBTUtils { if (nbt.containsKey("AttributeModifiers")) { NBTList attributes = nbt.getList("AttributeModifiers"); for (NBTCompound attributeNBT : attributes) { - // TODO: 1.16 changed how UUIDs are stored, is this part affected? - long uuidMost = attributeNBT.getLong("UUIDMost"); - long uuidLeast = attributeNBT.getLong("UUIDLeast"); - UUID uuid = new UUID(uuidMost, uuidLeast); - double value = attributeNBT.getDouble("Amount"); - String slot = attributeNBT.getString("Slot"); - String attributeName = attributeNBT.getString("AttributeName"); - int operation = attributeNBT.getInt("Operation"); - String name = attributeNBT.getString("Name"); + final long uuidMost = attributeNBT.getLong("UUIDMost"); + final long uuidLeast = attributeNBT.getLong("UUIDLeast"); + final UUID uuid = new UUID(uuidMost, uuidLeast); + final double value = attributeNBT.getDouble("Amount"); + final String slot = attributeNBT.getString("Slot"); + final String attributeName = attributeNBT.getString("AttributeName"); + final int operation = attributeNBT.getInt("Operation"); + final String name = attributeNBT.getString("Name"); final Attribute attribute = Attribute.fromKey(attributeName); // Wrong attribute name, stop here diff --git a/src/main/java/net/minestom/server/utils/Rotation.java b/src/main/java/net/minestom/server/utils/Rotation.java new file mode 100644 index 000000000..dc06913b6 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/Rotation.java @@ -0,0 +1,57 @@ +package net.minestom.server.utils; + +public enum Rotation { + + /** + * No rotation + */ + NONE, + /** + * Rotated clockwise by 45 degrees + */ + CLOCKWISE_45, + /** + * Rotated clockwise by 90 degrees + */ + CLOCKWISE, + /** + * Rotated clockwise by 135 degrees + */ + CLOCKWISE_135, + /** + * Flipped upside-down, a 180 degree rotation + */ + FLIPPED, + /** + * Flipped upside-down + 45 degree rotation + */ + FLIPPED_45, + /** + * Rotated counter-clockwise by 90 degrees + */ + COUNTER_CLOCKWISE, + /** + * Rotated counter-clockwise by 45 degrees + */ + COUNTER_CLOCKWISE_45; + + private static final Rotation[] rotations = values(); + + /** + * Rotate clockwise by 90 degrees. + * + * @return the relative rotation + */ + public Rotation rotateClockwise() { + return rotations[(this.ordinal() + 1) & 0x7]; + } + + /** + * Rotate counter-clockwise by 90 degrees. + * + * @return the relative rotation + */ + public Rotation rotateCounterClockwise() { + return rotations[(this.ordinal() - 1) & 0x7]; + } +} diff --git a/src/main/java/net/minestom/server/utils/SerializerUtils.java b/src/main/java/net/minestom/server/utils/SerializerUtils.java index b2b0a5a33..a2bcfc8cd 100644 --- a/src/main/java/net/minestom/server/utils/SerializerUtils.java +++ b/src/main/java/net/minestom/server/utils/SerializerUtils.java @@ -7,9 +7,9 @@ public class SerializerUtils { } public static BlockPosition longToBlockPosition(long value) { - int x = (int) (value >> 38); - int y = (int) (value & 0xFFF); - int z = (int) (value << 26 >> 38); + final int x = (int) (value >> 38); + final int y = (int) (value & 0xFFF); + final int z = (int) (value << 26 >> 38); return new BlockPosition(x, y, z); }