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

Merge pull request #154 from jhqwqmc/dev

feat(compatibility): 兼容FAWE的//set和//replace
This commit is contained in:
XiaoMoMi
2025-05-03 13:38:55 +08:00
committed by GitHub
8 changed files with 213 additions and 5 deletions

View File

@@ -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 {

View File

@@ -0,0 +1,165 @@
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.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.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.Set;
import static java.util.Objects.requireNonNull;
public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
private final Set<CEChunk> needSaveChunks;
private final CEWorld ceWorld;
protected FastAsyncWorldEditDelegate(EditSessionEvent event) {
super(event.getExtent());
this.needSaveChunks = new HashSet<>();
var weWorld = event.getWorld();
var world = Bukkit.getWorld(requireNonNull(weWorld).getName());
var ceWorld = CraftEngine.instance().worldManager().getWorld(requireNonNull(world).getUID());
this.ceWorld = requireNonNull(ceWorld);
}
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));
}
});
}
@Override
public int setBlocks(final Set<BlockVector3> 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 <B extends BlockStateHolder<B>> 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 <B extends BlockStateHolder<B>> int replaceBlocks(final Region region, final Set<BaseBlock> filter, final B replacement) {
this.processBlocks(region, replacement);
return super.replaceBlocks(region, filter, replacement);
}
@Override
public int replaceBlocks(final Region region, final Set<BaseBlock> filter, final Pattern pattern) {
this.processBlocks(region, pattern);
return super.replaceBlocks(region, filter, pattern);
}
@Override
public <T extends BlockStateHolder<T>> 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
public <T extends BlockStateHolder<T>> 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() {
saveAllChunks();
return super.commitBefore();
}
private void processBlocks(Iterable<BlockVector3> region, Pattern pattern) {
try {
for (BlockVector3 position : region) {
BaseBlock blockState = pattern.applyBlock(position);
BaseBlock oldBlockState = getBlock(position).toBaseBlock();
int blockX = position.x();
int blockY = position.y();
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);
}
}
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);
}
private void saveAllChunks() {
try {
for (CEChunk ceChunk : this.needSaveChunks) {
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);
}
}
}

View File

@@ -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<String> getSuggestions(String input) {
Set<String> namespacesInUse = manager.namespacesInUse();

View File

@@ -80,6 +80,7 @@ warning.config.image.invalid_font_chars: "<yellow>Issue found in file <arg:0> -
warning.config.image.missing_char: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'char' argument.</yellow>"
warning.config.image.codepoint_conflict: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character '<arg:3>(<arg:4>)' in font <arg:2> that has been used by another image '<arg:5>'.</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grid.</yellow>"
warning.config.image.invalid_char: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has a char parameter containing combining characters, which may result in image splitting.</yellow>"
warning.config.image.file_not_found: "<yellow>Issue found in file <arg:0> - PNG file '<arg:2>' not found for image '<arg:1>'.</yellow>"
warning.config.image.invalid_hex_value: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a unicode character '<arg:2>' that is not a valid hexadecimal (radix 16) value.</yellow>"
warning.config.recipe.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"

View File

@@ -80,6 +80,7 @@ warning.config.image.invalid_font_chars: "<yellow>在文件 <arg:0> 发现问题
warning.config.image.missing_char: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'char' 参数</yellow>"
warning.config.image.codepoint_conflict: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 在字体 <arg:2> 中使用的字符 '<arg:3>(<arg:4>)' 已被其他图片 '<arg:5>' 占用</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'chars' 码位网格无效</yellow>"
warning.config.image.invalid_char: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'char' 参数包含组合字符可能导致图片分裂</yellow>"
warning.config.image.file_not_found: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 PNG 文件 '<arg:2>' 未找到</yellow>"
warning.config.image.invalid_hex_value: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 使用的 Unicode 字符 '<arg:2>' 不是有效的十六进制值</yellow>"
warning.config.recipe.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的配方 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"

View File

@@ -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()));
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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