9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

优化区块读写性能

This commit is contained in:
XiaoMoMi
2025-08-30 20:22:44 +08:00
parent 81087ea003
commit 16334e0c88
20 changed files with 107 additions and 58 deletions

View File

@@ -4,11 +4,11 @@ plugins {
}
repositories {
mavenCentral()
maven("https://jitpack.io/")
maven("https://repo.momirealms.net/releases/")
maven("https://libraries.minecraft.net/")
maven("https://repo.papermc.io/repository/maven-public/")
mavenCentral()
}
dependencies {
@@ -60,6 +60,8 @@ dependencies {
compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}")
// authlib
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// concurrentutil
compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}")
}
java {

View File

@@ -30,5 +30,4 @@ public class ZaphkielSource implements ExternalItemSource<ItemStack> {
public String id(ItemStack item) {
return Zaphkiel.INSTANCE.api().getItemHandler().getItemId(item);
}
}

View File

@@ -21,6 +21,9 @@ dependencies {
implementation(project(":bukkit:compatibility:legacy"))
implementation(project(":common-files"))
// concurrentutil
implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar"))
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
implementation("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}")
@@ -77,5 +80,6 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil")
}
}

View File

@@ -23,6 +23,9 @@ dependencies {
implementation(project(":bukkit:compatibility:legacy"))
implementation(project(":common-files"))
// concurrentutil
implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar"))
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
implementation("net.momirealms:craft-engine-nms-helper-mojmap:${rootProject.properties["nms_helper_version"]}")
@@ -150,5 +153,6 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil")
}
}

View File

@@ -11,8 +11,8 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder;
import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20;
import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5;
import net.momirealms.craftengine.bukkit.plugin.reflection.leaves.LeavesReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LeavesReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -213,6 +213,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.LOGIN_FINISHED, NetworkReflections.clazz$ClientboundLoginFinishedPacket);
registerNMSPacketConsumer(PacketConsumers.UPDATE_TAGS, NetworkReflections.clazz$ClientboundUpdateTagsPacket);
registerNMSPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_21_5, VersionHelper.isOrAbove1_21_5() ? NetworkReflections.clazz$ServerboundContainerClickPacket : null);
registerS2CByteBufPacketConsumer(PacketConsumers.FORGET_LEVEL_CHUNK, this.packetIds.clientboundForgetLevelChunkPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());

View File

@@ -64,10 +64,7 @@ import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.plugin.network.*;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.chunk.Palette;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData;
@@ -75,6 +72,7 @@ import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
import net.momirealms.craftengine.core.world.collision.AABB;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.*;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -261,12 +259,29 @@ public class PacketConsumers {
return MOD_BLOCK_STATE_MAPPINGS[stateId];
}
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> FORGET_LEVEL_CHUNK = (user, event) -> {
try {
FriendlyByteBuf buf = event.getBuffer();
if (VersionHelper.isOrAbove1_20_2()) {
long chunkPos = buf.readLong();
user.setChunkTrackStatus(new ChunkPos(chunkPos), false);
} else {
int x = buf.readInt();
int y = buf.readInt();
user.setChunkTrackStatus(ChunkPos.of(x, y), false);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e);
}
};
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> LEVEL_CHUNK_WITH_LIGHT = (user, event) -> {
try {
BukkitServerPlayer player = (BukkitServerPlayer) user;
FriendlyByteBuf buf = event.getBuffer();
int chunkX = buf.readInt();
int chunkZ = buf.readInt();
player.setChunkTrackStatus(ChunkPos.of(chunkX, chunkZ), true);
boolean named = !VersionHelper.isOrAbove1_20_2();
// ClientboundLevelChunkPacketData
int heightmapsCount = 0;
@@ -1262,6 +1277,7 @@ public class PacketConsumers {
int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16;
player.setClientSideSectionCount(sectionCount);
player.setClientSideDimension(Key.of(location.toString()));
player.clearTrackedChunks();
} else {
CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist");
}

View File

@@ -67,4 +67,6 @@ public interface PacketIds {
int serverboundInteractPacket();
int clientboundUpdateRecipesPacket();
int clientboundForgetLevelChunkPacket();
}

View File

@@ -169,4 +169,9 @@ public class PacketIds1_20 implements PacketIds {
public int clientboundUpdateAdvancementsPacket() {
return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundUpdateAdvancementsPacket);
}
@Override
public int clientboundForgetLevelChunkPacket() {
return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundForgetLevelChunkPacket);
}
}

