Merge branch 'master' of github.com:Minestom/Minestom

This commit is contained in:
Articdive 2020-08-02 12:22:16 +02:00
commit 0f1f84db04
No known key found for this signature in database
GPG Key ID: B069585F0F7D90DE
36 changed files with 971 additions and 111 deletions

View File

@ -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();

View File

@ -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
* <p>
* {@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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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<PacketWriter> 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
}
}

View File

@ -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<PacketWriter> 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
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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());
}
}
}

View File

@ -26,6 +26,11 @@ public interface InventoryModifier {
*/
boolean addItemStack(ItemStack itemStack);
/**
* Clear the inventory
*/
void clear();
/**
* Get the item at the specified slot
*

View File

@ -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
*

View File

@ -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);
}
}

View File

@ -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());
}

View File

@ -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);
}

View File

@ -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
* <p>
* 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
* <p>
* 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
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 ||

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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<PotionType> 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<PotionType> 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;
}

View File

@ -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;
}
}

View File

@ -24,15 +24,15 @@ public class StorageFolder {
private Map<String, SerializableData> 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);
}

View File

@ -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
*/

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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
* <p>
* (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<Chunk, Set<Chunk>> cachedChunks = new HashMap<>();
/**
* Used to know to which instance is linked a Set of chunks
*/
private Map<Set<Chunk>, 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<Instance> updatedInstance = new HashSet<>();
// Update all the chunks
for (Map.Entry<Set<Chunk>, Instance> entry : instanceMap.entrySet()) {
Set<Chunk> 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<Chunk> 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<Chunk> 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);
}
}

View File

@ -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<Instance, GroupedInstanceChunk> 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<Instance, GroupedInstanceChunk> 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);
}
/**

View File

@ -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
* <p>
* 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
*/

View File

@ -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;

View File

@ -136,15 +136,14 @@ public class NBTUtils {
if (nbt.containsKey("AttributeModifiers")) {
NBTList<NBTCompound> 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

View File

@ -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];
}
}

View File

@ -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);
}