mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-08 16:32:07 +01:00
Improve block getter performance
This commit is contained in:
parent
295b3e24dd
commit
b419ce88c1
@ -20,7 +20,6 @@ import net.minestom.server.network.player.PlayerConnection;
|
|||||||
import net.minestom.server.tag.Tag;
|
import net.minestom.server.tag.Tag;
|
||||||
import net.minestom.server.tag.TagHandler;
|
import net.minestom.server.tag.TagHandler;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
import net.minestom.server.utils.ArrayUtils;
|
||||||
import net.minestom.server.utils.Position;
|
|
||||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||||
import net.minestom.server.world.biomes.Biome;
|
import net.minestom.server.world.biomes.Biome;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -119,14 +118,6 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
|
|||||||
@Override
|
@Override
|
||||||
public abstract void tick(long time);
|
public abstract void tick(long time);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the block entities in this chunk.
|
|
||||||
*
|
|
||||||
* @return the block entities in this chunk
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public abstract Set<Integer> getBlockEntities();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the last time that this chunk changed.
|
* Gets the last time that this chunk changed.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.minestom.server.instance;
|
package net.minestom.server.instance;
|
||||||
|
|
||||||
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
|
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import net.minestom.server.coordinate.Vec;
|
import net.minestom.server.coordinate.Vec;
|
||||||
import net.minestom.server.entity.pathfinding.PFBlock;
|
import net.minestom.server.entity.pathfinding.PFBlock;
|
||||||
@ -9,14 +11,15 @@ import net.minestom.server.instance.block.BlockHandler;
|
|||||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||||
import net.minestom.server.world.biomes.Biome;
|
import net.minestom.server.world.biomes.Biome;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
|
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a {@link Chunk} which store each individual block in memory.
|
* Represents a {@link Chunk} which store each individual block in memory.
|
||||||
@ -28,8 +31,7 @@ public class DynamicChunk extends Chunk {
|
|||||||
protected final TreeMap<Integer, Section> sectionMap = new TreeMap<>();
|
protected final TreeMap<Integer, Section> sectionMap = new TreeMap<>();
|
||||||
|
|
||||||
// Key = ChunkUtils#getBlockIndex
|
// Key = ChunkUtils#getBlockIndex
|
||||||
protected final Int2ObjectOpenHashMap<BlockHandler> handlerMap = new Int2ObjectOpenHashMap<>();
|
protected final Int2ObjectOpenHashMap<BlockEntry> entries = new Int2ObjectOpenHashMap<>();
|
||||||
protected final Int2ObjectOpenHashMap<NBTCompound> nbtMap = new Int2ObjectOpenHashMap<>();
|
|
||||||
protected final Int2ObjectOpenHashMap<BlockHandler> tickableMap = new Int2ObjectOpenHashMap<>();
|
protected final Int2ObjectOpenHashMap<BlockHandler> tickableMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private long lastChangeTime;
|
private long lastChangeTime;
|
||||||
@ -56,17 +58,11 @@ public class DynamicChunk extends Chunk {
|
|||||||
final int index = ChunkUtils.getBlockIndex(x, y, z);
|
final int index = ChunkUtils.getBlockIndex(x, y, z);
|
||||||
// Handler
|
// Handler
|
||||||
final BlockHandler handler = block.handler();
|
final BlockHandler handler = block.handler();
|
||||||
if (handler != null) {
|
|
||||||
this.handlerMap.put(index, handler);
|
|
||||||
} else {
|
|
||||||
this.handlerMap.remove(index);
|
|
||||||
}
|
|
||||||
// Nbt
|
|
||||||
final NBTCompound nbt = block.nbt();
|
final NBTCompound nbt = block.nbt();
|
||||||
if (nbt != null) {
|
if (handler != null || nbt != null) {
|
||||||
this.nbtMap.put(index, nbt);
|
this.entries.put(index, new BlockEntry(handler, nbt));
|
||||||
} else {
|
} else {
|
||||||
this.nbtMap.remove(index);
|
this.entries.remove(index);
|
||||||
}
|
}
|
||||||
// Tickable
|
// Tickable
|
||||||
if (handler != null && handler.isTickable()) {
|
if (handler != null && handler.isTickable()) {
|
||||||
@ -115,23 +111,20 @@ public class DynamicChunk extends Chunk {
|
|||||||
return Block.AIR;
|
return Block.AIR;
|
||||||
}
|
}
|
||||||
final int index = ChunkUtils.getBlockIndex(x, y, z);
|
final int index = ChunkUtils.getBlockIndex(x, y, z);
|
||||||
final BlockHandler handler = handlerMap.get(index);
|
final var entry = entries.get(index);
|
||||||
final NBTCompound nbt = nbtMap.get(index);
|
if (entry != null) {
|
||||||
if (handler != null) {
|
final BlockHandler handler = entry.handler;
|
||||||
block = block.withHandler(handler);
|
final NBTCompound nbt = entry.nbtCompound;
|
||||||
}
|
if (handler != null) {
|
||||||
if (nbt != null) {
|
block = block.withHandler(handler);
|
||||||
block = block.withNbt(nbt);
|
}
|
||||||
|
if (nbt != null) {
|
||||||
|
block = block.withNbt(nbt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<Integer> getBlockEntities() {
|
|
||||||
return nbtMap.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLastChangeTime() {
|
public long getLastChangeTime() {
|
||||||
return lastChangeTime;
|
return lastChangeTime;
|
||||||
@ -149,8 +142,7 @@ public class DynamicChunk extends Chunk {
|
|||||||
packet.chunkX = chunkX;
|
packet.chunkX = chunkX;
|
||||||
packet.chunkZ = chunkZ;
|
packet.chunkZ = chunkZ;
|
||||||
packet.sections = (Map<Integer, Section>) sectionMap.clone(); // TODO deep clone
|
packet.sections = (Map<Integer, Section>) sectionMap.clone(); // TODO deep clone
|
||||||
packet.handlerMap = handlerMap.clone();
|
packet.entries = entries.clone();
|
||||||
packet.nbtMap = nbtMap.clone();
|
|
||||||
|
|
||||||
this.cachedPacketTime = getLastChangeTime();
|
this.cachedPacketTime = getLastChangeTime();
|
||||||
this.cachedPacket = new SoftReference<>(packet);
|
this.cachedPacket = new SoftReference<>(packet);
|
||||||
@ -164,21 +156,42 @@ public class DynamicChunk extends Chunk {
|
|||||||
for (var entry : sectionMap.entrySet()) {
|
for (var entry : sectionMap.entrySet()) {
|
||||||
dynamicChunk.sectionMap.put(entry.getKey(), entry.getValue().clone());
|
dynamicChunk.sectionMap.put(entry.getKey(), entry.getValue().clone());
|
||||||
}
|
}
|
||||||
dynamicChunk.handlerMap.putAll(handlerMap);
|
dynamicChunk.entries.putAll(entries);
|
||||||
dynamicChunk.nbtMap.putAll(nbtMap);
|
|
||||||
|
|
||||||
return dynamicChunk;
|
return dynamicChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
this.sectionMap.values().forEach(Section::clear);
|
this.sectionMap.values().forEach(Section::clear);
|
||||||
this.handlerMap.clear();
|
this.entries.clear();
|
||||||
this.nbtMap.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull Section retrieveSection(int y) {
|
private @NotNull Section retrieveSection(int y) {
|
||||||
final int sectionIndex = ChunkUtils.getSectionAt(y);
|
final int sectionIndex = ChunkUtils.getSectionAt(y);
|
||||||
return getSection(sectionIndex);
|
return getSection(sectionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public static class BlockEntry {
|
||||||
|
private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||||
|
.weakValues()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private final BlockHandler handler;
|
||||||
|
private final NBTCompound nbtCompound;
|
||||||
|
|
||||||
|
public BlockEntry(BlockHandler handler, NBTCompound nbtCompound) {
|
||||||
|
this.handler = handler;
|
||||||
|
this.nbtCompound = nbtCompound != null ? NBT_CACHE.get(nbtCompound, compound -> nbtCompound) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockHandler handler() {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTCompound nbtCompound() {
|
||||||
|
return nbtCompound;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package net.minestom.server.instance.block;
|
package net.minestom.server.instance.block;
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import net.minestom.server.registry.Registry;
|
import net.minestom.server.registry.Registry;
|
||||||
import net.minestom.server.tag.Tag;
|
import net.minestom.server.tag.Tag;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -12,15 +10,8 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
class BlockImpl implements Block {
|
class BlockImpl implements Block {
|
||||||
|
|
||||||
private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder()
|
|
||||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
|
||||||
.weakValues()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private final Registry.BlockEntry registry;
|
private final Registry.BlockEntry registry;
|
||||||
private final Map<String, String> properties;
|
private final Map<String, String> properties;
|
||||||
private final NBTCompound nbt;
|
private final NBTCompound nbt;
|
||||||
@ -57,8 +48,7 @@ class BlockImpl implements Block {
|
|||||||
public @NotNull <T> Block withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
public @NotNull <T> Block withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||||
var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new);
|
var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new);
|
||||||
tag.write(compound, value);
|
tag.write(compound, value);
|
||||||
final var cachedNbt = NBT_CACHE.get(compound, c -> compound);
|
return new BlockImpl(registry, properties, compound, handler);
|
||||||
return new BlockImpl(registry, properties, cachedNbt, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,6 +5,7 @@ import io.netty.buffer.Unpooled;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2LongRBTreeMap;
|
import it.unimi.dsi.fastutil.ints.Int2LongRBTreeMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.instance.DynamicChunk;
|
||||||
import net.minestom.server.instance.Section;
|
import net.minestom.server.instance.Section;
|
||||||
import net.minestom.server.instance.block.BlockHandler;
|
import net.minestom.server.instance.block.BlockHandler;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
@ -34,8 +35,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
|
|||||||
public int chunkX, chunkZ;
|
public int chunkX, chunkZ;
|
||||||
|
|
||||||
public Map<Integer, Section> sections = new HashMap<>();
|
public Map<Integer, Section> sections = new HashMap<>();
|
||||||
public Map<Integer, BlockHandler> handlerMap = new HashMap<>();
|
public Map<Integer, DynamicChunk.BlockEntry> entries = new HashMap<>();
|
||||||
public Map<Integer, NBTCompound> nbtMap = new HashMap<>();
|
|
||||||
|
|
||||||
private static final byte CHUNK_SECTION_COUNT = 16;
|
private static final byte CHUNK_SECTION_COUNT = 16;
|
||||||
private static final int MAX_BITS_PER_ENTRY = 16;
|
private static final int MAX_BITS_PER_ENTRY = 16;
|
||||||
@ -124,17 +124,18 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
|
|||||||
blocks.release();
|
blocks.release();
|
||||||
|
|
||||||
// Block entities
|
// Block entities
|
||||||
if (handlerMap == null || handlerMap.isEmpty()) {
|
if (entries == null || entries.isEmpty()) {
|
||||||
writer.writeVarInt(0);
|
writer.writeVarInt(0);
|
||||||
} else {
|
} else {
|
||||||
List<NBTCompound> compounds = new ArrayList<>();
|
List<NBTCompound> compounds = new ArrayList<>();
|
||||||
for (var entry : handlerMap.entrySet()) {
|
for (var entry : entries.entrySet()) {
|
||||||
final int index = entry.getKey();
|
final int index = entry.getKey();
|
||||||
final BlockHandler handler = entry.getValue();
|
final var blockEntry = entry.getValue();
|
||||||
|
final BlockHandler handler = blockEntry.handler();
|
||||||
final var blockEntityTags = handler.getBlockEntityTags();
|
final var blockEntityTags = handler.getBlockEntityTags();
|
||||||
if (blockEntityTags.isEmpty())
|
if (blockEntityTags.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
final var blockNbt = Objects.requireNonNullElseGet(nbtMap.get(index), NBTCompound::new);
|
final var blockNbt = Objects.requireNonNullElseGet(blockEntry.nbtCompound(), NBTCompound::new);
|
||||||
final var resultNbt = new NBTCompound();
|
final var resultNbt = new NBTCompound();
|
||||||
|
|
||||||
for (Tag<?> tag : blockEntityTags) {
|
for (Tag<?> tag : blockEntityTags) {
|
||||||
@ -219,8 +220,8 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
|
|||||||
|
|
||||||
// Block entities
|
// Block entities
|
||||||
final int blockEntityCount = reader.readVarInt();
|
final int blockEntityCount = reader.readVarInt();
|
||||||
handlerMap = new Int2ObjectOpenHashMap<>();
|
|
||||||
nbtMap = new Int2ObjectOpenHashMap<>();
|
entries = new Int2ObjectOpenHashMap<>();
|
||||||
for (int i = 0; i < blockEntityCount; i++) {
|
for (int i = 0; i < blockEntityCount; i++) {
|
||||||
NBTCompound tag = (NBTCompound) reader.readTag();
|
NBTCompound tag = (NBTCompound) reader.readTag();
|
||||||
final String id = tag.getString("id");
|
final String id = tag.getString("id");
|
||||||
|
Loading…
Reference in New Issue
Block a user