View File

@@ -168,4 +168,9 @@ public class PacketIds1_20_5 implements PacketIds {
public int serverboundInteractPacket() {
return PacketIdFinder.serverboundByName("minecraft:interact");
}
@Override
public int clientboundForgetLevelChunkPacket() {
return PacketIdFinder.clientboundByName("minecraft:forget_level_chunk");
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
package net.momirealms.craftengine.bukkit.plugin.reflection.leaves;
//import net.momirealms.craftengine.core.util.MiscUtils;
//import net.momirealms.craftengine.core.util.ReflectionUtils;

View File

@@ -1665,4 +1665,11 @@ public final class NetworkReflections {
),
VersionHelper.isOrAbove1_21_5()
);
public static final Class<?> clazz$ClientboundForgetLevelChunkPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.protocol.game.PacketPlayOutUnloadChunk",
"network.protocol.game.ClientboundForgetLevelChunkPacket"
)
);
}

View File

@@ -34,6 +34,7 @@ import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.ChunkPos;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
import org.bukkit.*;
@@ -109,7 +110,11 @@ public class BukkitServerPlayer extends Player {
private double cachedInteractionRange;
// cooldown data
private CooldownData cooldownData;
// tracked chunks
private final Set<ChunkPos> trackedChunks = Collections.synchronizedSet(new HashSet<>());
// relighted chunks
private final Set<ChunkPos> relightedChunks = Collections.synchronizedSet(new HashSet<>());
// entity view
private final Map<Integer, EntityPacketHandler> entityTypeView = new ConcurrentHashMap<>();
public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) {
@@ -497,9 +502,6 @@ public class BukkitServerPlayer extends Player {
Item<ItemStack> tool = getItemInHand(InteractionHand.MAIN_HAND);
boolean isCorrectTool = FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool.getLiteralObject(), blockState);
// 如果自定义方块在服务端侧未使用正确的工具,那么需要还原挖掘速度
if (!isCorrectTool) {
progress *= (10f / 3f);
}
if (!BlockStateUtils.isCorrectTool(customState, tool)) {
progress *= customState.settings().incorrectToolSpeed();
}
@@ -1032,4 +1034,23 @@ public class BukkitServerPlayer extends Player {
public CooldownData cooldown() {
return this.cooldownData;
}
@Override
public boolean isChunkTracked(ChunkPos chunkPos) {
return this.trackedChunks.contains(chunkPos);
}
@Override
public void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked) {
if (tracked) {
this.trackedChunks.add(chunkPos);
} else {
this.trackedChunks.remove(chunkPos);
}
}
@Override
public void clearTrackedChunks() {
this.trackedChunks.clear();
}
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.world;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector;
@@ -29,7 +30,6 @@ import org.bukkit.event.world.*;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@@ -53,7 +53,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
public BukkitWorldManager(BukkitCraftEngine plugin) {
instance = this;
this.plugin = plugin;
this.worlds = new HashMap<>();
this.worlds = new Object2ObjectOpenHashMap<>(32, 0.5f);
this.storageAdaptor = new DefaultStorageAdaptor();
for (World world : Bukkit.getWorlds()) {
this.worlds.put(world.getUID(), new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor));

View File

@@ -65,6 +65,8 @@ dependencies {
compileOnly("com.mojang:brigadier:${rootProject.properties["mojang_brigadier_version"]}")
// authlib
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// concurrentutil
compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}")
}
java {

View File

@@ -10,8 +10,6 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class Player extends AbstractEntity implements NetWorkUser {
private static final Key TYPE = Key.of("minecraft:player");
@@ -26,10 +24,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
@Override
public abstract Object serverPlayer();
public abstract void sendPackets(List<Object> packet, boolean immediately);
public abstract void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener);
public abstract float getDestroyProgress(Object blockState, BlockPos pos);
public abstract void setClientSideCanBreakBlock(boolean canBreak);

View File

@@ -5,9 +5,11 @@ import io.netty.channel.ChannelHandler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.ChunkPos;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -38,6 +40,10 @@ public interface NetWorkUser {
void sendPacket(Object packet, boolean immediately, Runnable sendListener);
void sendPackets(List<Object> packet, boolean immediately);
void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener);
void sendCustomPayload(Key channel, byte[] data);
void kick(Component message);
@@ -71,4 +77,10 @@ public interface NetWorkUser {
void setShouldProcessFinishConfiguration(boolean shouldProcess);
boolean shouldProcessFinishConfiguration();
boolean isChunkTracked(ChunkPos chunkPos);
void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked);
void clearTrackedChunks();
}

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.core.world;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
@@ -10,17 +10,14 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class CEWorld {
public static final String REGION_DIRECTORY = "craftengine";
protected final World world;
protected final Map<Long, CEChunk> loadedChunkMap;
protected final ConcurrentLong2ReferenceChainedHashTable<CEChunk> loadedChunkMap;
protected final WorldDataStorage worldDataStorage;
protected final ReentrantReadWriteLock loadedChunkMapLock = new ReentrantReadWriteLock();
protected final WorldHeight worldHeightAccessor;
protected final Set<SectionPos> updatedSectionSet = ConcurrentHashMap.newKeySet(128);
@@ -29,7 +26,7 @@ public abstract class CEWorld {
public CEWorld(World world, StorageAdaptor adaptor) {
this.world = world;
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f);
this.worldDataStorage = adaptor.adapt(world);
this.worldHeightAccessor = world.worldHeight();
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
@@ -37,16 +34,15 @@ public abstract class CEWorld {
public CEWorld(World world, WorldDataStorage dataStorage) {
this.world = world;
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f);
this.worldDataStorage = dataStorage;
this.worldHeightAccessor = world.worldHeight();
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
}
public void save() {
this.loadedChunkMapLock.readLock().lock();
try {
for (Map.Entry<Long, CEChunk> entry : this.loadedChunkMap.entrySet()) {
for (ConcurrentLong2ReferenceChainedHashTable.TableEntry<CEChunk> entry : this.loadedChunkMap.entrySet()) {
CEChunk chunk = entry.getValue();
if (chunk.dirty()) {
worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk);
@@ -55,8 +51,6 @@ public abstract class CEWorld {
}
} catch (IOException e) {
CraftEngine.instance().logger().warn("Failed to save world chunks", e);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
}
@@ -65,44 +59,24 @@ public abstract class CEWorld {
}
public boolean isChunkLoaded(final long chunkPos) {
this.loadedChunkMapLock.readLock().lock();
try {
return loadedChunkMap.containsKey(chunkPos);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
}
public void addLoadedChunk(CEChunk chunk) {
this.loadedChunkMapLock.writeLock().lock();
try {
this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk);
} finally {
this.loadedChunkMapLock.writeLock().unlock();
}
}
public void removeLoadedChunk(CEChunk chunk) {
this.loadedChunkMapLock.writeLock().lock();
try {
this.loadedChunkMap.remove(chunk.chunkPos().longKey());
if (this.lastChunk == chunk) {
this.lastChunk = null;
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
}
} finally {
this.loadedChunkMapLock.writeLock().unlock();
}
}
@Nullable
public CEChunk getChunkAtIfLoaded(long chunkPos) {
this.loadedChunkMapLock.readLock().lock();
try {
return getChunkAtIfLoadedMainThread(chunkPos);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
}
@Nullable

View File

@@ -57,6 +57,7 @@ amazon_awssdk_version=2.31.23
amazon_awssdk_eventstream_version=1.0.1
jimfs_version=1.3.0
authlib_version=6.0.58
concurrent_util_version=0.0.3
# Proxy settings
#systemProp.socks.proxyHost=127.0.0.1

Binary file not shown.

Binary file not shown.