Optimization to prevent permanent map lookup

This commit is contained in:
themode 2020-11-14 09:02:29 +01:00
parent 648cad85c2
commit 0abedd7453
7 changed files with 82 additions and 86 deletions

View File

@ -51,6 +51,7 @@ public interface CommandSender extends PermissionHandler {
* Casts this object to a {@link Player}. * Casts this object to a {@link Player}.
* No checks are performed, {@link ClassCastException} can very much happen. * No checks are performed, {@link ClassCastException} can very much happen.
* *
* @throws ClassCastException if 'this' is not a player
* @see #isPlayer() * @see #isPlayer()
*/ */
default Player asPlayer() { default Player asPlayer() {
@ -61,6 +62,7 @@ public interface CommandSender extends PermissionHandler {
* Casts this object to a {@link ConsoleSender}. * Casts this object to a {@link ConsoleSender}.
* No checks are performed, {@link ClassCastException} can very much happen. * No checks are performed, {@link ClassCastException} can very much happen.
* *
* @throws ClassCastException if 'this' is not a console sender
* @see #isConsole() * @see #isConsole()
*/ */
default ConsoleSender asConsole() { default ConsoleSender asConsole() {

View File

@ -379,8 +379,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P
return; return;
} }
BlockPosition blockPosition = position.toBlockPosition(); final Chunk currentChunk = getChunk(); // current entity chunk
if (!ChunkUtils.isLoaded(instance, position.getX(), position.getZ()) || !ChunkUtils.isLoaded(instance, blockPosition.getX(), blockPosition.getZ())) {
if (!ChunkUtils.isLoaded(currentChunk)) {
// No update for entities in unloaded chunk // No update for entities in unloaded chunk
return; return;
} }
@ -468,8 +469,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P
float drag; float drag;
if (onGround) { if (onGround) {
final CustomBlock customBlock = final BlockPosition blockPosition = position.toBlockPosition();
instance.getCustomBlock(blockPosition); final CustomBlock customBlock = instance.getCustomBlock(blockPosition);
if (customBlock != null) { if (customBlock != null) {
// Custom drag // Custom drag
drag = customBlock.getDrag(instance, blockPosition); drag = customBlock.getDrag(instance, blockPosition);

View File

@ -19,7 +19,9 @@ import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundCategory; import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.Position; import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryWriter; 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.TimeUnit;
import net.minestom.server.utils.time.UpdateOption;
import net.minestom.server.utils.validate.Check; import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -29,7 +31,11 @@ import java.util.function.Consumer;
public abstract class LivingEntity extends Entity implements EquipmentHandler { public abstract class LivingEntity extends Entity implements EquipmentHandler {
// Item pickup
protected boolean canPickupItem; protected boolean canPickupItem;
protected UpdateOption itemPickupCooldown = new UpdateOption(10, TimeUnit.TICK);
protected long lastItemPickupTime;
protected boolean isDead; protected boolean isDead;
private float health; private float health;
@ -90,8 +96,10 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
} }
// Items picking // Items picking
if (canPickupItem()) { if (canPickupItem() && !CooldownUtils.hasCooldown(time, lastItemPickupTime, itemPickupCooldown)) {
final Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks this.lastItemPickupTime = time;
final Chunk chunk = getChunk(); // TODO check surrounding chunks
final Set<Entity> entities = instance.getChunkEntities(chunk); final Set<Entity> entities = instance.getChunkEntities(chunk);
for (Entity entity : entities) { for (Entity entity : entities) {
if (entity instanceof ItemEntity) { if (entity instanceof ItemEntity) {

View File

@ -55,6 +55,9 @@ import net.minestom.server.utils.callback.OptionalCallback;
import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkCallback;
import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.instance.InstanceUtils; 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.utils.validate.Check;
import net.minestom.server.world.DimensionType; import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.NotNull; 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 byte targetStage; // The current stage of the target block, only if multi player breaking is disabled
private final Set<Player> targetBreakers = new HashSet<>(1); // Only used if multi player breaking is disabled, contains only this player private final Set<Player> 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 BelowNameTag belowNameTag;
private int permissionLevel; private int permissionLevel;
@ -347,20 +354,24 @@ public class Player extends LivingEntity implements CommandSender {
} }
// Experience orb pickup // Experience orb pickup
final Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks if (!CooldownUtils.hasCooldown(time, lastExperiencePickupTime, experiencePickupCooldown)) {
final Set<Entity> entities = instance.getChunkEntities(chunk); this.lastExperiencePickupTime = time;
for (Entity entity : entities) {
if (entity instanceof ExperienceOrb) { final Chunk chunk = getChunk(); // TODO check surrounding chunks
final ExperienceOrb experienceOrb = (ExperienceOrb) entity; final Set<Entity> entities = instance.getChunkEntities(chunk);
final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox(); for (Entity entity : entities) {
if (expandedBoundingBox.intersect(itemBoundingBox)) { if (entity instanceof ExperienceOrb) {
if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled()) final ExperienceOrb experienceOrb = (ExperienceOrb) entity;
continue; final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox();
PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb); if (expandedBoundingBox.intersect(itemBoundingBox)) {
callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> { if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled())
short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player continue;
entity.remove(); PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb);
}); callCancellableEvent(PickupExperienceEvent.class, pickupExperienceEvent, () -> {
short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player
entity.remove();
});
}
} }
} }
} }

View File

@ -605,6 +605,15 @@ public class InstanceContainer extends Instance {
return Collections.unmodifiableList(sharedInstances); 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. * Assigns a {@link SharedInstance} to this container.
* <p> * <p>

View File

@ -112,7 +112,7 @@ public abstract class ThreadProvider {
// INSTANCE UPDATE // INSTANCE UPDATE
/** /**
* Process a whole tick for a chunk. * Processes a whole tick for a chunk.
* *
* @param instance the instance of the chunk * @param instance the instance of the chunk
* @param chunkIndex the index of the chunk {@link ChunkUtils#getChunkIndex(int, int)} * @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<SharedInstance> callback) { private void updateSharedInstances(@NotNull Instance instance, @NotNull Consumer<SharedInstance> callback) {
if (instance instanceof InstanceContainer) { if (instance instanceof InstanceContainer) {
final InstanceContainer instanceContainer = (InstanceContainer) instance; final InstanceContainer instanceContainer = (InstanceContainer) instance;
if (!instanceContainer.hasSharedInstances())
return;
for (SharedInstance sharedInstance : instanceContainer.getSharedInstances()) { for (SharedInstance sharedInstance : instanceContainer.getSharedInstances()) {
callback.accept(sharedInstance); callback.accept(sharedInstance);
} }

View File

@ -1,8 +1,6 @@
package net.minestom.server.utils; package net.minestom.server.utils;
import io.netty.buffer.ByteBuf; 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 net.minestom.server.utils.binary.BinaryWriter;
import java.util.UUID; import java.util.UUID;
@ -94,6 +92,32 @@ public final class Utils {
return new UUID(uuidMost, uuidLeast); 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 = { private static final int[] MAGIC = {
-1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE, -1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE,
0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756, 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, 70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
0, 5}; 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) { public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
final long maxEntryValue = (1L << bitsPerEntry) - 1; final long maxEntryValue = (1L << bitsPerEntry) - 1;
final char valuesPerLong = (char) (64 / bitsPerEntry); final char valuesPerLong = (char) (64 / bitsPerEntry);