From 358beab2317de92fb42623b17cdfc5598d418a4b Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 00:12:06 +0800 Subject: [PATCH 01/13] =?UTF-8?q?feat(compatibility):=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?FAWE=E7=9A=84//set=E5=92=8C//replace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/compatibility/build.gradle.kts | 7 +- .../worldedit/FastAsyncWorldEditDelegate.java | 87 +++++++++++++++++++ .../worldedit/WorldEditBlockRegister.java | 5 ++ 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index b0cd5fa25..db481a7c5 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -26,9 +26,6 @@ dependencies { compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42") // Placeholder compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") - // WorldEdit - compileOnly("com.sk89q.worldedit:worldedit-core:7.2.19") - compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19") // SlimeWorld compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT") // ModelEngine @@ -44,6 +41,10 @@ dependencies { compileOnly("com.viaversion:viaversion-api:5.3.2") // Skript compileOnly("com.github.SkriptLang:Skript:2.11.0") + // FAWE + compileOnly(platform("com.intellectualsites.bom:bom-newest:1.52")) + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } } java { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java new file mode 100644 index 000000000..d5f7e2d6f --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -0,0 +1,87 @@ +package net.momirealms.craftengine.bukkit.compatibility.worldedit; + +import com.fastasyncworldedit.core.configuration.Settings; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.event.extent.EditSessionEvent; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.eventbus.Subscribe; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.core.block.EmptyBlock; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import org.bukkit.Bukkit; + +public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { + protected FastAsyncWorldEditDelegate(Extent extent) { + super(extent); + } + + public static void init() { + Settings.settings().EXTENT.ALLOWED_PLUGINS.add(FastAsyncWorldEditDelegate.class.getCanonicalName()); + WorldEdit.getInstance().getEventBus().register(new Object() { + @Subscribe + @SuppressWarnings("unused") + public void onEditSessionEvent(EditSessionEvent event) { + if (event.getStage() != EditSession.Stage.BEFORE_HISTORY) return; + event.setExtent(new FastAsyncWorldEditDelegate(event.getExtent())); + } + }); + } + + @Override + public int setBlocks(final Region region, final Pattern pattern) { + this.processBlocks(region, pattern); + return super.setBlocks(region, pattern); + } + + @Override + public int replaceBlocks(Region region, Mask mask, Pattern pattern) { + this.processBlocks(region, pattern); + return super.replaceBlocks(region, mask, pattern); + } + + private void processBlocks(Region region, Pattern pattern) { + try { + for (BlockVector3 position : region) { + BaseBlock blockState = pattern.applyBlock(position); + BlockState oldBlockState = getBlock(position); + int blockX = position.x(); + int blockY = position.y(); + int blockZ = position.z(); + int chunkX = blockX >> 4; + int chunkZ = blockZ >> 4; + int stateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(blockState.getAsString())); + int oldStateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(oldBlockState.getAsString())); + if (BlockStateUtils.isVanillaBlock(stateId) && BlockStateUtils.isVanillaBlock(oldStateId)) continue; + var weWorld = region.getWorld(); + if (weWorld == null) continue; + var world = Bukkit.getWorld(weWorld.getName()); + if (world == null) continue; + var ceWorld = CraftEngine.instance().worldManager().getWorld(world.getUID()); + if (ceWorld == null) continue; + var ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); + if (ceChunk == null) { + world.loadChunk(chunkX, chunkZ); + ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); + if (ceChunk == null) continue; + } + var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (immutableBlockState == null) { + ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE); + } else { + ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java index 2066c2bc1..dd0951843 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java @@ -30,8 +30,12 @@ public class WorldEditBlockRegister { this.isFAWE = isFAWE; CEBlockParser blockParser = new CEBlockParser(WorldEdit.getInstance()); WorldEdit.getInstance().getBlockFactory().register(blockParser); + if (isFAWE) { + FastAsyncWorldEditDelegate.init(); + } } + @SuppressWarnings("deprecation") public void register(Key id) throws ReflectiveOperationException { BlockType blockType = new BlockType(id.toString(), blockState -> blockState); this.field$BlockType$blockMaterial.set(blockType, LazyReference.from(() -> new BukkitBlockRegistry.BukkitBlockMaterial(null, Material.STONE))); @@ -45,6 +49,7 @@ public class WorldEditBlockRegister { } @Override + @SuppressWarnings("deprecation") public Stream getSuggestions(String input) { Set namespacesInUse = manager.namespacesInUse(); From 41a71716234bd584d20d9abdd0a8c25754f95543 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 00:20:30 +0800 Subject: [PATCH 02/13] =?UTF-8?q?perf(bukkit):=20=E4=BC=98=E5=8C=96=20chun?= =?UTF-8?q?ks=20=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/worldedit/FastAsyncWorldEditDelegate.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index d5f7e2d6f..5c1a6ca82 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -17,6 +17,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.world.ChunkPos; import org.bukkit.Bukkit; public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @@ -69,9 +70,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { if (ceWorld == null) continue; var ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); if (ceChunk == null) { - world.loadChunk(chunkX, chunkZ); - ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); - if (ceChunk == null) continue; + ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, new ChunkPos(chunkX, chunkZ)); } var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null) { From bdaf9d759febd042a23a16d2e6e9f870c5229c19 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 00:29:40 +0800 Subject: [PATCH 03/13] =?UTF-8?q?perf(bukkit):=20=E4=BC=98=E5=8C=96=20chun?= =?UTF-8?q?ks=20=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index 5c1a6ca82..02f4c7611 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -18,8 +18,12 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.Bukkit; +import java.util.HashSet; +import java.util.Set; + public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { protected FastAsyncWorldEditDelegate(Extent extent) { super(extent); @@ -51,6 +55,13 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { private void processBlocks(Region region, Pattern pattern) { try { + var weWorld = region.getWorld(); + if (weWorld == null) return; + var world = Bukkit.getWorld(weWorld.getName()); + if (world == null) return; + var ceWorld = CraftEngine.instance().worldManager().getWorld(world.getUID()); + if (ceWorld == null) return; + Set needSaveChunks = new HashSet<>(); for (BlockVector3 position : region) { BaseBlock blockState = pattern.applyBlock(position); BlockState oldBlockState = getBlock(position); @@ -62,15 +73,11 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { int stateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(blockState.getAsString())); int oldStateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(oldBlockState.getAsString())); if (BlockStateUtils.isVanillaBlock(stateId) && BlockStateUtils.isVanillaBlock(oldStateId)) continue; - var weWorld = region.getWorld(); - if (weWorld == null) continue; - var world = Bukkit.getWorld(weWorld.getName()); - if (world == null) continue; - var ceWorld = CraftEngine.instance().worldManager().getWorld(world.getUID()); - if (ceWorld == null) continue; var ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); + boolean needSave = false; if (ceChunk == null) { ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, new ChunkPos(chunkX, chunkZ)); + needSave = true; } var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null) { @@ -78,6 +85,10 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { } else { ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); } + if (needSave) needSaveChunks.add(ceChunk); + } + for (CEChunk ceChunk : needSaveChunks) { + ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); } } catch (Exception e) { CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); From 7266a0eee2758cd76f1392e8976ab5dcde62ee0a Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 00:57:00 +0800 Subject: [PATCH 04/13] =?UTF-8?q?perf(bukkit):=20=E4=BC=98=E5=8C=96=20chun?= =?UTF-8?q?ks=20=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/worldedit/FastAsyncWorldEditDelegate.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index 02f4c7611..d7371b9d1 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -74,10 +74,8 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { int oldStateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(oldBlockState.getAsString())); if (BlockStateUtils.isVanillaBlock(stateId) && BlockStateUtils.isVanillaBlock(oldStateId)) continue; var ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); - boolean needSave = false; if (ceChunk == null) { ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, new ChunkPos(chunkX, chunkZ)); - needSave = true; } var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null) { @@ -85,7 +83,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { } else { ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); } - if (needSave) needSaveChunks.add(ceChunk); + needSaveChunks.add(ceChunk); } for (CEChunk ceChunk : needSaveChunks) { ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); From 350f7159d526465a4ef4a117ee3f5628756087ec Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 14:44:37 +0800 Subject: [PATCH 05/13] =?UTF-8?q?perf(bukkit):=20=E4=BC=98=E5=8C=96=20chun?= =?UTF-8?q?ks=20=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 92 ++++++++++++------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index d7371b9d1..9ff17b4ba 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -5,28 +5,39 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.extent.AbstractDelegateExtent; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.Bukkit; +import java.io.IOException; import java.util.HashSet; +import java.util.Objects; import java.util.Set; public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { - protected FastAsyncWorldEditDelegate(Extent extent) { - super(extent); + private final Set needSaveChunks; + private final CEWorld ceWorld; + + protected FastAsyncWorldEditDelegate(EditSessionEvent event) { + super(event.getExtent()); + this.needSaveChunks = new HashSet<>(); + var weWorld = event.getWorld(); + var world = Bukkit.getWorld(Objects.requireNonNull(weWorld).getName()); + var ceWorld = CraftEngine.instance().worldManager().getWorld(Objects.requireNonNull(world).getUID()); + this.ceWorld = Objects.requireNonNull(ceWorld); } public static void init() { @@ -36,7 +47,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @SuppressWarnings("unused") public void onEditSessionEvent(EditSessionEvent event) { if (event.getStage() != EditSession.Stage.BEFORE_HISTORY) return; - event.setExtent(new FastAsyncWorldEditDelegate(event.getExtent())); + event.setExtent(new FastAsyncWorldEditDelegate(event)); } }); } @@ -53,43 +64,60 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { return super.replaceBlocks(region, mask, pattern); } + @Override + public > boolean setBlock(int x, int y, int z, T block) { + try { + BaseBlock oldBlockState = getBlock(x, y, z).toBaseBlock(); + this.processBlock(x, y, z, block.toBaseBlock(), oldBlockState); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); + } + return super.setBlock(x, y, z, block); + } + + @Override + protected Operation commitBefore() { + try { + for (CEChunk ceChunk : this.needSaveChunks) { + this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation chunks", e); + } + return super.commitBefore(); + } + private void processBlocks(Region region, Pattern pattern) { try { - var weWorld = region.getWorld(); - if (weWorld == null) return; - var world = Bukkit.getWorld(weWorld.getName()); - if (world == null) return; - var ceWorld = CraftEngine.instance().worldManager().getWorld(world.getUID()); - if (ceWorld == null) return; - Set needSaveChunks = new HashSet<>(); for (BlockVector3 position : region) { BaseBlock blockState = pattern.applyBlock(position); - BlockState oldBlockState = getBlock(position); + BaseBlock oldBlockState = getBlock(position).toBaseBlock(); int blockX = position.x(); int blockY = position.y(); int blockZ = position.z(); - int chunkX = blockX >> 4; - int chunkZ = blockZ >> 4; - int stateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(blockState.getAsString())); - int oldStateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(oldBlockState.getAsString())); - if (BlockStateUtils.isVanillaBlock(stateId) && BlockStateUtils.isVanillaBlock(oldStateId)) continue; - var ceChunk = ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); - if (ceChunk == null) { - ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, new ChunkPos(chunkX, chunkZ)); - } - var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); - if (immutableBlockState == null) { - ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE); - } else { - ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); - } - needSaveChunks.add(ceChunk); - } - for (CEChunk ceChunk : needSaveChunks) { - ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); + this.processBlock(blockX, blockY, blockZ, blockState, oldBlockState); } } catch (Exception e) { CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); } } + + private void processBlock(int blockX, int blockY, int blockZ, BaseBlock blockState, BaseBlock oldBlockState) throws IOException { + int chunkX = blockX >> 4; + int chunkZ = blockZ >> 4; + int stateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(blockState.getAsString())); + int oldStateId = BlockStateUtils.blockDataToId(Bukkit.createBlockData(oldBlockState.getAsString())); + if (BlockStateUtils.isVanillaBlock(stateId) && BlockStateUtils.isVanillaBlock(oldStateId)) return; + var ceChunk = this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ); + if (ceChunk == null) { + ceChunk = this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ)); + } + var immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (immutableBlockState == null) { + ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE); + } else { + ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); + } + this.needSaveChunks.add(ceChunk); + } } From c1adcd1ac7f83dc4c92bbafdad5cf308778cd490 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 16:34:07 +0800 Subject: [PATCH 06/13] =?UTF-8?q?refactor(core):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/src/main/resources/translations/en.yml | 1 + bukkit/loader/src/main/resources/translations/zh_cn.yml | 1 + .../momirealms/craftengine/core/font/AbstractFontManager.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index 7b9afee86..06b1acd9f 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -80,6 +80,7 @@ warning.config.image.invalid_font_chars: "Issue found in file - warning.config.image.missing_char: "Issue found in file - The image '' is missing the required 'char' argument." warning.config.image.codepoint_conflict: "Issue found in file - The image '' is using a character '()' in font that has been used by another image ''." warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grid." +warning.config.image.invalid_char: "Issue found in file - The image '' has a 'char' argument is invalid. Please do not write a character that contains two Unicode characters such as '⁉️'." warning.config.image.file_not_found: "Issue found in file - PNG file '' not found for image ''." warning.config.image.invalid_hex_value: "Issue found in file - The image '' is using a unicode character '' that is not a valid hexadecimal (radix 16) value." warning.config.recipe.duplicate: "Issue found in file - Duplicated recipe ''. Please check if there is the same configuration in other files." diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index 860a110af..c1d7f67a8 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -80,6 +80,7 @@ warning.config.image.invalid_font_chars: "在文件 发现问题 warning.config.image.missing_char: "在文件 发现问题 - 图片 '' 缺少必需的 'char' 参数" warning.config.image.codepoint_conflict: "在文件 发现问题 - 图片 '' 在字体 中使用的字符 '()' 已被其他图片 '' 占用" warning.config.image.invalid_codepoint_grid: "在文件 发现问题 - 图片 '' 的 'chars' 码位网格无效" +warning.config.image.invalid_char: "在文件 发现问题 - 图片 '' 的 'char' 参数无效 请不要写一个包含两个 Unicode 的字符例如 '⁉️'" warning.config.image.file_not_found: "在文件 发现问题 - 图片 '' 的 PNG 文件 '' 未找到" warning.config.image.invalid_hex_value: "在文件 发现问题 - 图片 '' 使用的 Unicode 字符 '' 不是有效的十六进制值" warning.config.recipe.duplicate: "在文件 发现问题 - 重复的配方 '' 请检查其他文件中是否存在相同配置" diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index 3277303cf..5dc6a1986 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -482,7 +482,7 @@ public abstract class AbstractFontManager implements FontManager { } } if (codepoints.length == 0) { - throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); + throw new LocalizedResourceConfigException("warning.config.image.invalid_char", path, id); } codepointGrid[i] = codepoints; if (size == -1) size = codepoints.length; From b1fba7627b3efcdf493189f468ff15c03928fe1b Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 16:34:31 +0800 Subject: [PATCH 07/13] =?UTF-8?q?refactor(core):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a91348e1c..30c9a5e26 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.jvmargs=-Xmx1G # Rule: [major update].[feature update].[bug fix] project_version=0.0.53-beta.1 config_version=31 -lang_version=9 +lang_version=10 project_group=net.momirealms latest_supported_version=1.21.5 From d4790659111bb485714914fa269066cca429394f Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 17:34:42 +0800 Subject: [PATCH 08/13] =?UTF-8?q?refactor(bukkit):=20=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?FastAsyncWorldEditDelegate=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index 9ff17b4ba..d7c11b7eb 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.compatibility.worldedit; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.event.extent.EditSessionEvent; @@ -24,9 +25,10 @@ import org.bukkit.Bukkit; import java.io.IOException; import java.util.HashSet; -import java.util.Objects; import java.util.Set; +import static java.util.Objects.requireNonNull; + public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { private final Set needSaveChunks; private final CEWorld ceWorld; @@ -35,9 +37,9 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { super(event.getExtent()); this.needSaveChunks = new HashSet<>(); var weWorld = event.getWorld(); - var world = Bukkit.getWorld(Objects.requireNonNull(weWorld).getName()); - var ceWorld = CraftEngine.instance().worldManager().getWorld(Objects.requireNonNull(world).getUID()); - this.ceWorld = Objects.requireNonNull(ceWorld); + var world = Bukkit.getWorld(requireNonNull(weWorld).getName()); + var ceWorld = CraftEngine.instance().worldManager().getWorld(requireNonNull(world).getUID()); + this.ceWorld = requireNonNull(ceWorld); } public static void init() { @@ -52,17 +54,41 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { }); } + @Override + public int setBlocks(final Set vset, final Pattern pattern) { + this.processBlocks(vset, pattern); + return super.setBlocks(vset, pattern); + } + @Override public int setBlocks(final Region region, final Pattern pattern) { this.processBlocks(region, pattern); return super.setBlocks(region, pattern); } + + @Override + public > int setBlocks(final Region region, final B block) { + this.processBlocks(region, block); + return super.setBlocks(region, block); + } + @Override public int replaceBlocks(Region region, Mask mask, Pattern pattern) { this.processBlocks(region, pattern); return super.replaceBlocks(region, mask, pattern); } + @Override + public > int replaceBlocks(final Region region, final Set filter, final B replacement) { + this.processBlocks(region, replacement); + return super.replaceBlocks(region, filter, replacement); + } + + @Override + public int replaceBlocks(final Region region, final Set filter, final Pattern pattern) { + this.processBlocks(region, pattern); + return super.replaceBlocks(region, filter, pattern); + } @Override public > boolean setBlock(int x, int y, int z, T block) { @@ -87,7 +113,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { return super.commitBefore(); } - private void processBlocks(Region region, Pattern pattern) { + private void processBlocks(Iterable region, Pattern pattern) { try { for (BlockVector3 position : region) { BaseBlock blockState = pattern.applyBlock(position); From 24d859752778d67251f878ff17bc0235c26e8aca Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 17:38:26 +0800 Subject: [PATCH 09/13] =?UTF-8?q?refactor(bukkit):=20=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?FastAsyncWorldEditDelegate=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index d7c11b7eb..8bb866e5d 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -101,6 +101,17 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { return super.setBlock(x, y, z, block); } + @Override + public > boolean setBlock(BlockVector3 position, T block) { + try { + BaseBlock oldBlockState = getBlock(position).toBaseBlock(); + this.processBlock(position.x(), position.y(), position.z(), block.toBaseBlock(), oldBlockState); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); + } + return super.setBlock(position, block); + } + @Override protected Operation commitBefore() { try { From abd3d04b6ee73460b4f40f66aaf63e4c4466ef85 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 17:39:56 +0800 Subject: [PATCH 10/13] =?UTF-8?q?refactor(bukkit):=20=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?FastAsyncWorldEditDelegate=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/worldedit/FastAsyncWorldEditDelegate.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index 8bb866e5d..b0f12698c 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.bukkit.compatibility.worldedit; import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.event.extent.EditSessionEvent; From ef89cc8280fa8c95c132ef5bcfc02317157be365 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 17:57:42 +0800 Subject: [PATCH 11/13] =?UTF-8?q?refactor(bukkit):=20=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index b0f12698c..a09562412 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -113,13 +113,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Override protected Operation commitBefore() { - try { - for (CEChunk ceChunk : this.needSaveChunks) { - this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation chunks", e); - } + saveAllChunks(); return super.commitBefore(); } @@ -133,6 +127,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { int blockZ = position.z(); this.processBlock(blockX, blockY, blockZ, blockState, oldBlockState); } + saveAllChunks(); } catch (Exception e) { CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation blocks", e); } @@ -156,4 +151,16 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { } this.needSaveChunks.add(ceChunk); } + + private void saveAllChunks() { + try { + for (CEChunk ceChunk : this.needSaveChunks) { + System.out.println("saveAllChunks"); + this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); + } + this.needSaveChunks.clear(); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error when recording FastAsyncWorldEdit operation chunks", e); + } + } } From 74cc90c92ca2963eeba6eb7924053b4eeac63696 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 17:57:51 +0800 Subject: [PATCH 12/13] =?UTF-8?q?refactor(bukkit):=20=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/worldedit/FastAsyncWorldEditDelegate.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index a09562412..53987af76 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -155,7 +155,6 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { private void saveAllChunks() { try { for (CEChunk ceChunk : this.needSaveChunks) { - System.out.println("saveAllChunks"); this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); } this.needSaveChunks.clear(); From c33f71ae002483c362e8fee14a5cc179bd1a1a4b Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 2 May 2025 20:52:04 +0800 Subject: [PATCH 13/13] =?UTF-8?q?fix(core):=20=E5=A2=9E=E5=BC=BA=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/en.yml | 2 +- .../src/main/resources/translations/zh_cn.yml | 2 +- .../core/font/AbstractFontManager.java | 15 ++++++++++-- .../craftengine/core/util/CharacterUtils.java | 24 +++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index 06b1acd9f..28cf30a1f 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -80,7 +80,7 @@ warning.config.image.invalid_font_chars: "Issue found in file - warning.config.image.missing_char: "Issue found in file - The image '' is missing the required 'char' argument." warning.config.image.codepoint_conflict: "Issue found in file - The image '' is using a character '()' in font that has been used by another image ''." warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grid." -warning.config.image.invalid_char: "Issue found in file - The image '' has a 'char' argument is invalid. Please do not write a character that contains two Unicode characters such as '⁉️'." +warning.config.image.invalid_char: "Issue found in file - Image '' has a char parameter containing combining characters, which may result in image splitting." warning.config.image.file_not_found: "Issue found in file - PNG file '' not found for image ''." warning.config.image.invalid_hex_value: "Issue found in file - The image '' is using a unicode character '' that is not a valid hexadecimal (radix 16) value." warning.config.recipe.duplicate: "Issue found in file - Duplicated recipe ''. Please check if there is the same configuration in other files." diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index c1d7f67a8..dff59914a 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -80,7 +80,7 @@ warning.config.image.invalid_font_chars: "在文件 发现问题 warning.config.image.missing_char: "在文件 发现问题 - 图片 '' 缺少必需的 'char' 参数" warning.config.image.codepoint_conflict: "在文件 发现问题 - 图片 '' 在字体 中使用的字符 '()' 已被其他图片 '' 占用" warning.config.image.invalid_codepoint_grid: "在文件 发现问题 - 图片 '' 的 'chars' 码位网格无效" -warning.config.image.invalid_char: "在文件 发现问题 - 图片 '' 的 'char' 参数无效 请不要写一个包含两个 Unicode 的字符例如 '⁉️'" +warning.config.image.invalid_char: "在文件 发现问题 - 图片 '' 的 'char' 参数包含组合字符可能导致图片分裂" warning.config.image.file_not_found: "在文件 发现问题 - 图片 '' 的 PNG 文件 '' 未找到" warning.config.image.invalid_hex_value: "在文件 发现问题 - 图片 '' 使用的 Unicode 字符 '' 不是有效的十六进制值" warning.config.recipe.duplicate: "在文件 发现问题 - 重复的配方 '' 请检查其他文件中是否存在相同配置" diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index 5dc6a1986..fd0c030a0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -462,7 +462,18 @@ public abstract class AbstractFontManager implements FontManager { if (character.length() == 1) { chars = List.of(character.toCharArray()); } else { - chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); + if (character.startsWith("\\u")) { + chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); + } else { + if (CharacterUtils.containsCombinedCharacter(character)) { + TranslationManager.instance().log("warning.config.image.invalid_char", path.toString(), id.toString()); + } + StringBuilder stringBuilder = new StringBuilder(); + for (char c : character.toCharArray()) { + stringBuilder.append(String.format("\\u%04x", (int) c)); + } + chars = List.of(CharacterUtils.decodeUnicodeToChars(stringBuilder.toString())); + } } } } @@ -482,7 +493,7 @@ public abstract class AbstractFontManager implements FontManager { } } if (codepoints.length == 0) { - throw new LocalizedResourceConfigException("warning.config.image.invalid_char", path, id); + throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); } codepointGrid[i] = codepoints; if (size == -1) size = codepoints.length; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java index 2d8693728..940274bba 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.util; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import java.util.regex.Pattern; import java.util.stream.IntStream; public class CharacterUtils { @@ -72,4 +73,27 @@ public class CharacterUtils { } return builder.toString(); } + + public static boolean containsCombinedCharacter(String input) { + if (input == null || input.isEmpty() || input.length() == 1) return false; + for (int i = 0; i < input.length();) { + int codePoint = input.codePointAt(i); + i += Character.charCount(codePoint); + int type = Character.getType(codePoint); + if (type == Character.NON_SPACING_MARK || + type == Character.ENCLOSING_MARK || + type == Character.COMBINING_SPACING_MARK || + type == Character.FORMAT || + type == Character.CONTROL || + type == Character.SURROGATE || + type == Character.PRIVATE_USE || + Pattern.compile("[\\p{Mn}\\p{Me}\\p{Mc}\\p{Cf}]").matcher(new String(Character.toChars(codePoint))).find() + ) return true; + if (i < input.length()) { + int nextCodePoint = input.codePointAt(i); + if (Character.isSurrogatePair(Character.toChars(codePoint)[0], Character.toChars(nextCodePoint)[0])) return true; + } + } + return false; + } }