diff --git a/src/main/java/net/minestom/server/command/CommandSender.java b/src/main/java/net/minestom/server/command/CommandSender.java index 35bca4014..296a242d7 100644 --- a/src/main/java/net/minestom/server/command/CommandSender.java +++ b/src/main/java/net/minestom/server/command/CommandSender.java @@ -51,6 +51,7 @@ public interface CommandSender extends PermissionHandler { * Casts this object to a {@link Player}. * No checks are performed, {@link ClassCastException} can very much happen. * + * @throws ClassCastException if 'this' is not a player * @see #isPlayer() */ default Player asPlayer() { @@ -61,6 +62,7 @@ public interface CommandSender extends PermissionHandler { * Casts this object to a {@link ConsoleSender}. * No checks are performed, {@link ClassCastException} can very much happen. * + * @throws ClassCastException if 'this' is not a console sender * @see #isConsole() */ default ConsoleSender asConsole() { diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 01af85482..ab1de4cdf 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -379,8 +379,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P return; } - BlockPosition blockPosition = position.toBlockPosition(); - if (!ChunkUtils.isLoaded(instance, position.getX(), position.getZ()) || !ChunkUtils.isLoaded(instance, blockPosition.getX(), blockPosition.getZ())) { + final Chunk currentChunk = getChunk(); // current entity chunk + + if (!ChunkUtils.isLoaded(currentChunk)) { // No update for entities in unloaded chunk return; } @@ -468,8 +469,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P float drag; if (onGround) { - final CustomBlock customBlock = - instance.getCustomBlock(blockPosition); + final BlockPosition blockPosition = position.toBlockPosition(); + final CustomBlock customBlock = instance.getCustomBlock(blockPosition); if (customBlock != null) { // Custom drag drag = customBlock.getDrag(instance, blockPosition); diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 1555f024b..bf8d8ac24 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -19,7 +19,9 @@ import net.minestom.server.sound.Sound; import net.minestom.server.sound.SoundCategory; import net.minestom.server.utils.Position; import net.minestom.server.utils.binary.BinaryWriter; +import net.minestom.server.utils.time.CooldownUtils; import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,7 +31,11 @@ import java.util.function.Consumer; public abstract class LivingEntity extends Entity implements EquipmentHandler { + // Item pickup protected boolean canPickupItem; + protected UpdateOption itemPickupCooldown = new UpdateOption(10, TimeUnit.TICK); + protected long lastItemPickupTime; + protected boolean isDead; private float health; @@ -90,8 +96,10 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { } // Items picking - if (canPickupItem()) { - final Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks + if (canPickupItem() && !CooldownUtils.hasCooldown(time, lastItemPickupTime, itemPickupCooldown)) { + this.lastItemPickupTime = time; + + final Chunk chunk = getChunk(); // TODO check surrounding chunks final Set entities = instance.getChunkEntities(chunk); for (Entity entity : entities) { if (entity instanceof ItemEntity) { diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index f89fe762d..e99a49f41 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -55,6 +55,9 @@ import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.instance.InstanceUtils; +import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import net.minestom.server.world.DimensionType; import org.jetbrains.annotations.NotNull; @@ -126,6 +129,10 @@ public class Player extends LivingEntity implements CommandSender { private byte targetStage; // The current stage of the target block, only if multi player breaking is disabled private final Set targetBreakers = new HashSet<>(1); // Only used if multi player breaking is disabled, contains only this player + // Experience orb pickup + protected UpdateOption experiencePickupCooldown = new UpdateOption(10, TimeUnit.TICK); + protected long lastExperiencePickupTime; + private BelowNameTag belowNameTag; private int permissionLevel; @@ -347,20 +354,24 @@ public class Player extends LivingEntity implements CommandSender { } // Experience orb pickup - final Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks - final Set entities = instance.getChunkEntities(chunk); - for (Entity entity : entities) { - if (entity instanceof ExperienceOrb) { - final ExperienceOrb experienceOrb = (ExperienceOrb) entity; - final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox(); - if (expandedBoundingBox.intersect(itemBoundingBox)) { - if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled()) - continue; - PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb); - callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> { - short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player - entity.remove(); - }); + if (!CooldownUtils.hasCooldown(time, lastExperiencePickupTime, experiencePickupCooldown)) { + this.lastExperiencePickupTime = time; + + final Chunk chunk = getChunk(); // TODO check surrounding chunks + final Set entities = instance.getChunkEntities(chunk); + for (Entity entity : entities) { + if (entity instanceof ExperienceOrb) { + final ExperienceOrb experienceOrb = (ExperienceOrb) entity; + final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox(); + if (expandedBoundingBox.intersect(itemBoundingBox)) { + if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled()) + continue; + PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb); + callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> { + short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player + entity.remove(); + }); + } } } } diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index b350cd5db..6cdd05a33 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -605,6 +605,15 @@ public class InstanceContainer extends Instance { return Collections.unmodifiableList(sharedInstances); } + /** + * Gets if this instance has {@link SharedInstance} linked to it. + * + * @return true if {@link #getSharedInstances()} is not empty + */ + public boolean hasSharedInstances() { + return !sharedInstances.isEmpty(); + } + /** * Assigns a {@link SharedInstance} to this container. *

diff --git a/src/main/java/net/minestom/server/thread/ThreadProvider.java b/src/main/java/net/minestom/server/thread/ThreadProvider.java index 253d2f7ab..04adb5235 100644 --- a/src/main/java/net/minestom/server/thread/ThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/ThreadProvider.java @@ -112,7 +112,7 @@ public abstract class ThreadProvider { // INSTANCE UPDATE /** - * Process a whole tick for a chunk. + * Processes a whole tick for a chunk. * * @param instance the instance of the chunk * @param chunkIndex the index of the chunk {@link ChunkUtils#getChunkIndex(int, int)} @@ -243,6 +243,10 @@ public abstract class ThreadProvider { private void updateSharedInstances(@NotNull Instance instance, @NotNull Consumer callback) { if (instance instanceof InstanceContainer) { final InstanceContainer instanceContainer = (InstanceContainer) instance; + + if (!instanceContainer.hasSharedInstances()) + return; + for (SharedInstance sharedInstance : instanceContainer.getSharedInstances()) { callback.accept(sharedInstance); } diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java index c13218ec4..35a96caa6 100644 --- a/src/main/java/net/minestom/server/utils/Utils.java +++ b/src/main/java/net/minestom/server/utils/Utils.java @@ -1,8 +1,6 @@ package net.minestom.server.utils; import io.netty.buffer.ByteBuf; -import net.minestom.server.instance.Chunk; -import net.minestom.server.instance.block.Block; import net.minestom.server.utils.binary.BinaryWriter; import java.util.UUID; @@ -94,6 +92,32 @@ public final class Utils { return new UUID(uuidMost, uuidLeast); } + public static void writeBlocks(ByteBuf buffer, short[] palette, long[] blocksId, int bitsPerEntry) { + /*short count = 0; + for (short id : blocksId) + if (id != 0) + count++;*/ + + //buffer.writeShort(count); + buffer.writeShort(200); + buffer.writeByte((byte) bitsPerEntry); + + // Palette + if (bitsPerEntry < 9) { + // Palette has to exist + writeVarIntBuf(buffer, palette.length); + for (short paletteValue : palette) { + writeVarIntBuf(buffer, paletteValue); + } + } + + final long[] data = blocksId; + writeVarIntBuf(buffer, data.length); + for (long datum : data) { + buffer.writeLong(datum); + } + } + private static final int[] MAGIC = { -1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE, 0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756, @@ -116,69 +140,6 @@ public final class Utils { 70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE, 0, 5}; - public static void writeBlocks(ByteBuf buffer, short[] palette, long[] blocksId, int bitsPerEntry) { - /*short count = 0; - for (short id : blocksId) - if (id != 0) - count++;*/ - - //buffer.writeShort(count); - buffer.writeShort(200); - buffer.writeByte((byte) bitsPerEntry); - - // Palette - if (bitsPerEntry < 9) { - // Palette has to exist - writeVarIntBuf(buffer, palette.length); - for (short paletteValue : palette) { - writeVarIntBuf(buffer, paletteValue); - } - } - - final long[] data = blocksId;//encodeBlocksTEST(bitsPerEntry); - writeVarIntBuf(buffer, data.length); - for (long datum : data) { - buffer.writeLong(datum); - } - } - - public synchronized static long[] encodeBlocksTEST(int bitsPerEntry) { - //long test = (Block.TORCH.getBlockId() << (64 - 50 - bitsPerEntry + 1)); - //System.out.println("BINARY: 0b" + Long.toBinaryString(test) + " " + (64 - 50 - bitsPerEntry + 1)); - final int blockCount = 16 * 16 * 16; // A whole chunk section - final int longSize = Long.SIZE; // 64 - final char valuesPerLong = (char) (longSize / bitsPerEntry); - final int arraySize = blockCount / valuesPerLong; - - long[] data = new long[arraySize]; - data[0] = 0b000000010001L; - data[1] = 0b000000010001L; - - if (true) { - return data; - } - - for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) { - for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { - for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) { - final long blockId = x % 2 == 0 && z % 2 == 0 ? Block.AIR.getBlockId() : Block.LAVA.getBlockId(); - int sectionIndex = (((y * 16) + z) * 16) + x; - - final int index = sectionIndex / valuesPerLong; - final int bitIndex = sectionIndex % valuesPerLong * bitsPerEntry; - - data[index] |= (blockId << bitIndex); - } - } - } - - return data; - } - - private static String binary(long value) { - return "0b" + Long.toBinaryString(value); - } - public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) { final long maxEntryValue = (1L << bitsPerEntry) - 1; final char valuesPerLong = (char) (64 / bitsPerEntry);