From e54e6225803226e6632d8fc58a9c7711e70e6197 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 29 Nov 2025 04:08:48 +0800 Subject: [PATCH 01/46] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=87=AA=E6=89=98?= =?UTF-8?q?=E7=AE=A1=E7=9A=84=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 2 ++ .../core/pack/host/impl/SelfHost.java | 6 ++-- .../pack/host/impl/SelfHostHttpServer.java | 32 +++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index d2a376c04..50f324641 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -197,6 +197,8 @@ resource-pack: deny-non-minecraft-request: true # Generates a single-use, time-limited download link for each player. one-time-token: true + # Enhances validation for deny-non-minecraft-request and one-time-token. + strict-validation: true rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads max-bandwidth-per-second: 5_000_000 # 5MB/s diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java index 7d8937de1..93cede575 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -28,7 +28,7 @@ public class SelfHost implements ResourcePackHost { @Override public CompletableFuture> requestResourcePackDownloadLink(UUID player) { - ResourcePackDownloadData data = SelfHostHttpServer.instance().generateOneTimeUrl(); + ResourcePackDownloadData data = SelfHostHttpServer.instance().generateOneTimeUrl(player); if (data == null) return CompletableFuture.completedFuture(List.of()); return CompletableFuture.completedFuture(List.of(data)); } @@ -77,7 +77,7 @@ public class SelfHost implements ResourcePackHost { boolean oneTimeToken = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("one-time-token", true), "one-time-token"); String protocol = arguments.getOrDefault("protocol", "http").toString(); boolean denyNonMinecraftRequest = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("deny-non-minecraft-request", true), "deny-non-minecraft-request"); - + boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", true), "strict-validation"); Bandwidth limit = null; Map rateLimitingSection = ResourceConfigUtils.getAsMapOrNull(arguments.get("rate-limiting"), "rate-limiting"); @@ -98,7 +98,7 @@ public class SelfHost implements ResourcePackHost { maxBandwidthUsage = ResourceConfigUtils.getAsLong(rateLimitingSection.getOrDefault("max-bandwidth-per-second", 0), "max-bandwidth"); minDownloadSpeed = ResourceConfigUtils.getAsLong(rateLimitingSection.getOrDefault("min-download-speed-per-player", 50_000), "min-download-speed-per-player"); } - selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, limit, oneTimeToken, maxBandwidthUsage, minDownloadSpeed); + selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, limit, oneTimeToken, maxBandwidthUsage, minDownloadSpeed, strictValidation); return INSTANCE; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 9a88c719c..da20a14e3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -33,6 +33,8 @@ import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; +import java.util.Collections; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -41,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; public class SelfHostHttpServer { private static SelfHostHttpServer instance; - private final Cache oneTimePackUrls = Caffeine.newBuilder() + private final Cache oneTimePackUrls = Caffeine.newBuilder() .maximumSize(1024) .scheduler(Scheduler.systemScheduler()) .expireAfterWrite(1, TimeUnit.MINUTES) @@ -67,6 +69,7 @@ public class SelfHostHttpServer { private String url; private boolean denyNonMinecraft = true; private boolean useToken; + private boolean strictValidation = true; private long globalUploadRateLimit = 0; private long minDownloadSpeed = 50_000; @@ -97,13 +100,15 @@ public class SelfHostHttpServer { Bandwidth limitPerIp, boolean token, long globalUploadRateLimit, - long minDownloadSpeed) { + long minDownloadSpeed, + boolean strictValidation) { this.ip = ip; this.url = url; this.denyNonMinecraft = denyNonMinecraft; this.protocol = protocol; this.limitPerIp = limitPerIp; this.useToken = token; + this.strictValidation = strictValidation; if (this.globalUploadRateLimit != globalUploadRateLimit || this.minDownloadSpeed != minDownloadSpeed) { this.globalUploadRateLimit = globalUploadRateLimit; this.minDownloadSpeed = minDownloadSpeed; @@ -214,8 +219,9 @@ public class SelfHostHttpServer { private void handleDownload(ChannelHandlerContext ctx, FullHttpRequest request, QueryStringDecoder queryDecoder) { // 使用一次性token if (useToken) { - String token = queryDecoder.parameters().getOrDefault("token", java.util.Collections.emptyList()).stream().findFirst().orElse(null); - if (!validateToken(token)) { + String token = queryDecoder.parameters().getOrDefault("token", Collections.emptyList()).stream().findFirst().orElse(null); + String clientUUID = strictValidation ? request.headers().get("X-Minecraft-UUID") : null; + if (!validateToken(token, clientUUID)) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid token"); blockedRequests.incrementAndGet(); return; @@ -225,7 +231,12 @@ public class SelfHostHttpServer { // 不是Minecraft客户端 if (denyNonMinecraft) { String userAgent = request.headers().get(HttpHeaderNames.USER_AGENT); - if (userAgent == null || !userAgent.startsWith("Minecraft Java/")) { + boolean nonMinecraftClient = userAgent == null || !userAgent.startsWith("Minecraft Java/"); + if (strictValidation && !nonMinecraftClient) { + String clientVersion = request.headers().get("X-Minecraft-Version"); + nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring(15)); + } + if (nonMinecraftClient) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid client"); blockedRequests.incrementAndGet(); return; @@ -300,10 +311,11 @@ public class SelfHostHttpServer { return rateLimiter.tryConsume(1); } - private boolean validateToken(String token) { + private boolean validateToken(String token, String clientUUID) { if (token == null || token.length() != 36) return false; - Boolean valid = oneTimePackUrls.getIfPresent(token); - if (valid != null) { + String valid = oneTimePackUrls.getIfPresent(token); + boolean isValid = strictValidation ? Objects.equals(valid, clientUUID) : valid != null; + if (isValid) { oneTimePackUrls.invalidate(token); return true; } @@ -348,7 +360,7 @@ public class SelfHostHttpServer { } @Nullable - public ResourcePackDownloadData generateOneTimeUrl() { + public ResourcePackDownloadData generateOneTimeUrl(UUID user) { if (this.resourcePackBytes == null) return null; if (!this.useToken) { @@ -356,7 +368,7 @@ public class SelfHostHttpServer { } String token = UUID.randomUUID().toString(); - oneTimePackUrls.put(token, true); + oneTimePackUrls.put(token, strictValidation ? user.toString().replace("-", "") : ""); return new ResourcePackDownloadData( url() + "download?token=" + URLEncoder.encode(token, StandardCharsets.UTF_8), packUUID, From ac6d9f03907dbeea9ccf1ac0a79df91e56e00e07 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 30 Nov 2025 16:16:25 +0800 Subject: [PATCH 02/46] Update ItemBlockEntityElementConfig.java --- .../entity/renderer/element/ItemBlockEntityElementConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index b68c2af5d..01151fab5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -29,8 +29,8 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { List dataValues = new ArrayList<>(); - ItemEntityData.Item.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues); - ItemEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, dataValues); + ItemEntityData.Item.addEntityData(item.apply(player).getLiteralObject(), dataValues); + ItemEntityData.NoGravity.addEntityData(true, dataValues); return dataValues; }; } From 1fcf903036dfab3768b93f538ff2fb25befff0f3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 00:29:30 +0800 Subject: [PATCH 03/46] Update BukkitNetworkManager.java --- .../bukkit/plugin/network/BukkitNetworkManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 8ea7189f7..54482047e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -2217,7 +2217,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (Config.entityCullingRayTracing()) { SectionPos sectionPos = SectionPos.of(sPos); ClientChunk trackedChunk = user.getTrackedChunk(sectionPos.asChunkPos().longKey); - clientSection = trackedChunk.sectionById(sectionPos.y); + if (trackedChunk != null) { + clientSection = trackedChunk.sectionById(sectionPos.y); + } } for (int i = 0; i < blocks; i++) { From cf18f40c795333e2cca343ce3564c765c31c2b61 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:03:32 +0800 Subject: [PATCH 04/46] =?UTF-8?q?=E6=9B=B4=E8=B4=B4=E5=88=87=E5=8E=9F?= =?UTF-8?q?=E7=89=88=E7=9A=84snbt=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/plugin/BukkitPlatform.java | 23 - .../src/main/resources/translations/en.yml | 26 + .../src/main/resources/translations/zh_cn.yml | 26 + .../item/modifier/ComponentsModifier.java | 10 +- .../craftengine/core/plugin/Platform.java | 4 - .../config/template/ArgumentString.java | 11 +- .../craftengine/core/util/MiscUtils.java | 4 + .../craftengine/core/util/SNBTReader.java | 289 ------ .../core/util/snbt/SnbtGrammar.java | 865 ++++++++++++++++++ .../core/util/snbt/SnbtOperations.java | 93 ++ .../craftengine/core/util/snbt/TagParser.java | 87 ++ .../core/util/snbt/parse/Atom.java | 15 + .../util/snbt/parse/CachedParseState.java | 253 +++++ .../core/util/snbt/parse/Control.java | 18 + .../util/snbt/parse/DelayedException.java | 19 + .../core/util/snbt/parse/Dictionary.java | 92 ++ .../core/util/snbt/parse/ErrorCollector.java | 98 ++ .../core/util/snbt/parse/ErrorEntry.java | 4 + .../LocalizedCommandSyntaxException.java | 84 ++ .../LocalizedDynamicCommandExceptionType.java | 28 + .../util/snbt/parse/LocalizedMessage.java | 53 ++ .../LocalizedSimpleCommandExceptionType.java | 25 + .../core/util/snbt/parse/NamedRule.java | 7 + .../core/util/snbt/parse/ParseState.java | 42 + .../core/util/snbt/parse/Rule.java | 54 ++ .../core/util/snbt/parse/Scope.java | 316 +++++++ .../util/snbt/parse/SuggestionSupplier.java | 11 + .../core/util/snbt/parse/Term.java | 237 +++++ .../core/util/snbt/parse/grammar/Grammar.java | 53 ++ .../parse/grammar/GreedyPatternParseRule.java | 34 + .../grammar/GreedyPredicateParseRule.java | 49 + .../parse/grammar/NumberRunParseRule.java | 47 + .../grammar/StringReaderParserState.java | 29 + .../snbt/parse/grammar/StringReaderTerms.java | 65 ++ .../grammar/UnquotedStringParseRule.java | 33 + gradle.properties | 2 +- 36 files changed, 2786 insertions(+), 320 deletions(-) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java index 9a6729b0a..21bd8791a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java @@ -31,34 +31,11 @@ public class BukkitPlatform implements Platform { Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); } - @SuppressWarnings("unchecked") - @Override - public Object snbtToJava(String nbt) { - try { - Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}"); - Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); - return map.get("root"); - } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); - } - } - @Override public Tag jsonToSparrowNBT(JsonElement json) { return MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, json); } - @Override - public Tag snbtToSparrowNBT(String nbt) { - try { - Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}"); - CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); - return map.get("root"); - } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); - } - } - @Override public Tag javaToSparrowNBT(Object object) { return MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, object); diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..63419843e 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -90,6 +90,32 @@ warning.config.type.vec3d: "Issue found in file - Failed to load warning.config.type.map: "Issue found in file - Failed to load '': Cannot cast '' to Map type for option ''." warning.config.type.aabb: "Issue found in file - Failed to load '': Cannot cast '' to AABB type for option ''." warning.config.type.snbt.invalid_syntax: "Issue found in file - Failed to load '': Invalid snbt syntax ''." +warning.config.type.snbt.invalid_syntax.parse_error: " at position : " +warning.config.type.snbt.invalid_syntax.here: "<--[HERE]" +warning.config.type.snbt.parser.expected_string_uuid: "Expected a string representing a valid UUID" +warning.config.type.snbt.parser.expected_number_or_boolean: "Expected a number or a boolean" +warning.config.type.snbt.parser.trailing: "Unexpected trailing data" +warning.config.type.snbt.parser.expected.compound: "Expected compound tag" +warning.config.type.snbt.parser.number_parse_failure: "Failed to parse number: " +warning.config.type.snbt.parser.expected_hex_escape: "Expected a character literal of length " +warning.config.type.snbt.parser.invalid_codepoint: "Invalid Unicode character value: " +warning.config.type.snbt.parser.no_such_operation: "No such operation: " +warning.config.type.snbt.parser.expected_integer_type: "Expected an integer number" +warning.config.type.snbt.parser.expected_float_type: "Expected a floating point number" +warning.config.type.snbt.parser.expected_non_negative_number: "Expected a non-negative number" +warning.config.type.snbt.parser.invalid_character_name: "Invalid Unicode character name" +warning.config.type.snbt.parser.invalid_array_element_type: "Invalid array element type" +warning.config.type.snbt.parser.invalid_unquoted_start: "Unquoted strings can't start with digits 0-9, + or -" +warning.config.type.snbt.parser.expected_unquoted_string: "Expected a valid unquoted string" +warning.config.type.snbt.parser.invalid_string_contents: "Invalid string contents" +warning.config.type.snbt.parser.expected_binary_numeral: "Expected a binary number" +warning.config.type.snbt.parser.underscore_not_allowed: "Underscore is not allowed in binary numerals" +warning.config.type.snbt.parser.expected_decimal_numeral: "Expected a decimal number" +warning.config.type.snbt.parser.expected_hex_numeral: "Expected a hexadecimal number" +warning.config.type.snbt.parser.empty_key: "Key cannot be empty" +warning.config.type.snbt.parser.leading_zero_not_allowed: "Decimal numbers can't start with 0" +warning.config.type.snbt.parser.infinity_not_allowed: "Non-finite numbers are not allowed" +warning.config.type.snbt.parser.incorrect: "Expected literal " warning.config.number.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for number argument." warning.config.number.invalid_type: "Issue found in file - The config '' is using an invalid number argument type ''." warning.config.number.missing_argument: "Issue found in file - The config '' is missing the argument for 'number'." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..160e44580 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,6 +88,32 @@ warning.config.type.vector3f: "在文件 发现问题 - 无法 warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" +warning.config.type.snbt.invalid_syntax.parse_error: ", 位于第 个字符: " +warning.config.type.snbt.invalid_syntax.here: "<--[此处]" +warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效 UUID 的字符串" +warning.config.type.snbt.parser.expected_number_or_boolean: "应为数字或布尔型" +warning.config.type.snbt.parser.trailing: "多余的尾随数据" +warning.config.type.snbt.parser.expected.compound: "应为复合标签" +warning.config.type.snbt.parser.number_parse_failure: "解析数字失败: " +warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为 " +warning.config.type.snbt.parser.invalid_codepoint: "无效的 Unicode 字符码位: " +warning.config.type.snbt.parser.no_such_operation: "不存在的操作: " +warning.config.type.snbt.parser.expected_integer_type: "应为整数" +warning.config.type.snbt.parser.expected_float_type: "应为浮点数" +warning.config.type.snbt.parser.expected_non_negative_number: "应为非负数" +warning.config.type.snbt.parser.invalid_character_name: "无效的 Unicode 字符名称" +warning.config.type.snbt.parser.invalid_array_element_type: "无效的数组元素类型" +warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字 0-9、+ 或 - 开头" +warning.config.type.snbt.parser.expected_unquoted_string: "应为有效的无引号字符串" +warning.config.type.snbt.parser.invalid_string_contents: "无效的字符串内容" +warning.config.type.snbt.parser.expected_binary_numeral: "应为二进制数" +warning.config.type.snbt.parser.underscore_not_allowed: "数字的开头和结尾不允许使用下划线字符" +warning.config.type.snbt.parser.expected_decimal_numeral: "应为十进制数" +warning.config.type.snbt.parser.expected_hex_numeral: "应为十六进制数" +warning.config.type.snbt.parser.empty_key: "键不能为空" +warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以 0 开头" +warning.config.type.snbt.parser.infinity_not_allowed: "不允许使用非有限数的数值" +warning.config.type.snbt.parser.incorrect: "应为字面量 " warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" warning.config.number.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的数字类型 ''" warning.config.number.missing_argument: "在文件 发现问题 - 配置项 '' 缺少数字参数" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index 143e4572f..113f33324 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -1,12 +1,15 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.gson.JsonElement; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Pair; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.snbt.TagParser; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -41,7 +44,12 @@ public class ComponentsModifier implements ItemDataModifier { if (string.startsWith("(json) ")) { return CraftEngine.instance().platform().jsonToSparrowNBT(GsonHelper.get().fromJson(string.substring("(json) ".length()), JsonElement.class)); } else if (string.startsWith("(snbt) ")) { - return CraftEngine.instance().platform().snbtToSparrowNBT(string.substring("(snbt) ".length())); + String snbt = string.substring("(snbt) ".length()); + try { + return TagParser.parseCompoundFully(snbt); + } catch (CommandSyntaxException e) { + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); + } } } return CraftEngine.instance().platform().javaToSparrowNBT(value); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java index 6a8ec531b..1195d6904 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java @@ -10,12 +10,8 @@ public interface Platform { void dispatchCommand(String command); - Object snbtToJava(String nbt); - Tag jsonToSparrowNBT(JsonElement json); - Tag snbtToSparrowNBT(String nbt); - Tag javaToSparrowNBT(Object object); World getWorld(String name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java index f3846da86..15f821b76 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -1,8 +1,9 @@ package net.momirealms.craftengine.core.plugin.config.template; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.SNBTReader; +import net.momirealms.craftengine.core.util.snbt.TagParser; import java.util.ArrayList; import java.util.List; @@ -67,8 +68,14 @@ public interface ArgumentString { } else { this.placeholder = placeholderContent.substring(0, separatorIndex); String defaultValueString = placeholderContent.substring(separatorIndex + 2); + Object parsed; try { - this.defaultValue = ((TemplateManagerImpl) TemplateManager.INSTANCE).preprocessUnknownValue(new SNBTReader(defaultValueString).deserializeAsJava()); + parsed = TagParser.parseObjectFully(defaultValueString); + } catch (CommandSyntaxException e) { + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); + } + try { + this.defaultValue = ((TemplateManagerImpl) TemplateManager.INSTANCE).preprocessUnknownValue(parsed); } catch (LocalizedResourceConfigException e) { e.appendTailArgument(this.placeholder); throw e; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index 523bbc364..6f327817a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -415,4 +415,8 @@ public class MiscUtils { } return false; } + + public static int growByHalf(int value, int minValue) { + return (int) Math.max(Math.min((long) value + (value >> 1), 2147483639L), minValue); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java deleted file mode 100644 index 0be81388d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ /dev/null @@ -1,289 +0,0 @@ -package net.momirealms.craftengine.core.util; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -public final class SNBTReader extends DefaultStringReader { - private static final char COMPOUND_START = '{'; - private static final char COMPOUND_END = '}'; - private static final char LIST_START = '['; - private static final char LIST_END = ']'; - private static final char STRING_DELIMITER = '"'; - private static final char SINGLE_QUOTES = '\''; - private static final char DOUBLE_QUOTES = '"'; - private static final char KEY_VALUE_SEPARATOR = ':'; - private static final char ELEMENT_SEPARATOR = ','; - - private static final char ARRAY_DELIMITER = ';'; - private static final char BYTE_ARRAY = 'b'; - private static final char INT_ARRAY = 'i'; - private static final char LONG_ARRAY = 'l'; - - public SNBTReader(String content) { - super(content); - } - - public Object deserializeAsJava() { - Object result = this.parseValue(); - this.skipWhitespace(); - if (getCursor() != getTotalLength()) - throw new IllegalArgumentException("Extra content at end: " + substring(getCursor(), getTotalLength())); - return result; - } - - // 开始解析, 步进字符. - private Object parseValue() { - skipWhitespace(); - return switch (peek()) { - case COMPOUND_START -> parseCompound(); - case LIST_START -> parseList(); - case DOUBLE_QUOTES -> { - skip(); - yield readStringUntil(DOUBLE_QUOTES); - } - case SINGLE_QUOTES -> { - skip(); - yield readStringUntil(SINGLE_QUOTES); - } - default -> parsePrimitive(); - }; - } - - // 解析包小肠 {} - private Map parseCompound() { - skip(); // 跳过 '{' - skipWhitespace(); - - Map compoundMap = new LinkedHashMap<>(); - - if (canRead() && peek() != COMPOUND_END) { - do { - String key = parseKey(); - if (!canRead() || peek() != KEY_VALUE_SEPARATOR) { - throw new IllegalArgumentException("Expected ':' at position " + getCursor()); - } - skip(); // 跳过 ':' - Object value = parseValue(); - compoundMap.put(key, value); - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != COMPOUND_END) { - throw new IllegalArgumentException("Expected '}' at position " + getCursor()); - } - skip(); // 跳过 '}' - return compoundMap; - } - - // 解析列表值 [1, 2, 3] - private Object parseList() { - skip(); // 跳过 '[' - skipWhitespace(); - - // 检查接下来的2个非空格字符, 确认是否要走数组解析. - if (canRead()) { - setMarker(cursor); // 记录指针, 尝试解析数组. - char typeChar = Character.toLowerCase(peek()); - if (typeChar == BYTE_ARRAY || typeChar == INT_ARRAY || typeChar == LONG_ARRAY) { - skip(); - skipWhitespace(); - if (canRead() && peek() == ARRAY_DELIMITER) { // 下一个必须是 ';' - skip(); - switch (typeChar) { // 解析并返回数组喵 - case BYTE_ARRAY -> { - return parseArray(list -> { - byte[] bytes = new byte[list.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = list.get(i).byteValue(); - } - return bytes; - }); - } - case INT_ARRAY -> { - return parseArray(list -> { - int[] ints = new int[list.size()]; - for (int i = 0; i < ints.length; i++) { - ints[i] = list.get(i).intValue(); - } - return ints; - }); - } - case LONG_ARRAY -> { - return parseArray(list -> { - long[] longs = new long[list.size()]; - for (int i = 0; i < longs.length; i++) { - longs[i] = list.get(i).longValue(); - } - return longs; - }); - } - } - } - } - restore(); // 复原指针. - } - - List elementList = new ArrayList<>(); - - if (canRead() && peek() != LIST_END) { - do { - elementList.add(parseValue()); - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != LIST_END) { - throw new IllegalArgumentException("Expected ']' at position " + getCursor()); - } - skip(); // 跳过 ']' - return elementList; - } - - // 解析数组 [I; 11, 41, 54] - // ArrayType -> B, I, L. - private Object parseArray(Function, Object> convertor) { - skipWhitespace(); - // 用来暂存解析出的数字 - List elements = new ArrayList<>(); - if (canRead() && peek() != LIST_END) { - do { - Object element = parseValue(); - - // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. - // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. - // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element type at pos " + getCursor()); - if (!(element instanceof Number number)) - throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); - - elements.add(number); // 校验通过后加入 - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != LIST_END) - throw new IllegalArgumentException("Expected ']' at position " + getCursor()); - skip(); // 跳过 ']' - return convertor.apply(elements); - } - - // 解析Key值 - private String parseKey() { - skipWhitespace(); - if (!canRead()) { - throw new IllegalArgumentException("Unterminated key at " + getCursor()); - } - - // 如果有双引号就委托给string解析处理. - char peek = peek(); - if (peek == STRING_DELIMITER) { - skip(); - return readStringUntil(STRING_DELIMITER); - } else if (peek == SINGLE_QUOTES) { - skip(); - return readStringUntil(SINGLE_QUOTES); - } - - int start = getCursor(); - while (canRead()) { - char c = peek(); - if (c == ' ') break; // 忽略 key 后面的空格, { a :1} 应当解析成 {a:1} - if (Character.isJavaIdentifierPart(c)) skip(); else break; - } - - String key = substring(start, getCursor()); - skipWhitespace(); // 跳过 key 后面的空格. - return key; - } - - // 解析原生值 - private Object parsePrimitive() { - // 先解析获取值的长度 - int tokenStart = getCursor(); - int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". - boolean contentHasWhitespace = false; // 记录值中有没有空格. - while (canRead()) { - char c = peek(); - if (c == ',' || c == ']' || c == '}') break; - skip(); - if (c == ' ') { - lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. - continue; - } - if (lastWhitespace > 0) { - lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. - contentHasWhitespace = true; - } - } - int tokenLength = getCursor() - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. - if (tokenLength == 0) return null; // 如果值长度为0则返回null. - if (contentHasWhitespace) return substring(tokenStart, tokenStart + tokenLength); // 如果值的中间有空格, 一定是字符串, 可直接返回. - - // 布尔值检查 - if (tokenLength == 4) { - if (matchesAt(tokenStart, "true")) return Boolean.TRUE; - if (matchesAt(tokenStart, "null")) return null; // 支持 {key:null}. - } else if (tokenLength == 5) { - if (matchesAt(tokenStart, "false")) return Boolean.FALSE; - } - if (tokenLength > 1) { - // 至少有1个字符,给了后缀的可能性 - char lastChar = charAt(tokenStart + tokenLength - 1); - try { - switch (lastChar) { - case 'b', 'B' -> { - return Byte.parseByte(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 's', 'S' -> { - return Short.parseShort(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 'l', 'L' -> { - return Long.parseLong(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 'f', 'F' -> { - return Float.parseFloat(substring(tokenStart, tokenStart + tokenLength)); - } - case 'd', 'D' -> { - return Double.parseDouble(substring(tokenStart, tokenStart + tokenLength)); - } - default -> { - String fullString = substring(tokenStart, tokenStart + tokenLength); - try { - double d = Double.parseDouble(fullString); - if (d % 1 != 0 || fullString.contains(".") || fullString.contains("e")) { - return d; - } else { - return (int) d; - } - } catch (NumberFormatException e) { - return fullString; - } - } - } - } catch (NumberFormatException e) { - return substring(tokenStart, tokenStart + tokenLength); - } - } else { - char onlyChar = charAt(tokenStart); - if (isNumber(onlyChar)) { - return onlyChar - '0'; - } else { - return String.valueOf(onlyChar); - } - } - } - - // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. - private boolean matchesAt(int start, String target) { - for (int i = 0; i < target.length(); i++) { - char c1 = charAt(start + i); - char c2 = target.charAt(i); - if (c1 != c2 && c1 != (c2 ^ 32)) return false; // 忽略大小写比较 - } - return true; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java new file mode 100644 index 000000000..ad96f56e9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -0,0 +1,865 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JavaOps; +import it.unimi.dsi.fastutil.bytes.ByteArrayList; +import it.unimi.dsi.fastutil.bytes.ByteList; +import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.snbt.parse.*; +import net.momirealms.craftengine.core.util.snbt.parse.Dictionary; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.*; + +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +public class SnbtGrammar { + private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( + number -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(number)) + ); + static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( + length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) + ); + private static final DynamicCommandExceptionType ERROR_INVALID_CODEPOINT = new LocalizedDynamicCommandExceptionType( + codepoint -> new LocalizedMessage("warning.config.type.snbt.parser.invalid_codepoint", String.valueOf(codepoint)) + ); + private static final DynamicCommandExceptionType ERROR_NO_SUCH_OPERATION = new LocalizedDynamicCommandExceptionType( + operation -> new LocalizedMessage("warning.config.type.snbt.parser.no_such_operation", String.valueOf(operation)) + ); + static final DelayedException ERROR_EXPECTED_INTEGER_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_integer_type")) + ); + private static final DelayedException ERROR_EXPECTED_FLOAT_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_float_type")) + ); + static final DelayedException ERROR_EXPECTED_NON_NEGATIVE_NUMBER = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_non_negative_number")) + ); + private static final DelayedException ERROR_INVALID_CHARACTER_NAME = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_character_name")) + ); + static final DelayedException ERROR_INVALID_ARRAY_ELEMENT_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_array_element_type")) + ); + private static final DelayedException ERROR_INVALID_UNQUOTED_START = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_unquoted_start")) + ); + private static final DelayedException ERROR_EXPECTED_UNQUOTED_STRING = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_unquoted_string")) + ); + private static final DelayedException ERROR_INVALID_STRING_CONTENTS = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_string_contents")) + ); + private static final DelayedException ERROR_EXPECTED_BINARY_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_binary_numeral")) + ); + private static final DelayedException ERROR_UNDERSCORE_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.underscore_not_allowed")) + ); + private static final DelayedException ERROR_EXPECTED_DECIMAL_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_decimal_numeral")) + ); + private static final DelayedException ERROR_EXPECTED_HEX_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("Expected a hexadecimal number")) + ); + private static final DelayedException ERROR_EMPTY_KEY = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.empty_key")) + ); + private static final DelayedException ERROR_LEADING_ZERO_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.leading_zero_not_allowed")) + ); + private static final DelayedException ERROR_INFINITY_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.infinity_not_allowed")) + ); + private static final NumberRunParseRule BINARY_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_BINARY_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '_' -> true; + default -> false; + }; + } + }; + private static final NumberRunParseRule DECIMAL_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_DECIMAL_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' -> true; + default -> false; + }; + } + }; + private static final NumberRunParseRule HEX_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_HEX_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '_', 'a', 'b', 'c', 'd', 'e', 'f' -> true; + default -> false; + }; + } + }; + private static final GreedyPredicateParseRule PLAIN_STRING_CHUNK = new GreedyPredicateParseRule(1, ERROR_INVALID_STRING_CONTENTS) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '"', '\'', '\\' -> false; + default -> true; + }; + } + }; + private static final StringReaderTerms.TerminalCharacters NUMBER_LOOKEAHEAD = new StringReaderTerms.TerminalCharacters(CharList.of()) { + @Override + protected boolean isAccepted(char c) { + return canStartNumber(c); + } + }; + private static final Pattern UNICODE_NAME = Pattern.compile("[-a-zA-Z0-9 ]+"); + + static DelayedException createNumberParseError(NumberFormatException numberFormatException) { + return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, numberFormatException.getMessage()); + } + + private static boolean isAllowedToStartUnquotedString(char c) { + return !canStartNumber(c); + } + + static boolean canStartNumber(char c) { + return switch (c) { + case '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> true; + default -> false; + }; + } + + static boolean needsUnderscoreRemoval(String text) { + return text.indexOf(95) != -1; + } + + private static void cleanAndAppend(StringBuilder stringBuilder, String text) { + cleanAndAppend(stringBuilder, text, needsUnderscoreRemoval(text)); + } + + static void cleanAndAppend(StringBuilder stringBuilder, String text, boolean removeUnderscores) { + if (removeUnderscores) { + for (char c : text.toCharArray()) { + if (c != '_') { + stringBuilder.append(c); + } + } + } + stringBuilder.append(text); + } + + static short parseUnsignedShort(String text, int radix) { + int i = Integer.parseInt(text, radix); + if (i >> 16 == 0) { + return (short)i; + } + throw new NumberFormatException("out of range: " + i); + } + + @Nullable + private static T createFloat( + DynamicOps ops, + Sign sign, + @Nullable String wholePart, + @Nullable String fractionPart, + @Nullable Signed exponentPart, + @Nullable TypeSuffix suffix, + ParseState parseState + ) { + StringBuilder stringBuilder = new StringBuilder(); + sign.append(stringBuilder); + if (wholePart != null) { + cleanAndAppend(stringBuilder, wholePart); + } + + if (fractionPart != null) { + stringBuilder.append('.'); + cleanAndAppend(stringBuilder, fractionPart); + } + + if (exponentPart != null) { + stringBuilder.append('e'); + exponentPart.sign().append(stringBuilder); + cleanAndAppend(stringBuilder, exponentPart.value); + } + + try { + String string = stringBuilder.toString(); + + return switch (suffix) { + case null -> convertDouble(ops, parseState, string); + case FLOAT -> convertFloat(ops, parseState, string); + case DOUBLE -> convertDouble(ops, parseState, string); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_FLOAT_TYPE); + yield null; + } + }; + } catch (NumberFormatException var11) { + parseState.errorCollector().store(parseState.mark(), createNumberParseError(var11)); + return null; + } + } + + @Nullable + private static T convertFloat(DynamicOps ops, ParseState parseState, String value) { + float f = Float.parseFloat(value); + if (!Float.isFinite(f)) { + parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + return null; + } + return ops.createFloat(f); + } + + @Nullable + private static T convertDouble(DynamicOps ops, ParseState parseState, String value) { + double d = Double.parseDouble(value); + if (!Double.isFinite(d)) { + parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + return null; + } + return ops.createDouble(d); + } + + private static String joinList(List list) { + return switch (list.size()) { + case 0 -> ""; + case 1 -> list.getFirst(); + default -> String.join("", list); + }; + } + + @SuppressWarnings("unchecked") + public static Grammar createParser(DynamicOps ops) { + T object = ops.createBoolean(true); + T object1 = ops.createBoolean(false); + T object2 = ops.emptyMap(); + T object3 = ops.emptyList(); + Dictionary dictionary = new Dictionary<>(); + Atom atom = Atom.of("sign"); + dictionary.put( + atom, + Term.alternative( + Term.sequence(StringReaderTerms.character('+'), Term.marker(atom, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(atom, Sign.MINUS)) + ), + scope -> scope.getOrThrow(atom) + ); + Atom atom1 = Atom.of("integer_suffix"); + dictionary.put( + atom1, + Term.alternative( + Term.sequence( + StringReaderTerms.characters('u', 'U'), + Term.alternative( + Term.sequence( + StringReaderTerms.characters('b', 'B'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + ), + Term.sequence( + StringReaderTerms.characters('i', 'I'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) + ), + Term.sequence( + StringReaderTerms.characters('l', 'L'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) + ) + ) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.alternative( + Term.sequence( + StringReaderTerms.characters('b', 'B'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) + ), + Term.sequence( + StringReaderTerms.characters('i', 'I'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) + ), + Term.sequence( + StringReaderTerms.characters('l', 'L'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) + ) + ) + ), + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.LONG))) + ), + scope -> scope.getOrThrow(atom1) + ); + Atom atom2 = Atom.of("binary_numeral"); + dictionary.put(atom2, BINARY_NUMERAL); + Atom atom3 = Atom.of("decimal_numeral"); + dictionary.put(atom3, DECIMAL_NUMERAL); + Atom atom4 = Atom.of("hex_numeral"); + dictionary.put(atom4, HEX_NUMERAL); + Atom atom5 = Atom.of("integer_literal"); + NamedRule namedRule = dictionary.put( + atom5, + Term.sequence( + Term.optional(dictionary.named(atom)), + Term.alternative( + Term.sequence( + StringReaderTerms.character('0'), + Term.cut(), + Term.alternative( + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), dictionary.named(atom4)), + Term.sequence(StringReaderTerms.characters('b', 'B'), dictionary.named(atom2)), + Term.sequence(dictionary.named(atom3), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(atom3, "0") + ) + ), + dictionary.named(atom3) + ), + Term.optional(dictionary.named(atom1)) + ), + scope -> { + IntegerSuffix integerSuffix = scope.getOrDefault(atom1, IntegerSuffix.EMPTY); + Sign sign = scope.getOrDefault(atom, Sign.PLUS); + String string = scope.get(atom3); + if (string != null) { + return new IntegerLiteral(sign, Base.DECIMAL, string, integerSuffix); + } + String string1 = scope.get(atom4); + if (string1 != null) { + return new IntegerLiteral(sign, Base.HEX, string1, integerSuffix); + } + String string2 = scope.getOrThrow(atom2); + return new IntegerLiteral(sign, Base.BINARY, string2, integerSuffix); + } + ); + Atom atom6 = Atom.of("float_type_suffix"); + dictionary.put( + atom6, + Term.alternative( + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(atom6, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(atom6, TypeSuffix.DOUBLE)) + ), + scope -> scope.getOrThrow(atom6) + ); + Atom> atom7 = Atom.of("float_exponent_part"); + dictionary.put( + atom7, + Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(dictionary.named(atom)), dictionary.named(atom3)), + scope -> new Signed<>(scope.getOrDefault(atom, Sign.PLUS), scope.getOrThrow(atom3)) + ); + Atom atom8 = Atom.of("float_whole_part"); + Atom atom9 = Atom.of("float_fraction_part"); + Atom atom10 = Atom.of("float_literal"); + dictionary.putComplex( + atom10, + Term.sequence( + Term.optional(dictionary.named(atom)), + Term.alternative( + Term.sequence( + dictionary.namedWithAlias(atom3, atom8), + StringReaderTerms.character('.'), + Term.cut(), + Term.optional(dictionary.namedWithAlias(atom3, atom9)), + Term.optional(dictionary.named(atom7)), + Term.optional(dictionary.named(atom6)) + ), + Term.sequence( + StringReaderTerms.character('.'), + Term.cut(), + dictionary.namedWithAlias(atom3, atom9), + Term.optional(dictionary.named(atom7)), + Term.optional(dictionary.named(atom6)) + ), + Term.sequence(dictionary.namedWithAlias(atom3, atom8), dictionary.named(atom7), Term.cut(), Term.optional(dictionary.named(atom6))), + Term.sequence(dictionary.namedWithAlias(atom3, atom8), Term.optional(dictionary.named(atom7)), dictionary.named(atom6)) + ) + ), + parseState -> { + Scope scope = parseState.scope(); + Sign sign = scope.getOrDefault(atom, Sign.PLUS); + String string = scope.get(atom8); + String string1 = scope.get(atom9); + Signed signed = scope.get(atom7); + TypeSuffix typeSuffix = scope.get(atom6); + return createFloat(ops, sign, string, string1, signed, typeSuffix, parseState); + } + ); + Atom atom11 = Atom.of("string_hex_2"); + dictionary.put(atom11, new SimpleHexLiteralParseRule(2)); + Atom atom12 = Atom.of("string_hex_4"); + dictionary.put(atom12, new SimpleHexLiteralParseRule(4)); + Atom atom13 = Atom.of("string_hex_8"); + dictionary.put(atom13, new SimpleHexLiteralParseRule(8)); + Atom atom14 = Atom.of("string_unicode_name"); + dictionary.put(atom14, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom atom15 = Atom.of("string_escape_sequence"); + dictionary.putComplex( + atom15, + Term.alternative( + Term.sequence(StringReaderTerms.character('b'), Term.marker(atom15, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(atom15, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(atom15, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(atom15, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(atom15, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(atom15, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(atom15, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(atom15, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(atom15, "\"")), + Term.sequence(StringReaderTerms.character('x'), dictionary.named(atom11)), + Term.sequence(StringReaderTerms.character('u'), dictionary.named(atom12)), + Term.sequence(StringReaderTerms.character('U'), dictionary.named(atom13)), + Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), dictionary.named(atom14), StringReaderTerms.character('}')) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.getAny(atom15); + if (string != null) { + return string; + } + String string1 = scope.getAny(atom11, atom12, atom13); + if (string1 != null) { + int i = HexFormat.fromHexDigits(string1); + if (!Character.isValidCodePoint(i)) { + parseState.errorCollector() + .store(parseState.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", i))); + return null; + } + return Character.toString(i); + } + String string2 = scope.getOrThrow(atom14); + + int i1; + try { + i1 = Character.codePointOf(string2); + } catch (IllegalArgumentException var12x) { + parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_CHARACTER_NAME); + return null; + } + + return Character.toString(i1); + } + ); + Atom atom16 = Atom.of("string_plain_contents"); + dictionary.put(atom16, PLAIN_STRING_CHUNK); + Atom> atom17 = Atom.of("string_chunks"); + Atom atom18 = Atom.of("string_contents"); + Atom atom19 = Atom.of("single_quoted_string_chunk"); + NamedRule namedRule1 = dictionary.put( + atom19, + Term.alternative( + dictionary.namedWithAlias(atom16, atom18), + Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(atom18, "\"")) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom20 = Atom.of("single_quoted_string_contents"); + dictionary.put(atom20, Term.repeated(namedRule1, atom17), scope -> joinList(scope.getOrThrow(atom17))); + Atom atom21 = Atom.of("double_quoted_string_chunk"); + NamedRule namedRule2 = dictionary.put( + atom21, + Term.alternative( + dictionary.namedWithAlias(atom16, atom18), + Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(atom18, "'")) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom22 = Atom.of("double_quoted_string_contents"); + dictionary.put(atom22, Term.repeated(namedRule2, atom17), scope -> joinList(scope.getOrThrow(atom17))); + Atom atom23 = Atom.of("quoted_string_literal"); + dictionary.put( + atom23, + Term.alternative( + Term.sequence( + StringReaderTerms.character('"'), Term.cut(), Term.optional(dictionary.namedWithAlias(atom22, atom18)), StringReaderTerms.character('"') + ), + Term.sequence(StringReaderTerms.character('\''), Term.optional(dictionary.namedWithAlias(atom20, atom18)), StringReaderTerms.character('\'')) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom24 = Atom.of("unquoted_string"); + dictionary.put(atom24, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + Atom atom25 = Atom.of("literal"); + Atom> atom26 = Atom.of("arguments"); + dictionary.put( + atom26, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom26, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom26) + ); + Atom atom27 = Atom.of("unquoted_string_or_builtin"); + dictionary.putComplex( + atom27, + Term.sequence( + dictionary.named(atom24), + Term.optional(Term.sequence(StringReaderTerms.character('('), dictionary.named(atom26), StringReaderTerms.character(')'))) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.getOrThrow(atom24); + if (!string.isEmpty() && isAllowedToStartUnquotedString(string.charAt(0))) { + List list = scope.get(atom26); + if (list != null) { + SnbtOperations.BuiltinKey builtinKey = new SnbtOperations.BuiltinKey(string, list.size()); + SnbtOperations.BuiltinOperation builtinOperation = SnbtOperations.BUILTIN_OPERATIONS.get(builtinKey); + if (builtinOperation != null) { + return builtinOperation.run(ops, list, parseState); + } + parseState.errorCollector().store(parseState.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, builtinKey.toString())); + return null; + } else if (string.equalsIgnoreCase("true")) { + return object; + } else if (string.equalsIgnoreCase("false")) { + return object1; + } else if (string.equalsIgnoreCase("null")) { + return Objects.requireNonNullElseGet(ops.empty(), () -> { + T nullString = ops.createString("null"); + if ("null".equals(nullString)) { // 确定是 Java 类型的 + return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; + } + return nullString; + }); + } + return ops.createString(string); + } + parseState.errorCollector().store(parseState.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); + return null; + } + ); + Atom atom28 = Atom.of("map_key"); + dictionary.put(atom28, Term.alternative(dictionary.named(atom23), dictionary.named(atom24)), scope -> scope.getAnyOrThrow(atom23, atom24)); + Atom> atom29 = Atom.of("map_entry"); + NamedRule> namedRule3 = dictionary.putComplex( + atom29, Term.sequence(dictionary.named(atom28), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), dictionary.named(atom25)), parseState -> { + Scope scope = parseState.scope(); + String string = scope.getOrThrow(atom28); + if (string.isEmpty()) { + parseState.errorCollector().store(parseState.mark(), ERROR_EMPTY_KEY); + return null; + } + T orThrow = scope.getOrThrow(atom25); + return Map.entry(string, orThrow); + } + ); + Atom>> atom30 = Atom.of("map_entries"); + dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); + Atom atom31 = Atom.of("map_literal"); + dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth + List> list = scope.getOrThrow(atom30); + if (list.isEmpty()) { + return object2; + } else { + Builder builder = ImmutableMap.builderWithExpectedSize(list.size()); + + for (Entry entry : list) { + builder.put(ops.createString(entry.getKey()), entry.getValue()); + } + + return ops.createMap(builder.buildKeepingLast()); + } + }); + Atom> atom32 = Atom.of("list_entries"); + dictionary.put( + atom32, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom32, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom32) + ); + Atom atom33 = Atom.of("array_prefix"); + dictionary.put( + atom33, + Term.alternative( + Term.sequence(StringReaderTerms.character('B'), Term.marker(atom33, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(atom33, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(atom33, ArrayPrefix.INT)) + ), + scope -> scope.getOrThrow(atom33) + ); + Atom> atom34 = Atom.of("int_array_entries"); + dictionary.put(atom34, Term.repeatedWithTrailingSeparator(namedRule, atom34, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom34)); + Atom atom35 = Atom.of("list_literal"); + dictionary.putComplex( + atom35, + Term.sequence( + StringReaderTerms.character('['), + Scope.increaseDepth(), + Term.alternative(Term.sequence(dictionary.named(atom33), StringReaderTerms.character(';'), dictionary.named(atom34)), dictionary.named(atom32)), + Scope.decreaseDepth(), + StringReaderTerms.character(']') + ), + parseState -> { + Scope scope = parseState.scope(); + ArrayPrefix arrayPrefix = scope.get(atom33); + if (arrayPrefix != null) { + List list = scope.getOrThrow(atom34); + return list.isEmpty() ? arrayPrefix.create(ops) : arrayPrefix.create(ops, list, parseState); + } + List list = scope.getOrThrow(atom32); + return list.isEmpty() ? object3 : ops.createList(list.stream()); + } + ); + NamedRule namedRule4 = dictionary.putComplex( + atom25, + Term.alternative( + Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(dictionary.namedWithAlias(atom10, atom25), dictionary.named(atom5))), + Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), dictionary.named(atom23)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), dictionary.namedWithAlias(atom31, atom25)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), dictionary.namedWithAlias(atom35, atom25)), + dictionary.namedWithAlias(atom27, atom25) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.get(atom23); + if (string != null) { + return ops.createString(string); + } + IntegerLiteral integerLiteral = scope.get(atom5); + return integerLiteral != null ? integerLiteral.create(ops, parseState) : scope.getOrThrow(atom25); + } + ); + return new Grammar<>(dictionary, namedRule4); + } + + enum ArrayPrefix { + BYTE(TypeSuffix.BYTE) { + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]); + + @Override + public T create(DynamicOps ops) { + return ops.createByteList(EMPTY_BUFFER); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + ByteList list = new ByteArrayList(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + list.add(number.byteValue()); + } + + return ops.createByteList(ByteBuffer.wrap(list.toByteArray())); + } + }, + INT(TypeSuffix.INT, TypeSuffix.BYTE, TypeSuffix.SHORT) { + @Override + public T create(DynamicOps ops) { + return ops.createIntList(IntStream.empty()); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + IntStream.Builder builder = IntStream.builder(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + builder.add(number.intValue()); + } + + return ops.createIntList(builder.build()); + } + }, + LONG(TypeSuffix.LONG, TypeSuffix.BYTE, TypeSuffix.SHORT, TypeSuffix.INT) { + @Override + public T create(DynamicOps ops) { + return ops.createLongList(LongStream.empty()); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + LongStream.Builder builder = LongStream.builder(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + builder.add(number.longValue()); + } + + return ops.createLongList(builder.build()); + } + }; + + private final TypeSuffix defaultType; + private final Set additionalTypes; + + ArrayPrefix(final TypeSuffix defaultType, final TypeSuffix... additionalTypes) { + this.additionalTypes = Set.of(additionalTypes); + this.defaultType = defaultType; + } + + public boolean isAllowed(TypeSuffix suffix) { + return suffix == this.defaultType || this.additionalTypes.contains(suffix); + } + + public abstract T create(DynamicOps ops); + + @Nullable + public abstract T create(DynamicOps ops, List values, ParseState parseState); + + @Nullable + protected Number buildNumber(IntegerLiteral value, ParseState parseState) { + TypeSuffix typeSuffix = this.computeType(value.suffix); + if (typeSuffix == null) { + parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); + return null; + } + return (Number)value.create(JavaOps.INSTANCE, typeSuffix, parseState); + } + + @Nullable + private TypeSuffix computeType(IntegerSuffix suffix) { + TypeSuffix typeSuffix = suffix.type(); + if (typeSuffix == null) { + return this.defaultType; + } + return !this.isAllowed(typeSuffix) ? null : typeSuffix; + } + } + + enum Base { + BINARY, + DECIMAL, + HEX + } + + record IntegerLiteral(Sign sign, Base base, String digits, IntegerSuffix suffix) { + private SignedPrefix signedOrDefault() { + return Objects.requireNonNullElseGet(this.suffix.signed, () -> switch (this.base) { + case BINARY, HEX -> SignedPrefix.UNSIGNED; + case DECIMAL -> SignedPrefix.SIGNED; + }); + } + + private String cleanupDigits(Sign sign) { + boolean flag = needsUnderscoreRemoval(this.digits); + if (sign != Sign.MINUS && !flag) { + return this.digits; + } + StringBuilder stringBuilder = new StringBuilder(); + sign.append(stringBuilder); + cleanAndAppend(stringBuilder, this.digits, flag); + return stringBuilder.toString(); + } + + @Nullable + public T create(DynamicOps ops, ParseState parseState) { + return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), parseState); + } + + @Nullable + public T create(DynamicOps ops, TypeSuffix typeSuffix, ParseState parseState) { + boolean flag = this.signedOrDefault() == SignedPrefix.SIGNED; + if (!flag && this.sign == Sign.MINUS) { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); + return null; + } + String string = this.cleanupDigits(this.sign); + + int i = switch (this.base) { + case BINARY -> 2; + case DECIMAL -> 10; + case HEX -> 16; + }; + + try { + if (flag) { + return switch (typeSuffix) { + case BYTE -> ops.createByte(Byte.parseByte(string, i)); + case SHORT -> ops.createShort(Short.parseShort(string, i)); + case INT -> ops.createInt(Integer.parseInt(string, i)); + case LONG -> ops.createLong(Long.parseLong(string, i)); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + yield null; + } + }; + } + return switch (typeSuffix) { + case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(string, i)); + case SHORT -> ops.createShort(parseUnsignedShort(string, i)); + case INT -> ops.createInt(Integer.parseUnsignedInt(string, i)); + case LONG -> ops.createLong(Long.parseUnsignedLong(string, i)); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + yield null; + } + }; + } catch (NumberFormatException var8) { + parseState.errorCollector().store(parseState.mark(), createNumberParseError(var8)); + return null; + } + } + } + + record IntegerSuffix(@Nullable SignedPrefix signed, @Nullable TypeSuffix type) { + public static final IntegerSuffix EMPTY = new IntegerSuffix(null, null); + } + + enum Sign { + PLUS, + MINUS; + + public void append(StringBuilder stringBuilder) { + if (this == MINUS) { + stringBuilder.append("-"); + } + } + } + + record Signed(Sign sign, T value) { + } + + enum SignedPrefix { + SIGNED, + UNSIGNED + } + + static class SimpleHexLiteralParseRule extends GreedyPredicateParseRule { + public SimpleHexLiteralParseRule(int minSize) { + super(minSize, minSize, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(minSize))); + } + + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f' -> true; + default -> false; + }; + } + } + + enum TypeSuffix { + FLOAT, + DOUBLE, + BYTE, + SHORT, + INT, + LONG + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java new file mode 100644 index 000000000..9c684725d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -0,0 +1,93 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import net.momirealms.craftengine.core.util.snbt.parse.*; +import net.momirealms.sparrow.nbt.util.UUIDUtil; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class SnbtOperations { + static final DelayedException ERROR_EXPECTED_STRING_UUID = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_string_uuid")) + ); + static final DelayedException ERROR_EXPECTED_NUMBER_OR_BOOLEAN = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_number_or_boolean")) + ); + public static final String BUILTIN_TRUE = "true"; + public static final String BUILTIN_FALSE = "false"; + public static final Map BUILTIN_OPERATIONS = Map.of( + new BuiltinKey("bool", 1), new BuiltinOperation() { + @Override + public T run(DynamicOps ops, List args, ParseState parseState) { + Boolean bool = convert(ops, args.getFirst()); + if (bool == null) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); + return null; + } else { + return ops.createBoolean(bool); + } + } + + @Nullable + private static Boolean convert(DynamicOps ops, T value) { + Optional optional = ops.getBooleanValue(value).result(); + if (optional.isPresent()) { + return optional.get(); + } else { + Optional optional1 = ops.getNumberValue(value).result(); + return optional1.isPresent() ? optional1.get().doubleValue() != 0.0 : null; + } + } + }, new BuiltinKey("uuid", 1), new BuiltinOperation() { + @Override + public T run(DynamicOps ops, List args, ParseState parseState) { + Optional optional = ops.getStringValue(args.getFirst()).result(); + if (optional.isEmpty()) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } else { + UUID uuid; + try { + uuid = UUID.fromString(optional.get()); + } catch (IllegalArgumentException var7) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } + + return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); + } + } + } + ); + public static final SuggestionSupplier BUILTIN_IDS = new SuggestionSupplier<>() { + private final Set keys = Stream.concat( + Stream.of(BUILTIN_FALSE, BUILTIN_TRUE), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) + ) + .collect(Collectors.toSet()); + + @Override + public Stream possibleValues(ParseState parseState) { + return this.keys.stream(); + } + }; + + public record BuiltinKey(String id, int argCount) { + @Override + public @NotNull String toString() { + return this.id + "/" + this.argCount; + } + } + + public interface BuiltinOperation { + @Nullable + T run(DynamicOps ops, List args, ParseState parseState); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java new file mode 100644 index 000000000..6c7badbf9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -0,0 +1,87 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JavaOps; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.snbt.parse.LocalizedMessage; +import net.momirealms.craftengine.core.util.snbt.parse.LocalizedSimpleCommandExceptionType; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.Grammar; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; +import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; +import net.momirealms.sparrow.nbt.codec.NBTOps; + +public class TagParser { + public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType( + new LocalizedMessage("warning.config.type.snbt.parser.trailing") + ); + public static final SimpleCommandExceptionType ERROR_EXPECTED_COMPOUND = new LocalizedSimpleCommandExceptionType( + new LocalizedMessage("warning.config.type.snbt.parser.expected.compound") + ); + public static final char ELEMENT_SEPARATOR = ','; + public static final char NAME_VALUE_SEPARATOR = ':'; + private static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); + private static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); + private final DynamicOps ops; + private final Grammar grammar; + + private TagParser(DynamicOps ops, Grammar grammar) { + this.ops = ops; + this.grammar = grammar; + } + + public DynamicOps ops() { + return this.ops; + } + + public static TagParser create(DynamicOps ops) { + return new TagParser<>(ops, SnbtGrammar.createParser(ops)); + } + + private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag tag) throws CommandSyntaxException { + if (tag instanceof CompoundTag compoundTag) { + return compoundTag; + } + throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); + } + + public static CompoundTag parseCompoundFully(String data) throws CommandSyntaxException { + StringReader stringReader = new StringReader(data); + return parseCompoundAsArgument(stringReader); + } + + public static Object parseObjectFully(String data) throws CommandSyntaxException { + StringReader stringReader = new StringReader(data); + return parseObjectAsArgument(stringReader); + } + + public T parseFully(String text) throws CommandSyntaxException { + return this.parseFully(new StringReader(text)); + } + + public T parseFully(StringReader reader) throws CommandSyntaxException { + T object = this.grammar.parse(reader); + reader.skipWhitespace(); + if (reader.canRead()) { + throw ERROR_TRAILING_DATA.createWithContext(reader); + } + return object; + } + + public T parseAsArgument(StringReader reader) throws CommandSyntaxException { + return this.grammar.parse(reader); + } + + public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException { + Tag tag = NBT_OPS_PARSER.parseAsArgument(reader); + return castToCompoundOrThrow(reader, tag); + } + + public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException { + return JAVA_OPS_PARSER.parseAsArgument(reader); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java new file mode 100644 index 000000000..1601e947b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unused") +public record Atom(String name) { + @Override + public @NotNull String toString() { + return "<" + this.name + ">"; + } + + public static Atom of(String name) { + return new Atom<>(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java new file mode 100644 index 000000000..03c439c82 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java @@ -0,0 +1,253 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.util.MiscUtils; + +import javax.annotation.Nullable; + +@SuppressWarnings("unchecked") +public abstract class CachedParseState implements ParseState { + private PositionCache[] positionCache = new PositionCache[256]; + private final ErrorCollector errorCollector; + private final Scope scope = new Scope(); + private SimpleControl[] controlCache = new SimpleControl[16]; + private int nextControlToReturn; + private final Silent silent = new Silent(); + private final IntList markedNull = new IntArrayList(); + public static final Object JAVA_NULL_VALUE_MARKER = new Object() { + @Override + public String toString() { + return "null"; + } + }; + + protected CachedParseState(ErrorCollector errorCollector) { + this.errorCollector = errorCollector; + } + + @Override + public Scope scope() { + return this.scope; + } + + @Override + public ErrorCollector errorCollector() { + return this.errorCollector; + } + + @Nullable + @Override + public T parse(NamedRule rule) { + int i = this.mark(); + PositionCache cacheForPosition = this.getCacheForPosition(i); + int i1 = cacheForPosition.findKeyIndex(rule.name()); + if (i1 != -1) { + CacheEntry value = cacheForPosition.getValue(i1); + if (value != null) { + if (value == CachedParseState.CacheEntry.NEGATIVE) { + return null; + } + this.restore(value.markAfterParse); + return value.value; + } + } else { + i1 = cacheForPosition.allocateNewEntry(rule.name()); + } + + T object = rule.value().parse(this); + CacheEntry cacheEntry; + if (object == null) { + cacheEntry = (CacheEntry) CacheEntry.NEGATIVE; + } else { + cacheEntry = new CacheEntry<>(object, this.mark()); + } + + cacheForPosition.setValue(i1, cacheEntry); + return object; + } + + private PositionCache getCacheForPosition(int position) { + int i = this.positionCache.length; + if (position >= i) { + int i1 = MiscUtils.growByHalf(i, position + 1); + PositionCache[] positionCaches = new PositionCache[i1]; + System.arraycopy(this.positionCache, 0, positionCaches, 0, i); + this.positionCache = positionCaches; + } + + PositionCache positionCache = this.positionCache[position]; + if (positionCache == null) { + positionCache = new PositionCache(); + this.positionCache[position] = positionCache; + } + + return positionCache; + } + + @Override + public Control acquireControl() { + int i = this.controlCache.length; + if (this.nextControlToReturn >= i) { + int i1 = MiscUtils.growByHalf(i, this.nextControlToReturn + 1); + SimpleControl[] simpleControls = new SimpleControl[i1]; + System.arraycopy(this.controlCache, 0, simpleControls, 0, i); + this.controlCache = simpleControls; + } + + int i1 = this.nextControlToReturn++; + SimpleControl simpleControl = this.controlCache[i1]; + if (simpleControl == null) { + simpleControl = new SimpleControl(); + this.controlCache[i1] = simpleControl; + } else { + simpleControl.reset(); + } + + return simpleControl; + } + + @Override + public void releaseControl() { + this.nextControlToReturn--; + } + + @Override + public ParseState silent() { + return this.silent; + } + + @Override + public void markNull(int mark) { + this.markedNull.add(mark); + } + + @Override + public boolean isNull(int mark) { + return this.markedNull.contains(mark); + } + + record CacheEntry(@Nullable T value, int markAfterParse) { + public static final CacheEntry NEGATIVE = new CacheEntry<>(null, -1); + } + + static class PositionCache { + public static final int ENTRY_STRIDE = 2; + private static final int NOT_FOUND = -1; + private Object[] atomCache = new Object[16]; + private int nextKey; + + public int findKeyIndex(Atom atom) { + for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) { + if (this.atomCache[i] == atom) { + return i; + } + } + + return NOT_FOUND; + } + + public int allocateNewEntry(Atom entry) { + int i = this.nextKey; + this.nextKey += 2; + int i1 = i + 1; + int i2 = this.atomCache.length; + if (i1 >= i2) { + int i3 = MiscUtils.growByHalf(i2, i1 + 1); + Object[] objects = new Object[i3]; + System.arraycopy(this.atomCache, 0, objects, 0, i2); + this.atomCache = objects; + } + + this.atomCache[i] = entry; + return i; + } + + @Nullable + public CacheEntry getValue(int index) { + return (CacheEntry)this.atomCache[index + 1]; + } + + public void setValue(int index, CacheEntry value) { + this.atomCache[index + 1] = value; + } + } + + class Silent implements ParseState { + private final ErrorCollector silentCollector = new ErrorCollector.Nop<>(); + + @Override + public ErrorCollector errorCollector() { + return this.silentCollector; + } + + @Override + public Scope scope() { + return CachedParseState.this.scope(); + } + + @Nullable + @Override + public T parse(NamedRule rule) { + return CachedParseState.this.parse(rule); + } + + @Override + public S input() { + return CachedParseState.this.input(); + } + + @Override + public int mark() { + return CachedParseState.this.mark(); + } + + @Override + public void restore(int cursor) { + CachedParseState.this.restore(cursor); + } + + @Override + public Control acquireControl() { + return CachedParseState.this.acquireControl(); + } + + @Override + public void releaseControl() { + CachedParseState.this.releaseControl(); + } + + @Override + public ParseState silent() { + return this; + } + + @Override + public void markNull(int mark) { + CachedParseState.this.markNull(mark); + } + + @Override + public boolean isNull(int mark) { + return CachedParseState.this.isNull(mark); + } + } + + static class SimpleControl implements Control { + private boolean hasCut; + + @Override + public void cut() { + this.hasCut = true; + } + + @Override + public boolean hasCut() { + return this.hasCut; + } + + public void reset() { + this.hasCut = false; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java new file mode 100644 index 000000000..b17ce13ec --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public interface Control { + Control UNBOUND = new Control() { + @Override + public void cut() { + } + + @Override + public boolean hasCut() { + return false; + } + }; + + void cut(); + + boolean hasCut(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java new file mode 100644 index 000000000..ee79b450c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java @@ -0,0 +1,19 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms; + +@FunctionalInterface +public interface DelayedException { + T create(String message, int cursor); + + static DelayedException create(SimpleCommandExceptionType exception) { + return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor)); + } + + static DelayedException create(DynamicCommandExceptionType exception, String argument) { + return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor), argument); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java new file mode 100644 index 000000000..20d6c4bb2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java @@ -0,0 +1,92 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +@SuppressWarnings("unchecked") +public class Dictionary { + private final Map, Entry> terms = new IdentityHashMap<>(); + + public NamedRule put(Atom name, Rule rule) { + Entry entry = (Entry)this.terms.computeIfAbsent(name, Entry::new); + if (entry.value != null) { + throw new IllegalArgumentException("Trying to override rule: " + name); + } else { + entry.value = rule; + return entry; + } + } + + public NamedRule putComplex(Atom name, Term term, Rule.RuleAction ruleAction) { + return this.put(name, Rule.fromTerm(term, ruleAction)); + } + + public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction ruleAction) { + return this.put(name, Rule.fromTerm(term, ruleAction)); + } + + public void checkAllBound() { + List> list = this.terms.entrySet().stream().filter(entry -> entry.getValue() == null).map(Map.Entry::getKey).toList(); + if (!list.isEmpty()) { + throw new IllegalStateException("Unbound names: " + list); + } + } + + public NamedRule forward(Atom name) { + return this.getOrCreateEntry(name); + } + + private Entry getOrCreateEntry(Atom name) { + return (Entry)this.terms.computeIfAbsent(name, Entry::new); + } + + public Term named(Atom name) { + return new Reference<>(this.getOrCreateEntry(name), name); + } + + public Term namedWithAlias(Atom name, Atom alias) { + return new Reference<>(this.getOrCreateEntry(name), alias); + } + + static class Entry implements NamedRule, Supplier { + private final Atom name; + @Nullable + Rule value; + + private Entry(Atom name) { + this.name = name; + } + + @Override + public Atom name() { + return this.name; + } + + @Override + public Rule value() { + return Objects.requireNonNull(this.value, this); + } + + @Override + public String get() { + return "Unbound rule " + this.name; + } + } + + record Reference(Entry ruleToParse, Atom nameToStore) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + T object = parseState.parse(this.ruleToParse); + if (object == null) { + return false; + } else { + scope.put(this.nameToStore, object); + return true; + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java new file mode 100644 index 000000000..ef0aac7e3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import net.momirealms.craftengine.core.util.MiscUtils; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +public interface ErrorCollector { + void store(int cursor, SuggestionSupplier suggestions, Object reason); + + default void store(int cursor, Object reason) { + this.store(cursor, SuggestionSupplier.empty(), reason); + } + + void finish(int cursor); + + class LongestOnly implements ErrorCollector { + private MutableErrorEntry[] entries = new MutableErrorEntry[16]; + private int nextErrorEntry; + private int lastCursor = -1; + + private void discardErrorsFromShorterParse(int cursor) { + if (cursor > this.lastCursor) { + this.lastCursor = cursor; + this.nextErrorEntry = 0; + } + } + + @Override + public void finish(int cursor) { + this.discardErrorsFromShorterParse(cursor); + } + + @Override + public void store(int cursor, SuggestionSupplier suggestions, Object reason) { + this.discardErrorsFromShorterParse(cursor); + if (cursor == this.lastCursor) { + this.addErrorEntry(suggestions, reason); + } + } + + private void addErrorEntry(SuggestionSupplier suggestions, Object reason) { + int i = this.entries.length; + if (this.nextErrorEntry >= i) { + int i1 = MiscUtils.growByHalf(i, this.nextErrorEntry + 1); + MutableErrorEntry[] mutableErrorEntrys = new MutableErrorEntry[i1]; + System.arraycopy(this.entries, 0, mutableErrorEntrys, 0, i); + this.entries = mutableErrorEntrys; + } + + int i1 = this.nextErrorEntry++; + MutableErrorEntry mutableErrorEntry = this.entries[i1]; + if (mutableErrorEntry == null) { + mutableErrorEntry = new MutableErrorEntry<>(); + this.entries[i1] = mutableErrorEntry; + } + + mutableErrorEntry.suggestions = suggestions; + mutableErrorEntry.reason = reason; + } + + public List> entries() { + int i = this.nextErrorEntry; + if (i == 0) { + return List.of(); + } else { + List> list = new ArrayList<>(i); + + for (int i1 = 0; i1 < i; i1++) { + MutableErrorEntry mutableErrorEntry = this.entries[i1]; + list.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); + } + + return list; + } + } + + public int cursor() { + return this.lastCursor; + } + + static class MutableErrorEntry { + SuggestionSupplier suggestions = SuggestionSupplier.empty(); + Object reason = "empty"; + } + } + + class Nop implements ErrorCollector { + @Override + public void store(int cursor, SuggestionSupplier suggestions, Object reason) { + } + + @Override + public void finish(int cursor) { + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java new file mode 100644 index 000000000..dff487beb --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java @@ -0,0 +1,4 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public record ErrorEntry(int cursor, SuggestionSupplier suggestions, Object reason) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java new file mode 100644 index 000000000..cb4912898 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java @@ -0,0 +1,84 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.AdventureHelper; + +import java.util.Optional; +import java.util.function.Supplier; + +public class LocalizedCommandSyntaxException extends CommandSyntaxException { + public static final int CONTEXT_AMOUNT = 10; + public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error"; + public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here"; + private final Message message; + private final String input; + private final int cursor; + + public LocalizedCommandSyntaxException(CommandExceptionType type, Message message) { + super(type, message); + this.message = message; + this.input = null; + this.cursor = -1; + } + + public LocalizedCommandSyntaxException(CommandExceptionType type, Message message, String input, int cursor) { + super(type, message, input, cursor); + this.message = message; + this.input = input; + this.cursor = cursor; + } + + @Override + public String getMessage() { + String message = this.message.getString(); + final String context = getContext(); + if (context == null) { + return message; + } + return generateLocalizedMessage( + PARSE_ERROR_NODE, + () -> message + " at position " + this.cursor + ": " + context, + message, String.valueOf(this.cursor), context + ); + } + + @Override + public String getContext() { + if (this.input == null || this.cursor < 0) { + return null; + } + final StringBuilder builder = new StringBuilder(); + final int cursor = Math.min(this.input.length(), this.cursor); + + if (cursor > CONTEXT_AMOUNT) { + builder.append("..."); + } + + builder.append(this.input, Math.max(0, cursor - CONTEXT_AMOUNT), cursor); + builder.append(generateLocalizedMessage(HERE_NODE, () -> "<--[HERE]")); + + return builder.toString(); + } + + + private String generateLocalizedMessage(String node, Supplier fallback, String... arguments) { + try { + String rawMessage = Optional.ofNullable(TranslationManager.instance() + .miniMessageTranslation(node)).orElse(fallback.get()); + String cleanMessage = AdventureHelper.miniMessage() + .stripTags(rawMessage); + for (int i = 0; i < arguments.length; i++) { + cleanMessage = cleanMessage.replace( + "", + arguments[i] != null ? arguments[i] : "null" + ); + } + return cleanMessage; + } catch (Exception e) { + return fallback.get(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java new file mode 100644 index 000000000..ee80a3bee --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +import java.util.function.Function; + +public class LocalizedDynamicCommandExceptionType extends DynamicCommandExceptionType { + private final Function function; + + public LocalizedDynamicCommandExceptionType(Function function) { + super(function); + this.function = function; + } + + @Override + public CommandSyntaxException create(final Object arg) { + return new LocalizedCommandSyntaxException(this, function.apply(arg)); + } + + @Override + public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object arg) { + return new LocalizedCommandSyntaxException(this, function.apply(arg), reader.getString(), reader.getCursor()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java new file mode 100644 index 000000000..c8da1cab7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.Message; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.AdventureHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Optional; + +public class LocalizedMessage implements Message { + private final String node; + private final String[] arguments; + + public LocalizedMessage( + @NotNull String node, + @Nullable String... arguments + ) { + this.node = node; + this.arguments = arguments != null + ? Arrays.copyOf(arguments, arguments.length) + : new String[0]; + } + + @Override + public String getString() { + return generateLocalizedMessage(); + } + + private String generateLocalizedMessage() { + try { + String rawMessage = Optional.ofNullable(TranslationManager.instance() + .miniMessageTranslation(this.node)).orElse(this.node); + String cleanMessage = AdventureHelper.miniMessage() + .stripTags(rawMessage); + for (int i = 0; i < arguments.length; i++) { + cleanMessage = cleanMessage.replace( + "", + arguments[i] != null ? arguments[i] : "null" + ); + } + return cleanMessage; + } catch (Exception e) { + return String.format( + "Failed to translate. Node: %s, Arguments: %s. Cause: %s", + node, + Arrays.toString(arguments), + e.getMessage() + ); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java new file mode 100644 index 000000000..b046fab0c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +public class LocalizedSimpleCommandExceptionType extends SimpleCommandExceptionType { + private final Message message; + + public LocalizedSimpleCommandExceptionType(Message message) { + super(message); + this.message = message; + } + + @Override + public CommandSyntaxException create() { + return new LocalizedCommandSyntaxException(this, message); + } + + @Override + public CommandSyntaxException createWithContext(final ImmutableStringReader reader) { + return new LocalizedCommandSyntaxException(this, message, reader.getString(), reader.getCursor()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java new file mode 100644 index 000000000..d4f634e1d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public interface NamedRule { + Atom name(); + + Rule value(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java new file mode 100644 index 000000000..5a631476b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; +import java.util.Optional; + +public interface ParseState { + Scope scope(); + + ErrorCollector errorCollector(); + + default Optional parseTopRule(NamedRule rule) { + T object = this.parse(rule); + if (object != null) { + this.errorCollector().finish(this.mark()); + } + + if (!this.scope().hasOnlySingleFrame()) { + throw new IllegalStateException("Malformed scope: " + this.scope()); + } else { + return Optional.ofNullable(object); + } + } + + @Nullable + T parse(NamedRule rule); + + S input(); + + int mark(); + + void restore(int cursor); + + Control acquireControl(); + + void releaseControl(); + + ParseState silent(); + + void markNull(int mark); + + boolean isNull(int mark); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java new file mode 100644 index 000000000..6a5728ec4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; + +public interface Rule { + @Nullable + T parse(ParseState parseState); + + static Rule fromTerm(Term child, RuleAction action) { + return new WrappedTerm<>(action, child); + } + + static Rule fromTerm(Term child, SimpleRuleAction action) { + return new WrappedTerm<>(action, child); + } + + @FunctionalInterface + interface RuleAction { + @Nullable + T run(ParseState parseState); + } + + @FunctionalInterface + interface SimpleRuleAction extends RuleAction { + T run(Scope scope); + + @Override + default T run(ParseState parseState) { + return this.run(parseState.scope()); + } + } + + record WrappedTerm(RuleAction action, Term child) implements Rule { + @Nullable + @Override + public T parse(ParseState parseState) { + Scope scope = parseState.scope(); + scope.pushFrame(); + + T var3; + try { + if (!this.child.parse(parseState, scope, Control.UNBOUND)) { + return null; + } + + var3 = this.action.run(parseState); + } finally { + scope.popFrame(); + } + + return var3; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java new file mode 100644 index 000000000..81ad7ecfd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java @@ -0,0 +1,316 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.google.common.annotations.VisibleForTesting; +import net.momirealms.craftengine.core.util.MiscUtils; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("unchecked") +public final class Scope { + private static final int NOT_FOUND = -1; + private static final Object FRAME_START_MARKER = new Object() { + @Override + public String toString() { + return "frame"; + } + }; + private static final int ENTRY_STRIDE = 2; + private Object[] stack = new Object[128]; + private int topEntryKeyIndex = 0; + private int topMarkerKeyIndex = 0; + private int depth; + + public Scope() { + this.stack[0] = FRAME_START_MARKER; + this.stack[1] = null; + } + + private int valueIndex(Atom name) { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + + assert object instanceof Atom; + + if (object == name) { + return i + 1; + } + } + + return NOT_FOUND; + } + + public int valueIndexForAny(Atom... names) { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + + assert object instanceof Atom; + + for (Atom atom : names) { + if (atom == object) { + return i + 1; + } + } + } + + return NOT_FOUND; + } + + private void ensureCapacity(int requiredCapacitty) { + int i = this.stack.length; + int i1 = this.topEntryKeyIndex + 1; + int i2 = i1 + requiredCapacitty * 2; + if (i2 >= i) { + int i3 = MiscUtils.growByHalf(i, i2 + 1); + Object[] objects = new Object[i3]; + System.arraycopy(this.stack, 0, objects, 0, i); + this.stack = objects; + } + + assert this.validateStructure(); + } + + private void setupNewFrame() { + this.topEntryKeyIndex += ENTRY_STRIDE; + this.stack[this.topEntryKeyIndex] = FRAME_START_MARKER; + this.stack[this.topEntryKeyIndex + 1] = this.topMarkerKeyIndex; + this.topMarkerKeyIndex = this.topEntryKeyIndex; + } + + public void pushFrame() { + this.ensureCapacity(1); + this.setupNewFrame(); + + assert this.validateStructure(); + } + + private int getPreviousMarkerIndex(int markerIndex) { + return (Integer)this.stack[markerIndex + 1]; + } + + public void popFrame() { + assert this.topMarkerKeyIndex != 0; + + this.topEntryKeyIndex = this.topMarkerKeyIndex - ENTRY_STRIDE; + this.topMarkerKeyIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); + + assert this.validateStructure(); + } + + public void splitFrame() { + int i = this.topMarkerKeyIndex; + int i1 = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; + this.ensureCapacity(i1 + 1); + this.setupNewFrame(); + int i2 = i + ENTRY_STRIDE; + int i3 = this.topEntryKeyIndex; + + for (int i4 = 0; i4 < i1; i4++) { + i3 += ENTRY_STRIDE; + Object object = this.stack[i2]; + + assert object != null; + + this.stack[i3] = object; + this.stack[i3 + 1] = null; + i2 += ENTRY_STRIDE; + } + + this.topEntryKeyIndex = i3; + + assert this.validateStructure(); + } + + public void clearFrameValues() { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + assert this.stack[i] instanceof Atom; + + this.stack[i + 1] = null; + } + + assert this.validateStructure(); + } + + public void mergeFrame() { + int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); + int i = previousMarkerIndex; + int i1 = this.topMarkerKeyIndex; + + while (i1 < this.topEntryKeyIndex) { + i += ENTRY_STRIDE; + i1 += ENTRY_STRIDE; + Object object = this.stack[i1]; + + assert object instanceof Atom; + + Object object1 = this.stack[i1 + 1]; + Object object2 = this.stack[i]; + if (object2 != object) { + this.stack[i] = object; + this.stack[i + 1] = object1; + } else if (object1 != null) { + this.stack[i + 1] = object1; + } + } + + this.topEntryKeyIndex = i; + this.topMarkerKeyIndex = previousMarkerIndex; + + assert this.validateStructure(); + } + + public void put(Atom atom, @Nullable T value) { + int i = this.valueIndex(atom); + if (i != NOT_FOUND) { + this.stack[i] = value; + } else { + this.ensureCapacity(1); + this.topEntryKeyIndex += ENTRY_STRIDE; + this.stack[this.topEntryKeyIndex] = atom; + this.stack[this.topEntryKeyIndex + 1] = value; + } + + assert this.validateStructure(); + } + + @Nullable + public T get(Atom atom) { + int i = this.valueIndex(atom); + return (T)(i != NOT_FOUND ? this.stack[i] : null); + } + + public T getOrThrow(Atom atom) { + int i = this.valueIndex(atom); + if (i == NOT_FOUND) { + throw new IllegalArgumentException("No value for atom " + atom); + } else { + return (T)this.stack[i]; + } + } + + public T getOrDefault(Atom atom, T defaultValue) { + int i = this.valueIndex(atom); + return (T)(i != NOT_FOUND ? this.stack[i] : defaultValue); + } + + @Nullable + @SafeVarargs + public final T getAny(Atom... atoms) { + int i = this.valueIndexForAny(atoms); + return (T)(i != NOT_FOUND ? this.stack[i] : null); + } + + @SafeVarargs + public final T getAnyOrThrow(Atom... atoms) { + int i = this.valueIndexForAny(atoms); + if (i == NOT_FOUND) { + throw new IllegalArgumentException("No value for atoms " + Arrays.toString(atoms)); + } else { + return (T)this.stack[i]; + } + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + boolean flag = true; + + for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { + Object object = this.stack[i]; + Object object1 = this.stack[i + 1]; + if (object == FRAME_START_MARKER) { + stringBuilder.append('|'); + flag = true; + } else { + if (!flag) { + stringBuilder.append(','); + } + + flag = false; + stringBuilder.append(object).append(':').append(object1); + } + } + + return stringBuilder.toString(); + } + + @VisibleForTesting + public Map, ?> lastFrame() { + HashMap, Object> map = new HashMap<>(); + + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + Object object1 = this.stack[i + 1]; + map.put((Atom)object, object1); + } + + return map; + } + + public boolean hasOnlySingleFrame() { + for (int i = this.topEntryKeyIndex; i > 0; i--) { + if (this.stack[i] == FRAME_START_MARKER) { + return false; + } + } + + if (this.stack[0] != FRAME_START_MARKER) { + throw new IllegalStateException("Corrupted stack"); + } else { + return true; + } + } + + private boolean validateStructure() { + assert this.topMarkerKeyIndex >= 0; + + assert this.topEntryKeyIndex >= this.topMarkerKeyIndex; + + for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { + Object object = this.stack[i]; + if (object != FRAME_START_MARKER && !(object instanceof Atom)) { + return false; + } + } + + for (int ix = this.topMarkerKeyIndex; ix != 0; ix = this.getPreviousMarkerIndex(ix)) { + Object object = this.stack[ix]; + if (object != FRAME_START_MARKER) { + return false; + } + } + + return true; + } + + @SuppressWarnings({"unchecked","rawtypes"}) + public static Term increaseDepth() { + class IncreasingDepthTerm implements Term { + public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm(); + @Override + public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + if (++scope.depth > 512) { + parseState.errorCollector().store(parseState.mark(), new IllegalStateException("Too deep")); + return false; + } + return true; + } + } + return (Term) IncreasingDepthTerm.INSTANCE; + } + + @SuppressWarnings({"unchecked","rawtypes"}) + public static Term decreaseDepth() { + class DecreasingDepthTerm implements Term { + public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm(); + @Override + public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + scope.depth--; + return true; + } + } + return (Term) DecreasingDepthTerm.INSTANCE; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java new file mode 100644 index 000000000..ed88ccd42 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import java.util.stream.Stream; + +public interface SuggestionSupplier { + Stream possibleValues(ParseState parseState); + + static SuggestionSupplier empty() { + return parseState -> Stream.empty(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java new file mode 100644 index 000000000..ceab78ef0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java @@ -0,0 +1,237 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import java.util.ArrayList; +import java.util.List; + +public interface Term { + boolean parse(ParseState parseState, Scope scope, Control control); + + static Term marker(Atom name, T value) { + return new Marker<>(name, value); + } + + @SafeVarargs + static Term sequence(Term... elements) { + return new Sequence<>(elements); + } + + @SafeVarargs + static Term alternative(Term... elements) { + return new Alternative<>(elements); + } + + static Term optional(Term term) { + return new Maybe<>(term); + } + + static Term repeated(NamedRule element, Atom> listName) { + return repeated(element, listName, 0); + } + + static Term repeated(NamedRule element, Atom> listName, int minRepetitions) { + return new Repeated<>(element, listName, minRepetitions); + } + + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator) { + return repeatedWithTrailingSeparator(element, listName, seperator, 0); + } + + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator, int minRepetitions) { + return new RepeatedWithSeparator<>(element, listName, seperator, minRepetitions, true); + } + + static Term positiveLookahead(Term term) { + return new LookAhead<>(term, true); + } + + static Term cut() { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + control.cut(); + return true; + } + + @Override + public String toString() { + return "↑"; + } + }; + } + + static Term empty() { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + return true; + } + + @Override + public String toString() { + return "ε"; + } + }; + } + + static Term fail(final Object reason) { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + parseState.errorCollector().store(parseState.mark(), reason); + return false; + } + + @Override + public String toString() { + return "fail"; + } + }; + } + + record Alternative(Term[] elements) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + Control control1 = parseState.acquireControl(); + + try { + int i = parseState.mark(); + scope.splitFrame(); + + for (Term term : this.elements) { + if (term.parse(parseState, scope, control1)) { + scope.mergeFrame(); + return true; + } + + scope.clearFrameValues(); + parseState.restore(i); + if (control1.hasCut()) { + break; + } + } + + scope.popFrame(); + return false; + } finally { + parseState.releaseControl(); + } + } + } + + record LookAhead(Term term, boolean positive) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + boolean flag = this.term.parse(parseState.silent(), scope, control); + parseState.restore(i); + return this.positive == flag; + } + } + + record Marker(Atom name, T value) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + scope.put(this.name, this.value); + return true; + } + } + + record Maybe(Term term) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + if (!this.term.parse(parseState, scope, control)) { + parseState.restore(i); + } + + return true; + } + } + + record Repeated(NamedRule element, Atom> listName, int minRepetitions) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + List list = new ArrayList<>(this.minRepetitions); + + while (true) { + int i1 = parseState.mark(); + T object = parseState.parse(this.element); + if (object == null) { + parseState.restore(i1); + if (list.size() < this.minRepetitions) { + parseState.restore(i); + return false; + } else { + scope.put(this.listName, list); + return true; + } + } + + list.add(object); + } + } + } + + record RepeatedWithSeparator( + NamedRule element, Atom> listName, Term separator, int minRepetitions, boolean allowTrailingSeparator + ) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + List list = new ArrayList<>(this.minRepetitions); + boolean flag = true; + + while (true) { + int i1 = parseState.mark(); + if (!flag && !this.separator.parse(parseState, scope, control)) { + parseState.restore(i1); + break; + } + + int i2 = parseState.mark(); + T object = parseState.parse(this.element); + if (object == null) { + if (flag) { + parseState.restore(i2); + } else { + if (!this.allowTrailingSeparator) { + parseState.restore(i); + return false; + } + + parseState.restore(i2); + } + break; + } + + list.add(object); + flag = false; + } + + if (list.size() < this.minRepetitions) { + parseState.restore(i); + return false; + } else { + scope.put(this.listName, list); + return true; + } + } + } + + record Sequence(Term[] elements) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + + for (Term term : this.elements) { + if (!term.parse(parseState, scope, control)) { + parseState.restore(i); + return false; + } + } + + return true; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java new file mode 100644 index 000000000..2f13a7448 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.*; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public record Grammar(Dictionary rules, NamedRule top) { + public Grammar { + rules.checkAllBound(); + } + + public Optional parse(ParseState parseState) { + return parseState.parseTopRule(this.top); + } + + public T parse(StringReader reader) throws CommandSyntaxException { + ErrorCollector.LongestOnly longestOnly = new ErrorCollector.LongestOnly<>(); + StringReaderParserState stringReaderParserState = new StringReaderParserState(longestOnly, reader); + Optional optional = this.parse(stringReaderParserState); + if (optional.isPresent()) { + T result = optional.get(); + if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) { + result = null; + } + return result; + } else { + List> list = longestOnly.entries(); + List list1 = list.stream().mapMulti((errorEntry, consumer) -> { + if (errorEntry.reason() instanceof DelayedException delayedException) { + consumer.accept(delayedException.create(reader.getString(), errorEntry.cursor())); + } else if (errorEntry.reason() instanceof Exception exception1) { + consumer.accept(exception1); + } + }).toList(); + + for (Exception exception : list1) { + if (exception instanceof CommandSyntaxException commandSyntaxException) { + throw commandSyntaxException; + } + } + + if (list1.size() == 1 && list1.getFirst() instanceof RuntimeException runtimeException) { + throw runtimeException; + } else { + throw new IllegalStateException("Failed to parse: " + list.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java new file mode 100644 index 000000000..9d5e07b91 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class GreedyPatternParseRule implements Rule { + private final Pattern pattern; + private final DelayedException error; + + public GreedyPatternParseRule(Pattern pattern, DelayedException error) { + this.pattern = pattern; + this.error = error; + } + + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + String string = stringReader.getString(); + Matcher matcher = this.pattern.matcher(string).region(stringReader.getCursor(), string.length()); + if (!matcher.lookingAt()) { + parseState.errorCollector().store(parseState.mark(), this.error); + return null; + } else { + stringReader.setCursor(matcher.end()); + return matcher.group(0); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java new file mode 100644 index 000000000..d3c8d8870 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public abstract class GreedyPredicateParseRule implements Rule { + private final int minSize; + private final int maxSize; + private final DelayedException error; + + public GreedyPredicateParseRule(int minSize, DelayedException error) { + this(minSize, Integer.MAX_VALUE, error); + } + + public GreedyPredicateParseRule(int minSize, int maxSize, DelayedException error) { + this.minSize = minSize; + this.maxSize = maxSize; + this.error = error; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + String string = stringReader.getString(); + int cursor = stringReader.getCursor(); + int i = cursor; + + while (i < string.length() && this.isAccepted(string.charAt(i)) && i - cursor < this.maxSize) { + i++; + } + + int i1 = i - cursor; + if (i1 < this.minSize) { + parseState.errorCollector().store(parseState.mark(), this.error); + return null; + } else { + stringReader.setCursor(i); + return string.substring(cursor, i); + } + } + + protected abstract boolean isAccepted(char c); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java new file mode 100644 index 000000000..71a919544 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java @@ -0,0 +1,47 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public abstract class NumberRunParseRule implements Rule { + private final DelayedException noValueError; + private final DelayedException underscoreNotAllowedError; + + public NumberRunParseRule(DelayedException noValueError, DelayedException underscoreNotAllowedError) { + this.noValueError = noValueError; + this.underscoreNotAllowedError = underscoreNotAllowedError; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + stringReader.skipWhitespace(); + String string = stringReader.getString(); + int cursor = stringReader.getCursor(); + int i = cursor; + + while (i < string.length() && this.isAccepted(string.charAt(i))) { + i++; + } + + int i1 = i - cursor; + if (i1 == 0) { + parseState.errorCollector().store(parseState.mark(), this.noValueError); + return null; + } else if (string.charAt(cursor) != '_' && string.charAt(i - 1) != '_') { + stringReader.setCursor(i); + return string.substring(cursor, i); + } else { + parseState.errorCollector().store(parseState.mark(), this.underscoreNotAllowedError); + return null; + } + } + + protected abstract boolean isAccepted(char c); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java new file mode 100644 index 000000000..9bf0a87b9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java @@ -0,0 +1,29 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import net.momirealms.craftengine.core.util.snbt.parse.CachedParseState; +import net.momirealms.craftengine.core.util.snbt.parse.ErrorCollector; + +public class StringReaderParserState extends CachedParseState { + private final StringReader input; + + public StringReaderParserState(ErrorCollector errorCollector, StringReader input) { + super(errorCollector); + this.input = input; + } + + @Override + public StringReader input() { + return this.input; + } + + @Override + public int mark() { + return this.input.getCursor(); + } + + @Override + public void restore(int cursor) { + this.input.setCursor(cursor); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java new file mode 100644 index 000000000..12cb7def3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.snbt.parse.*; + +import java.util.stream.Collectors; + +public interface StringReaderTerms { + DynamicCommandExceptionType LITERAL_INCORRECT = new LocalizedDynamicCommandExceptionType( + expected -> new LocalizedMessage("warning.config.type.snbt.parser.incorrect", String.valueOf(expected)) + ); + + static Term character(final char value) { + return new TerminalCharacters(CharList.of(value)) { + @Override + protected boolean isAccepted(char c) { + return value == c; + } + }; + } + + static Term characters(final char value1, final char value2) { + return new TerminalCharacters(CharList.of(value1, value2)) { + @Override + protected boolean isAccepted(char c) { + return c == value1 || c == value2; + } + }; + } + + static StringReader createReader(String input, int cursor) { + StringReader stringReader = new StringReader(input); + stringReader.setCursor(cursor); + return stringReader; + } + + abstract class TerminalCharacters implements Term { + private final DelayedException error; + private final SuggestionSupplier suggestions; + + public TerminalCharacters(CharList characters) { + String string = characters.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); + this.error = DelayedException.create(LITERAL_INCORRECT, string); + this.suggestions = parseState -> characters.intStream().mapToObj(Character::toString); + } + + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + parseState.input().skipWhitespace(); + int i = parseState.mark(); + if (parseState.input().canRead() && this.isAccepted(parseState.input().read())) { + return true; + } else { + parseState.errorCollector().store(i, this.suggestions, this.error); + return false; + } + } + + protected abstract boolean isAccepted(char c); + } + +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java new file mode 100644 index 000000000..1909633c8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public class UnquotedStringParseRule implements Rule { + private final int minSize; + private final DelayedException error; + + public UnquotedStringParseRule(int minSize, DelayedException error) { + this.minSize = minSize; + this.error = error; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + parseState.input().skipWhitespace(); + int i = parseState.mark(); + String unquotedString = parseState.input().readUnquotedString(); + if (unquotedString.length() < this.minSize) { + parseState.errorCollector().store(i, this.error); + return null; + } else { + return unquotedString; + } + } +} diff --git a/gradle.properties b/gradle.properties index 46db05f12..a82325cc5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings project_version=0.0.65.15 config_version=60 -lang_version=41 +lang_version=42 project_group=net.momirealms latest_supported_version=1.21.10 From 563dc931e97c6703b9607d2197cea8f2e26bf500 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:07:02 +0800 Subject: [PATCH 05/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=BB=E9=80=A0?= =?UTF-8?q?=E9=85=8D=E6=96=B9=E5=90=8E=E5=A4=84=E7=90=86=E5=99=A8=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=E4=BF=9D=E7=95=99custom=5Fdata=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=BC=9A=E5=86=99=E5=85=A5=E9=94=99=E8=AF=AF=E7=9A=84=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=89=A9=E5=93=81ID=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/item/recipe/CustomSmithingTransformRecipe.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 190ee8004..cad232493 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -19,10 +19,7 @@ import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -158,6 +155,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip private T createSmithingResult(Item base, T result) { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; + Optional customId = finalResult.customId(); // 修复在保留custom_data组件的时候意外保留前物品的id if (this.mergeComponents) { finalResult = base.mergeCopy(wrappedResult); } @@ -166,6 +164,9 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip processor.accept(base, wrappedResult, finalResult); } } + if (customId.isPresent()) { + finalResult.customId(customId.get()); + } return finalResult.getItem(); } From 52a83a18d611e3868f6ec62c5d723dba0f832321 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:33:32 +0800 Subject: [PATCH 06/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index ad96f56e9..cf69f1474 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -69,7 +69,7 @@ public class SnbtGrammar { new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_decimal_numeral")) ); private static final DelayedException ERROR_EXPECTED_HEX_NUMERAL = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("Expected a hexadecimal number")) + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_numeral")) ); private static final DelayedException ERROR_EMPTY_KEY = DelayedException.create( new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.empty_key")) From bdc247dc469698338938d19248c6a45e984e59c0 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:37:21 +0800 Subject: [PATCH 07/46] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index cf69f1474..4415e2655 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -559,7 +559,7 @@ public class SnbtGrammar { Atom>> atom30 = Atom.of("map_entries"); dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); Atom atom31 = Atom.of("map_literal"); - dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth + dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { List> list = scope.getOrThrow(atom30); if (list.isEmpty()) { return object2; From 16aedafbe4c40db87d6d173695bd5a3de3d64ee0 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 06:55:42 +0800 Subject: [PATCH 08/46] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E6=88=90?= =?UTF-8?q?=E4=BA=BA=E7=B1=BB=E5=8F=AF=E8=AF=BB=E7=9A=84=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 731 +++++++++--------- .../core/util/snbt/SnbtOperations.java | 54 +- .../craftengine/core/util/snbt/TagParser.java | 24 +- .../util/snbt/parse/CachedParseState.java | 144 ++-- .../util/snbt/parse/DelayedException.java | 10 +- .../core/util/snbt/parse/Dictionary.java | 43 +- .../core/util/snbt/parse/ErrorCollector.java | 55 +- .../core/util/snbt/parse/ParseState.java | 13 +- .../core/util/snbt/parse/Rule.java | 21 +- .../core/util/snbt/parse/Scope.java | 195 +++-- .../util/snbt/parse/SuggestionSupplier.java | 4 +- .../core/util/snbt/parse/Term.java | 136 ++-- .../core/util/snbt/parse/grammar/Grammar.java | 48 +- .../parse/grammar/GreedyPatternParseRule.java | 15 +- .../grammar/GreedyPredicateParseRule.java | 25 +- .../parse/grammar/NumberRunParseRule.java | 33 +- .../grammar/StringReaderParserState.java | 4 +- .../snbt/parse/grammar/StringReaderTerms.java | 43 +- .../grammar/UnquotedStringParseRule.java | 15 +- 19 files changed, 788 insertions(+), 825 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 4415e2655..74512176c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -124,8 +124,8 @@ public class SnbtGrammar { }; private static final Pattern UNICODE_NAME = Pattern.compile("[-a-zA-Z0-9 ]+"); - static DelayedException createNumberParseError(NumberFormatException numberFormatException) { - return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, numberFormatException.getMessage()); + static DelayedException createNumberParseError(NumberFormatException ex) { + return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, ex.getMessage()); } private static boolean isAllowedToStartUnquotedString(char c) { @@ -139,96 +139,97 @@ public class SnbtGrammar { }; } - static boolean needsUnderscoreRemoval(String text) { - return text.indexOf(95) != -1; + static boolean needsUnderscoreRemoval(String contents) { + return contents.indexOf(95) != -1; } - private static void cleanAndAppend(StringBuilder stringBuilder, String text) { - cleanAndAppend(stringBuilder, text, needsUnderscoreRemoval(text)); + private static void cleanAndAppend(StringBuilder output, String contents) { + cleanAndAppend(output, contents, needsUnderscoreRemoval(contents)); } - static void cleanAndAppend(StringBuilder stringBuilder, String text, boolean removeUnderscores) { - if (removeUnderscores) { - for (char c : text.toCharArray()) { + static void cleanAndAppend(StringBuilder output, String contents, boolean needsUnderscoreRemoval) { + if (needsUnderscoreRemoval) { + for (char c : contents.toCharArray()) { if (c != '_') { - stringBuilder.append(c); + output.append(c); } } + return; } - stringBuilder.append(text); + output.append(contents); } - static short parseUnsignedShort(String text, int radix) { - int i = Integer.parseInt(text, radix); - if (i >> 16 == 0) { - return (short)i; + static short parseUnsignedShort(String string, int radix) { + int parse = Integer.parseInt(string, radix); + if (parse >> 16 == 0) { + return (short) parse; } - throw new NumberFormatException("out of range: " + i); + throw new NumberFormatException("out of range: " + parse); } @Nullable private static T createFloat( DynamicOps ops, Sign sign, - @Nullable String wholePart, - @Nullable String fractionPart, - @Nullable Signed exponentPart, - @Nullable TypeSuffix suffix, - ParseState parseState + @Nullable String whole, + @Nullable String fraction, + @Nullable Signed exponent, + @Nullable TypeSuffix typeSuffix, + ParseState state ) { - StringBuilder stringBuilder = new StringBuilder(); - sign.append(stringBuilder); - if (wholePart != null) { - cleanAndAppend(stringBuilder, wholePart); + StringBuilder result = new StringBuilder(); + sign.append(result); + if (whole != null) { + cleanAndAppend(result, whole); } - if (fractionPart != null) { - stringBuilder.append('.'); - cleanAndAppend(stringBuilder, fractionPart); + if (fraction != null) { + result.append('.'); + cleanAndAppend(result, fraction); } - if (exponentPart != null) { - stringBuilder.append('e'); - exponentPart.sign().append(stringBuilder); - cleanAndAppend(stringBuilder, exponentPart.value); + if (exponent != null) { + result.append('e'); + exponent.sign().append(result); + cleanAndAppend(result, exponent.value); } try { - String string = stringBuilder.toString(); + String string = result.toString(); - return switch (suffix) { - case null -> convertDouble(ops, parseState, string); - case FLOAT -> convertFloat(ops, parseState, string); - case DOUBLE -> convertDouble(ops, parseState, string); + return switch (typeSuffix) { + case null -> convertDouble(ops, state, string); + case FLOAT -> convertFloat(ops, state, string); + case DOUBLE -> convertDouble(ops, state, string); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_FLOAT_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_FLOAT_TYPE); yield null; } }; - } catch (NumberFormatException var11) { - parseState.errorCollector().store(parseState.mark(), createNumberParseError(var11)); + } catch (NumberFormatException e) { + state.errorCollector().store(state.mark(), createNumberParseError(e)); return null; } } @Nullable - private static T convertFloat(DynamicOps ops, ParseState parseState, String value) { - float f = Float.parseFloat(value); - if (!Float.isFinite(f)) { - parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + private static T convertFloat(DynamicOps ops, ParseState state, String contents) { + float value = Float.parseFloat(contents); + if (!Float.isFinite(value)) { + state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); return null; } - return ops.createFloat(f); + return ops.createFloat(value); } @Nullable - private static T convertDouble(DynamicOps ops, ParseState parseState, String value) { - double d = Double.parseDouble(value); - if (!Double.isFinite(d)) { - parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + private static T convertDouble(DynamicOps ops, ParseState state, String contents) { + double value = Double.parseDouble(contents); + if (!Double.isFinite(value)) { + state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); return null; } - return ops.createDouble(d); + return ops.createDouble(value); } private static String joinList(List list) { @@ -241,42 +242,44 @@ public class SnbtGrammar { @SuppressWarnings("unchecked") public static Grammar createParser(DynamicOps ops) { - T object = ops.createBoolean(true); - T object1 = ops.createBoolean(false); - T object2 = ops.emptyMap(); - T object3 = ops.emptyList(); - Dictionary dictionary = new Dictionary<>(); - Atom atom = Atom.of("sign"); - dictionary.put( - atom, + T trueValue = ops.createBoolean(true); + T falseValue = ops.createBoolean(false); + T emptyMapValue = ops.emptyMap(); + T emptyList = ops.emptyList(); + T nullString = ops.createString("null"); + boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + Dictionary rules = new Dictionary<>(); + Atom sign = Atom.of("sign"); + rules.put( + sign, Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(atom, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(atom, Sign.MINUS)) + Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) ), - scope -> scope.getOrThrow(atom) + scope -> scope.getOrThrow(sign) ); - Atom atom1 = Atom.of("integer_suffix"); - dictionary.put( - atom1, + Atom integerSuffix = Atom.of("integer_suffix"); + rules.put( + integerSuffix, Term.alternative( Term.sequence( StringReaderTerms.characters('u', 'U'), Term.alternative( Term.sequence( StringReaderTerms.characters('b', 'B'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) ), Term.sequence( StringReaderTerms.characters('s', 'S'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) ), Term.sequence( StringReaderTerms.characters('i', 'I'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) ), Term.sequence( StringReaderTerms.characters('l', 'L'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) ) ) ), @@ -285,351 +288,349 @@ public class SnbtGrammar { Term.alternative( Term.sequence( StringReaderTerms.characters('b', 'B'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) ), Term.sequence( StringReaderTerms.characters('s', 'S'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) ), Term.sequence( StringReaderTerms.characters('i', 'I'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) ), Term.sequence( StringReaderTerms.characters('l', 'L'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) ) ) ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.LONG))) + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) ), - scope -> scope.getOrThrow(atom1) + scope -> scope.getOrThrow(integerSuffix) ); - Atom atom2 = Atom.of("binary_numeral"); - dictionary.put(atom2, BINARY_NUMERAL); - Atom atom3 = Atom.of("decimal_numeral"); - dictionary.put(atom3, DECIMAL_NUMERAL); - Atom atom4 = Atom.of("hex_numeral"); - dictionary.put(atom4, HEX_NUMERAL); - Atom atom5 = Atom.of("integer_literal"); - NamedRule namedRule = dictionary.put( - atom5, + Atom binaryNumeral = Atom.of("binary_numeral"); + rules.put(binaryNumeral, BINARY_NUMERAL); + Atom decimalNumeral = Atom.of("decimal_numeral"); + rules.put(decimalNumeral, DECIMAL_NUMERAL); + Atom hexNumeral = Atom.of("hex_numeral"); + rules.put(hexNumeral, HEX_NUMERAL); + Atom integerLiteral = Atom.of("integer_literal"); + NamedRule integerLiteralRule = rules.put( + integerLiteral, Term.sequence( - Term.optional(dictionary.named(atom)), + Term.optional(rules.named(sign)), Term.alternative( Term.sequence( StringReaderTerms.character('0'), Term.cut(), Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), dictionary.named(atom4)), - Term.sequence(StringReaderTerms.characters('b', 'B'), dictionary.named(atom2)), - Term.sequence(dictionary.named(atom3), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(atom3, "0") + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), + Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), + Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(decimalNumeral, "0") ) ), - dictionary.named(atom3) + rules.named(decimalNumeral) ), - Term.optional(dictionary.named(atom1)) + Term.optional(rules.named(integerSuffix)) ), scope -> { - IntegerSuffix integerSuffix = scope.getOrDefault(atom1, IntegerSuffix.EMPTY); - Sign sign = scope.getOrDefault(atom, Sign.PLUS); - String string = scope.get(atom3); - if (string != null) { - return new IntegerLiteral(sign, Base.DECIMAL, string, integerSuffix); + IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); + Sign signValue = scope.getOrDefault(sign, Sign.PLUS); + String decimalContents = scope.get(decimalNumeral); + if (decimalContents != null) { + return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); } - String string1 = scope.get(atom4); + String string1 = scope.get(hexNumeral); if (string1 != null) { - return new IntegerLiteral(sign, Base.HEX, string1, integerSuffix); + return new IntegerLiteral(signValue, Base.HEX, string1, suffix); } - String string2 = scope.getOrThrow(atom2); - return new IntegerLiteral(sign, Base.BINARY, string2, integerSuffix); + String string2 = scope.getOrThrow(binaryNumeral); + return new IntegerLiteral(signValue, Base.BINARY, string2, suffix); } ); - Atom atom6 = Atom.of("float_type_suffix"); - dictionary.put( - atom6, + Atom floatTypeSuffix = Atom.of("float_type_suffix"); + rules.put( + floatTypeSuffix, Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(atom6, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(atom6, TypeSuffix.DOUBLE)) + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) ), - scope -> scope.getOrThrow(atom6) + scope -> scope.getOrThrow(floatTypeSuffix) ); - Atom> atom7 = Atom.of("float_exponent_part"); - dictionary.put( - atom7, - Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(dictionary.named(atom)), dictionary.named(atom3)), - scope -> new Signed<>(scope.getOrDefault(atom, Sign.PLUS), scope.getOrThrow(atom3)) + Atom> floatExponentPart = Atom.of("float_exponent_part"); + rules.put( + floatExponentPart, + Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(rules.named(sign)), rules.named(decimalNumeral)), + scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); - Atom atom8 = Atom.of("float_whole_part"); - Atom atom9 = Atom.of("float_fraction_part"); - Atom atom10 = Atom.of("float_literal"); - dictionary.putComplex( - atom10, + Atom floatWholePart = Atom.of("float_whole_part"); + Atom floatFractionPart = Atom.of("float_fraction_part"); + Atom floatLiteral = Atom.of("float_literal"); + rules.putComplex( + floatLiteral, Term.sequence( - Term.optional(dictionary.named(atom)), + Term.optional(rules.named(sign)), Term.alternative( Term.sequence( - dictionary.namedWithAlias(atom3, atom8), + rules.namedWithAlias(decimalNumeral, floatWholePart), StringReaderTerms.character('.'), Term.cut(), - Term.optional(dictionary.namedWithAlias(atom3, atom9)), - Term.optional(dictionary.named(atom7)), - Term.optional(dictionary.named(atom6)) + Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) ), Term.sequence( StringReaderTerms.character('.'), Term.cut(), - dictionary.namedWithAlias(atom3, atom9), - Term.optional(dictionary.named(atom7)), - Term.optional(dictionary.named(atom6)) + rules.namedWithAlias(decimalNumeral, floatFractionPart), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) ), - Term.sequence(dictionary.namedWithAlias(atom3, atom8), dictionary.named(atom7), Term.cut(), Term.optional(dictionary.named(atom6))), - Term.sequence(dictionary.namedWithAlias(atom3, atom8), Term.optional(dictionary.named(atom7)), dictionary.named(atom6)) + Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), rules.named(floatExponentPart), Term.cut(), Term.optional(rules.named(floatTypeSuffix))), + Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), Term.optional(rules.named(floatExponentPart)), rules.named(floatTypeSuffix)) ) ), - parseState -> { - Scope scope = parseState.scope(); - Sign sign = scope.getOrDefault(atom, Sign.PLUS); - String string = scope.get(atom8); - String string1 = scope.get(atom9); - Signed signed = scope.get(atom7); - TypeSuffix typeSuffix = scope.get(atom6); - return createFloat(ops, sign, string, string1, signed, typeSuffix, parseState); + state -> { + Scope scope = state.scope(); + Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); + String whole = scope.get(floatWholePart); + String fraction = scope.get(floatFractionPart); + Signed exponent = scope.get(floatExponentPart); + TypeSuffix typeSuffix = scope.get(floatTypeSuffix); + return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); } ); - Atom atom11 = Atom.of("string_hex_2"); - dictionary.put(atom11, new SimpleHexLiteralParseRule(2)); - Atom atom12 = Atom.of("string_hex_4"); - dictionary.put(atom12, new SimpleHexLiteralParseRule(4)); - Atom atom13 = Atom.of("string_hex_8"); - dictionary.put(atom13, new SimpleHexLiteralParseRule(8)); - Atom atom14 = Atom.of("string_unicode_name"); - dictionary.put(atom14, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); - Atom atom15 = Atom.of("string_escape_sequence"); - dictionary.putComplex( - atom15, + Atom stringHex2 = Atom.of("string_hex_2"); + rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + Atom stringHex4 = Atom.of("string_hex_4"); + rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + Atom stringHex8 = Atom.of("string_hex_8"); + rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + Atom stringUnicodeName = Atom.of("string_unicode_name"); + rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom stringEscapeSequence = Atom.of("string_escape_sequence"); + rules.putComplex( + stringEscapeSequence, Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(atom15, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(atom15, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(atom15, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(atom15, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(atom15, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(atom15, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(atom15, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(atom15, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(atom15, "\"")), - Term.sequence(StringReaderTerms.character('x'), dictionary.named(atom11)), - Term.sequence(StringReaderTerms.character('u'), dictionary.named(atom12)), - Term.sequence(StringReaderTerms.character('U'), dictionary.named(atom13)), - Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), dictionary.named(atom14), StringReaderTerms.character('}')) + Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), + Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), + Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), + Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), + Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), rules.named(stringUnicodeName), StringReaderTerms.character('}')) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.getAny(atom15); - if (string != null) { - return string; + state -> { + Scope scope = state.scope(); + String plainEscape = scope.getAny(stringEscapeSequence); + if (plainEscape != null) { + return plainEscape; } - String string1 = scope.getAny(atom11, atom12, atom13); - if (string1 != null) { - int i = HexFormat.fromHexDigits(string1); - if (!Character.isValidCodePoint(i)) { - parseState.errorCollector() - .store(parseState.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", i))); + String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); + if (hexEscape != null) { + int codePoint = HexFormat.fromHexDigits(hexEscape); + if (!Character.isValidCodePoint(codePoint)) { + state.errorCollector() + .store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); return null; } - return Character.toString(i); + return Character.toString(codePoint); } - String string2 = scope.getOrThrow(atom14); + String character = scope.getOrThrow(stringUnicodeName); - int i1; + int codePoint; try { - i1 = Character.codePointOf(string2); + codePoint = Character.codePointOf(character); } catch (IllegalArgumentException var12x) { - parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_CHARACTER_NAME); + state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); return null; } - return Character.toString(i1); + return Character.toString(codePoint); } ); - Atom atom16 = Atom.of("string_plain_contents"); - dictionary.put(atom16, PLAIN_STRING_CHUNK); - Atom> atom17 = Atom.of("string_chunks"); - Atom atom18 = Atom.of("string_contents"); - Atom atom19 = Atom.of("single_quoted_string_chunk"); - NamedRule namedRule1 = dictionary.put( - atom19, + Atom stringPlainContents = Atom.of("string_plain_contents"); + rules.put(stringPlainContents, PLAIN_STRING_CHUNK); + Atom> stringChunks = Atom.of("string_chunks"); + Atom stringContents = Atom.of("string_contents"); + Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); + NamedRule namedRule1 = rules.put( + singleQuotedStringChunk, Term.alternative( - dictionary.namedWithAlias(atom16, atom18), - Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(atom18, "\"")) + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom20 = Atom.of("single_quoted_string_contents"); - dictionary.put(atom20, Term.repeated(namedRule1, atom17), scope -> joinList(scope.getOrThrow(atom17))); - Atom atom21 = Atom.of("double_quoted_string_chunk"); - NamedRule namedRule2 = dictionary.put( - atom21, + Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); + rules.put(singleQuotedStringContents, Term.repeated(namedRule1, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); + NamedRule namedRule2 = rules.put( + doubleQuotedStringChunk, Term.alternative( - dictionary.namedWithAlias(atom16, atom18), - Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(atom18, "'")) + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom22 = Atom.of("double_quoted_string_contents"); - dictionary.put(atom22, Term.repeated(namedRule2, atom17), scope -> joinList(scope.getOrThrow(atom17))); - Atom atom23 = Atom.of("quoted_string_literal"); - dictionary.put( - atom23, + Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); + rules.put(doubleQuotedStringContents, Term.repeated(namedRule2, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + Atom quotedStringLiteral = Atom.of("quoted_string_literal"); + rules.put( + quotedStringLiteral, Term.alternative( Term.sequence( - StringReaderTerms.character('"'), Term.cut(), Term.optional(dictionary.namedWithAlias(atom22, atom18)), StringReaderTerms.character('"') + StringReaderTerms.character('"'), Term.cut(), Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), StringReaderTerms.character('"') ), - Term.sequence(StringReaderTerms.character('\''), Term.optional(dictionary.namedWithAlias(atom20, atom18)), StringReaderTerms.character('\'')) + Term.sequence(StringReaderTerms.character('\''), Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), StringReaderTerms.character('\'')) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom24 = Atom.of("unquoted_string"); - dictionary.put(atom24, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); - Atom atom25 = Atom.of("literal"); - Atom> atom26 = Atom.of("arguments"); - dictionary.put( - atom26, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom26, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom26) + Atom unquotedString = Atom.of("unquoted_string"); + rules.put(unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + Atom literal = Atom.of("literal"); + Atom> argumentList = Atom.of("arguments"); + rules.put( + argumentList, Term.repeatedWithTrailingSeparator(rules.forward(literal), argumentList, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(argumentList) ); - Atom atom27 = Atom.of("unquoted_string_or_builtin"); - dictionary.putComplex( - atom27, + Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); + rules.putComplex( + unquotedStringOrBuiltIn, Term.sequence( - dictionary.named(atom24), - Term.optional(Term.sequence(StringReaderTerms.character('('), dictionary.named(atom26), StringReaderTerms.character(')'))) + rules.named(unquotedString), + Term.optional(Term.sequence(StringReaderTerms.character('('), rules.named(argumentList), StringReaderTerms.character(')'))) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.getOrThrow(atom24); - if (!string.isEmpty() && isAllowedToStartUnquotedString(string.charAt(0))) { - List list = scope.get(atom26); - if (list != null) { - SnbtOperations.BuiltinKey builtinKey = new SnbtOperations.BuiltinKey(string, list.size()); - SnbtOperations.BuiltinOperation builtinOperation = SnbtOperations.BUILTIN_OPERATIONS.get(builtinKey); - if (builtinOperation != null) { - return builtinOperation.run(ops, list, parseState); + state -> { + Scope scope = state.scope(); + String contents = scope.getOrThrow(unquotedString); + if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { + List arguments = scope.get(argumentList); + if (arguments != null) { + SnbtOperations.BuiltinKey key = new SnbtOperations.BuiltinKey(contents, arguments.size()); + SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); + if (operation != null) { + return operation.run(ops, arguments, state); } - parseState.errorCollector().store(parseState.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, builtinKey.toString())); + state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); return null; - } else if (string.equalsIgnoreCase("true")) { - return object; - } else if (string.equalsIgnoreCase("false")) { - return object1; - } else if (string.equalsIgnoreCase("null")) { + } else if (contents.equalsIgnoreCase("true")) { + return trueValue; + } else if (contents.equalsIgnoreCase("false")) { + return falseValue; + } else if (contents.equalsIgnoreCase("null")) { return Objects.requireNonNullElseGet(ops.empty(), () -> { - T nullString = ops.createString("null"); - if ("null".equals(nullString)) { // 确定是 Java 类型的 + if (isJavaType) { return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; } return nullString; }); } - return ops.createString(string); + return ops.createString(contents); } - parseState.errorCollector().store(parseState.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); + state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); return null; } ); - Atom atom28 = Atom.of("map_key"); - dictionary.put(atom28, Term.alternative(dictionary.named(atom23), dictionary.named(atom24)), scope -> scope.getAnyOrThrow(atom23, atom24)); - Atom> atom29 = Atom.of("map_entry"); - NamedRule> namedRule3 = dictionary.putComplex( - atom29, Term.sequence(dictionary.named(atom28), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), dictionary.named(atom25)), parseState -> { - Scope scope = parseState.scope(); - String string = scope.getOrThrow(atom28); - if (string.isEmpty()) { - parseState.errorCollector().store(parseState.mark(), ERROR_EMPTY_KEY); + Atom mapKey = Atom.of("map_key"); + rules.put(mapKey, Term.alternative(rules.named(quotedStringLiteral), rules.named(unquotedString)), scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString)); + Atom> mapEntry = Atom.of("map_entry"); + NamedRule> mapEntryRule = rules.putComplex( + mapEntry, Term.sequence(rules.named(mapKey), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), rules.named(literal)), state -> { + Scope scope = state.scope(); + String key = scope.getOrThrow(mapKey); + if (key.isEmpty()) { + state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); return null; } - T orThrow = scope.getOrThrow(atom25); - return Map.entry(string, orThrow); + T value = scope.getOrThrow(literal); + return Map.entry(key, value); } ); - Atom>> atom30 = Atom.of("map_entries"); - dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); - Atom atom31 = Atom.of("map_literal"); - dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { - List> list = scope.getOrThrow(atom30); - if (list.isEmpty()) { - return object2; - } else { - Builder builder = ImmutableMap.builderWithExpectedSize(list.size()); - - for (Entry entry : list) { - builder.put(ops.createString(entry.getKey()), entry.getValue()); - } - - return ops.createMap(builder.buildKeepingLast()); + Atom>> mapEntries = Atom.of("map_entries"); + rules.put(mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(mapEntries)); + Atom mapLiteral = Atom.of("map_literal"); + rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; } + Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + + for (Entry e : entries) { + builder.put(ops.createString(e.getKey()), e.getValue()); + } + + return ops.createMap(builder.buildKeepingLast()); }); - Atom> atom32 = Atom.of("list_entries"); - dictionary.put( - atom32, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom32, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom32) + Atom> listEntries = Atom.of("list_entries"); + rules.put( + listEntries, Term.repeatedWithTrailingSeparator(rules.forward(literal), listEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(listEntries) ); - Atom atom33 = Atom.of("array_prefix"); - dictionary.put( - atom33, + Atom arrayPrefix = Atom.of("array_prefix"); + rules.put( + arrayPrefix, Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(atom33, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(atom33, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(atom33, ArrayPrefix.INT)) + Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) ), - scope -> scope.getOrThrow(atom33) + scope -> scope.getOrThrow(arrayPrefix) ); - Atom> atom34 = Atom.of("int_array_entries"); - dictionary.put(atom34, Term.repeatedWithTrailingSeparator(namedRule, atom34, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom34)); - Atom atom35 = Atom.of("list_literal"); - dictionary.putComplex( - atom35, + Atom> intArrayEntries = Atom.of("int_array_entries"); + rules.put(intArrayEntries, Term.repeatedWithTrailingSeparator(integerLiteralRule, intArrayEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(intArrayEntries)); + Atom listLiteral = Atom.of("list_literal"); + rules.putComplex( + listLiteral, Term.sequence( StringReaderTerms.character('['), Scope.increaseDepth(), - Term.alternative(Term.sequence(dictionary.named(atom33), StringReaderTerms.character(';'), dictionary.named(atom34)), dictionary.named(atom32)), + Term.alternative(Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries)), Scope.decreaseDepth(), StringReaderTerms.character(']') ), - parseState -> { - Scope scope = parseState.scope(); - ArrayPrefix arrayPrefix = scope.get(atom33); - if (arrayPrefix != null) { - List list = scope.getOrThrow(atom34); - return list.isEmpty() ? arrayPrefix.create(ops) : arrayPrefix.create(ops, list, parseState); + state -> { + Scope scope = state.scope(); + ArrayPrefix arrayType = scope.get(arrayPrefix); + if (arrayType != null) { + List entries = scope.getOrThrow(intArrayEntries); + return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); } - List list = scope.getOrThrow(atom32); - return list.isEmpty() ? object3 : ops.createList(list.stream()); + List entries = scope.getOrThrow(listEntries); + return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); } ); - NamedRule namedRule4 = dictionary.putComplex( - atom25, + NamedRule literalRule = rules.putComplex( + literal, Term.alternative( - Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(dictionary.namedWithAlias(atom10, atom25), dictionary.named(atom5))), - Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), dictionary.named(atom23)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), dictionary.namedWithAlias(atom31, atom25)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), dictionary.namedWithAlias(atom35, atom25)), - dictionary.namedWithAlias(atom27, atom25) + Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(rules.namedWithAlias(floatLiteral, literal), rules.named(integerLiteral))), + Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), rules.named(quotedStringLiteral)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), rules.namedWithAlias(mapLiteral, literal)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), rules.namedWithAlias(listLiteral, literal)), + rules.namedWithAlias(unquotedStringOrBuiltIn, literal) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.get(atom23); - if (string != null) { - return ops.createString(string); + state -> { + Scope scope = state.scope(); + String quotedString = scope.get(quotedStringLiteral); + if (quotedString != null) { + return ops.createString(quotedString); } - IntegerLiteral integerLiteral = scope.get(atom5); - return integerLiteral != null ? integerLiteral.create(ops, parseState) : scope.getOrThrow(atom25); + IntegerLiteral integer = scope.get(integerLiteral); + return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); } ); - return new Grammar<>(dictionary, namedRule4); + return new Grammar<>(rules, literalRule); } enum ArrayPrefix { @@ -643,19 +644,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - ByteList list = new ByteArrayList(); + public T create(DynamicOps ops, List entries, ParseState state) { + ByteList result = new ByteArrayList(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); + for (IntegerLiteral entry : entries) { + Number number = this.buildNumber(entry, state); if (number == null) { return null; } - list.add(number.byteValue()); + result.add(number.byteValue()); } - return ops.createByteList(ByteBuffer.wrap(list.toByteArray())); + return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); } }, INT(TypeSuffix.INT, TypeSuffix.BYTE, TypeSuffix.SHORT) { @@ -666,19 +667,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - IntStream.Builder builder = IntStream.builder(); + public T create(DynamicOps ops, List entries, ParseState state) { + IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); - if (number == null) { + for (IntegerLiteral entry : entries) { + Number parsedNumber = this.buildNumber(entry, state); + if (parsedNumber == null) { return null; } - builder.add(number.intValue()); + result.add(parsedNumber.intValue()); } - return ops.createIntList(builder.build()); + return ops.createIntList(result.build()); } }, LONG(TypeSuffix.LONG, TypeSuffix.BYTE, TypeSuffix.SHORT, TypeSuffix.INT) { @@ -689,19 +690,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - LongStream.Builder builder = LongStream.builder(); + public T create(DynamicOps ops, List entries, ParseState state) { + LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); - if (number == null) { + for (IntegerLiteral entry : entries) { + Number parsedNumber = this.buildNumber(entry, state); + if (parsedNumber == null) { return null; } - builder.add(number.longValue()); + result.add(parsedNumber.longValue()); } - return ops.createLongList(builder.build()); + return ops.createLongList(result.build()); } }; @@ -713,32 +714,32 @@ public class SnbtGrammar { this.defaultType = defaultType; } - public boolean isAllowed(TypeSuffix suffix) { - return suffix == this.defaultType || this.additionalTypes.contains(suffix); + public boolean isAllowed(TypeSuffix type) { + return type == this.defaultType || this.additionalTypes.contains(type); } public abstract T create(DynamicOps ops); @Nullable - public abstract T create(DynamicOps ops, List values, ParseState parseState); + public abstract T create(DynamicOps ops, List entries, ParseState state); @Nullable - protected Number buildNumber(IntegerLiteral value, ParseState parseState) { - TypeSuffix typeSuffix = this.computeType(value.suffix); - if (typeSuffix == null) { - parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); + protected Number buildNumber(IntegerLiteral entry, ParseState state) { + TypeSuffix actualType = this.computeType(entry.suffix); + if (actualType == null) { + state.errorCollector().store(state.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); return null; } - return (Number)value.create(JavaOps.INSTANCE, typeSuffix, parseState); + return (Number) entry.create(JavaOps.INSTANCE, actualType, state); } @Nullable - private TypeSuffix computeType(IntegerSuffix suffix) { - TypeSuffix typeSuffix = suffix.type(); - if (typeSuffix == null) { + private TypeSuffix computeType(IntegerSuffix value) { + TypeSuffix type = value.type(); + if (type == null) { return this.defaultType; } - return !this.isAllowed(typeSuffix) ? null : typeSuffix; + return !this.isAllowed(type) ? null : type; } } @@ -757,61 +758,61 @@ public class SnbtGrammar { } private String cleanupDigits(Sign sign) { - boolean flag = needsUnderscoreRemoval(this.digits); - if (sign != Sign.MINUS && !flag) { + boolean needsUnderscoreRemoval = needsUnderscoreRemoval(this.digits); + if (sign != Sign.MINUS && !needsUnderscoreRemoval) { return this.digits; } - StringBuilder stringBuilder = new StringBuilder(); - sign.append(stringBuilder); - cleanAndAppend(stringBuilder, this.digits, flag); - return stringBuilder.toString(); + StringBuilder result = new StringBuilder(); + sign.append(result); + cleanAndAppend(result, this.digits, needsUnderscoreRemoval); + return result.toString(); } @Nullable - public T create(DynamicOps ops, ParseState parseState) { - return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), parseState); + public T create(DynamicOps ops, ParseState state) { + return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), state); } @Nullable - public T create(DynamicOps ops, TypeSuffix typeSuffix, ParseState parseState) { - boolean flag = this.signedOrDefault() == SignedPrefix.SIGNED; - if (!flag && this.sign == Sign.MINUS) { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); + public T create(DynamicOps ops, TypeSuffix type, ParseState state) { + boolean isSigned = this.signedOrDefault() == SignedPrefix.SIGNED; + if (!isSigned && this.sign == Sign.MINUS) { + state.errorCollector().store(state.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); return null; } - String string = this.cleanupDigits(this.sign); + String fixedDigits = this.cleanupDigits(this.sign); - int i = switch (this.base) { + int radix = switch (this.base) { case BINARY -> 2; case DECIMAL -> 10; case HEX -> 16; }; try { - if (flag) { - return switch (typeSuffix) { - case BYTE -> ops.createByte(Byte.parseByte(string, i)); - case SHORT -> ops.createShort(Short.parseShort(string, i)); - case INT -> ops.createInt(Integer.parseInt(string, i)); - case LONG -> ops.createLong(Long.parseLong(string, i)); + if (isSigned) { + return switch (type) { + case BYTE -> ops.createByte(Byte.parseByte(fixedDigits, radix)); + case SHORT -> ops.createShort(Short.parseShort(fixedDigits, radix)); + case INT -> ops.createInt(Integer.parseInt(fixedDigits, radix)); + case LONG -> ops.createLong(Long.parseLong(fixedDigits, radix)); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); yield null; } }; } - return switch (typeSuffix) { - case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(string, i)); - case SHORT -> ops.createShort(parseUnsignedShort(string, i)); - case INT -> ops.createInt(Integer.parseUnsignedInt(string, i)); - case LONG -> ops.createLong(Long.parseUnsignedLong(string, i)); + return switch (type) { + case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(fixedDigits, radix)); + case SHORT -> ops.createShort(parseUnsignedShort(fixedDigits, radix)); + case INT -> ops.createInt(Integer.parseUnsignedInt(fixedDigits, radix)); + case LONG -> ops.createLong(Long.parseUnsignedLong(fixedDigits, radix)); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); yield null; } }; } catch (NumberFormatException var8) { - parseState.errorCollector().store(parseState.mark(), createNumberParseError(var8)); + state.errorCollector().store(state.mark(), createNumberParseError(var8)); return null; } } @@ -825,9 +826,9 @@ public class SnbtGrammar { PLUS, MINUS; - public void append(StringBuilder stringBuilder) { + public void append(StringBuilder output) { if (this == MINUS) { - stringBuilder.append("-"); + output.append("-"); } } } @@ -841,8 +842,8 @@ public class SnbtGrammar { } static class SimpleHexLiteralParseRule extends GreedyPredicateParseRule { - public SimpleHexLiteralParseRule(int minSize) { - super(minSize, minSize, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(minSize))); + public SimpleHexLiteralParseRule(int size) { + super(size, size, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(size))); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java index 9c684725d..589426cad 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -26,44 +26,42 @@ public class SnbtOperations { public static final Map BUILTIN_OPERATIONS = Map.of( new BuiltinKey("bool", 1), new BuiltinOperation() { @Override - public T run(DynamicOps ops, List args, ParseState parseState) { - Boolean bool = convert(ops, args.getFirst()); - if (bool == null) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); + public T run(DynamicOps ops, List arguments, ParseState state) { + Boolean result = convert(ops, arguments.getFirst()); + if (result == null) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); return null; - } else { - return ops.createBoolean(bool); } + return ops.createBoolean(result); } @Nullable - private static Boolean convert(DynamicOps ops, T value) { - Optional optional = ops.getBooleanValue(value).result(); - if (optional.isPresent()) { - return optional.get(); + private static Boolean convert(DynamicOps ops, T arg) { + Optional asBoolean = ops.getBooleanValue(arg).result(); + if (asBoolean.isPresent()) { + return asBoolean.get(); } else { - Optional optional1 = ops.getNumberValue(value).result(); - return optional1.isPresent() ? optional1.get().doubleValue() != 0.0 : null; + Optional asNumber = ops.getNumberValue(arg).result(); + return asNumber.isPresent() ? asNumber.get().doubleValue() != 0.0 : null; } } }, new BuiltinKey("uuid", 1), new BuiltinOperation() { @Override - public T run(DynamicOps ops, List args, ParseState parseState) { - Optional optional = ops.getStringValue(args.getFirst()).result(); - if (optional.isEmpty()) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + public T run(DynamicOps ops, List arguments, ParseState state) { + Optional arg = ops.getStringValue(arguments.getFirst()).result(); + if (arg.isEmpty()) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); return null; - } else { - UUID uuid; - try { - uuid = UUID.fromString(optional.get()); - } catch (IllegalArgumentException var7) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); - return null; - } - - return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); } + UUID uuid; + try { + uuid = UUID.fromString(arg.get()); + } catch (IllegalArgumentException var7) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } + + return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); } } ); @@ -74,7 +72,7 @@ public class SnbtOperations { .collect(Collectors.toSet()); @Override - public Stream possibleValues(ParseState parseState) { + public Stream possibleValues(ParseState state) { return this.keys.stream(); } }; @@ -88,6 +86,6 @@ public class SnbtOperations { public interface BuiltinOperation { @Nullable - T run(DynamicOps ops, List args, ParseState parseState); + T run(DynamicOps ops, List arguments, ParseState state); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java index 6c7badbf9..d399e6151 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -42,34 +42,34 @@ public class TagParser { return new TagParser<>(ops, SnbtGrammar.createParser(ops)); } - private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag tag) throws CommandSyntaxException { - if (tag instanceof CompoundTag compoundTag) { + private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag result) throws CommandSyntaxException { + if (result instanceof CompoundTag compoundTag) { return compoundTag; } throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); } - public static CompoundTag parseCompoundFully(String data) throws CommandSyntaxException { - StringReader stringReader = new StringReader(data); - return parseCompoundAsArgument(stringReader); + public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return parseCompoundAsArgument(reader); } - public static Object parseObjectFully(String data) throws CommandSyntaxException { - StringReader stringReader = new StringReader(data); - return parseObjectAsArgument(stringReader); + public static Object parseObjectFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return parseObjectAsArgument(reader); } - public T parseFully(String text) throws CommandSyntaxException { - return this.parseFully(new StringReader(text)); + public T parseFully(String input) throws CommandSyntaxException { + return this.parseFully(new StringReader(input)); } public T parseFully(StringReader reader) throws CommandSyntaxException { - T object = this.grammar.parse(reader); + T result = this.grammar.parse(reader); reader.skipWhitespace(); if (reader.canRead()) { throw ERROR_TRAILING_DATA.createWithContext(reader); } - return object; + return result; } public T parseAsArgument(StringReader reader) throws CommandSyntaxException { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java index 03c439c82..b71d66c24 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java @@ -1,7 +1,5 @@ package net.momirealms.craftengine.core.util.snbt.parse; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; import net.momirealms.craftengine.core.util.MiscUtils; import javax.annotation.Nullable; @@ -14,7 +12,6 @@ public abstract class CachedParseState implements ParseState { private SimpleControl[] controlCache = new SimpleControl[16]; private int nextControlToReturn; private final Silent silent = new Silent(); - private final IntList markedNull = new IntArrayList(); public static final Object JAVA_NULL_VALUE_MARKER = new Object() { @Override public String toString() { @@ -39,11 +36,11 @@ public abstract class CachedParseState implements ParseState { @Nullable @Override public T parse(NamedRule rule) { - int i = this.mark(); - PositionCache cacheForPosition = this.getCacheForPosition(i); - int i1 = cacheForPosition.findKeyIndex(rule.name()); - if (i1 != -1) { - CacheEntry value = cacheForPosition.getValue(i1); + int markBeforeParse = this.mark(); + PositionCache positionCache = this.getCacheForPosition(markBeforeParse); + int entryIndex = positionCache.findKeyIndex(rule.name()); + if (entryIndex != -1) { + CacheEntry value = positionCache.getValue(entryIndex); if (value != null) { if (value == CachedParseState.CacheEntry.NEGATIVE) { return null; @@ -52,59 +49,60 @@ public abstract class CachedParseState implements ParseState { return value.value; } } else { - i1 = cacheForPosition.allocateNewEntry(rule.name()); + entryIndex = positionCache.allocateNewEntry(rule.name()); } - T object = rule.value().parse(this); - CacheEntry cacheEntry; - if (object == null) { - cacheEntry = (CacheEntry) CacheEntry.NEGATIVE; + T result = rule.value().parse(this); + CacheEntry entry; + if (result == null) { + entry = CacheEntry.negativeEntry(); } else { - cacheEntry = new CacheEntry<>(object, this.mark()); + int markAfterParse = this.mark(); + entry = new CacheEntry<>(result, markAfterParse); } - cacheForPosition.setValue(i1, cacheEntry); - return object; + positionCache.setValue(entryIndex, entry); + return result; } - private PositionCache getCacheForPosition(int position) { - int i = this.positionCache.length; - if (position >= i) { - int i1 = MiscUtils.growByHalf(i, position + 1); - PositionCache[] positionCaches = new PositionCache[i1]; - System.arraycopy(this.positionCache, 0, positionCaches, 0, i); - this.positionCache = positionCaches; + private PositionCache getCacheForPosition(int index) { + int currentSize = this.positionCache.length; + if (index >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, index + 1); + PositionCache[] newCache = new PositionCache[newSize]; + System.arraycopy(this.positionCache, 0, newCache, 0, currentSize); + this.positionCache = newCache; } - PositionCache positionCache = this.positionCache[position]; - if (positionCache == null) { - positionCache = new PositionCache(); - this.positionCache[position] = positionCache; + PositionCache result = this.positionCache[index]; + if (result == null) { + result = new PositionCache(); + this.positionCache[index] = result; } - return positionCache; + return result; } @Override public Control acquireControl() { - int i = this.controlCache.length; - if (this.nextControlToReturn >= i) { - int i1 = MiscUtils.growByHalf(i, this.nextControlToReturn + 1); - SimpleControl[] simpleControls = new SimpleControl[i1]; - System.arraycopy(this.controlCache, 0, simpleControls, 0, i); - this.controlCache = simpleControls; + int currentSize = this.controlCache.length; + if (this.nextControlToReturn >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, this.nextControlToReturn + 1); + SimpleControl[] newControlCache = new SimpleControl[newSize]; + System.arraycopy(this.controlCache, 0, newControlCache, 0, currentSize); + this.controlCache = newControlCache; } - int i1 = this.nextControlToReturn++; - SimpleControl simpleControl = this.controlCache[i1]; - if (simpleControl == null) { - simpleControl = new SimpleControl(); - this.controlCache[i1] = simpleControl; + int controlIndex = this.nextControlToReturn++; + SimpleControl entry = this.controlCache[controlIndex]; + if (entry == null) { + entry = new SimpleControl(); + this.controlCache[controlIndex] = entry; } else { - simpleControl.reset(); + entry.reset(); } - return simpleControl; + return entry; } @Override @@ -117,18 +115,12 @@ public abstract class CachedParseState implements ParseState { return this.silent; } - @Override - public void markNull(int mark) { - this.markedNull.add(mark); - } - - @Override - public boolean isNull(int mark) { - return this.markedNull.contains(mark); - } - record CacheEntry(@Nullable T value, int markAfterParse) { public static final CacheEntry NEGATIVE = new CacheEntry<>(null, -1); + + public static CacheEntry negativeEntry() { + return (CacheEntry) NEGATIVE; + } } static class PositionCache { @@ -137,9 +129,9 @@ public abstract class CachedParseState implements ParseState { private Object[] atomCache = new Object[16]; private int nextKey; - public int findKeyIndex(Atom atom) { + public int findKeyIndex(Atom key) { for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) { - if (this.atomCache[i] == atom) { + if (this.atomCache[i] == key) { return i; } } @@ -147,29 +139,29 @@ public abstract class CachedParseState implements ParseState { return NOT_FOUND; } - public int allocateNewEntry(Atom entry) { - int i = this.nextKey; - this.nextKey += 2; - int i1 = i + 1; - int i2 = this.atomCache.length; - if (i1 >= i2) { - int i3 = MiscUtils.growByHalf(i2, i1 + 1); - Object[] objects = new Object[i3]; - System.arraycopy(this.atomCache, 0, objects, 0, i2); - this.atomCache = objects; + public int allocateNewEntry(Atom key) { + int newKeyIndex = this.nextKey; + this.nextKey += ENTRY_STRIDE; + int newValueIndex = newKeyIndex + 1; + int currentSize = this.atomCache.length; + if (newValueIndex >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, newValueIndex + 1); + Object[] newCache = new Object[newSize]; + System.arraycopy(this.atomCache, 0, newCache, 0, currentSize); + this.atomCache = newCache; } - this.atomCache[i] = entry; - return i; + this.atomCache[newKeyIndex] = key; + return newKeyIndex; } @Nullable - public CacheEntry getValue(int index) { - return (CacheEntry)this.atomCache[index + 1]; + public CacheEntry getValue(int keyIndex) { + return (CacheEntry) this.atomCache[keyIndex + 1]; } - public void setValue(int index, CacheEntry value) { - this.atomCache[index + 1] = value; + public void setValue(int keyIndex, CacheEntry entry) { + this.atomCache[keyIndex + 1] = entry; } } @@ -203,8 +195,8 @@ public abstract class CachedParseState implements ParseState { } @Override - public void restore(int cursor) { - CachedParseState.this.restore(cursor); + public void restore(int mark) { + CachedParseState.this.restore(mark); } @Override @@ -221,16 +213,6 @@ public abstract class CachedParseState implements ParseState { public ParseState silent() { return this; } - - @Override - public void markNull(int mark) { - CachedParseState.this.markNull(mark); - } - - @Override - public boolean isNull(int mark) { - return CachedParseState.this.isNull(mark); - } } static class SimpleControl implements Control { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java index ee79b450c..173b85840 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java @@ -7,13 +7,13 @@ import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms @FunctionalInterface public interface DelayedException { - T create(String message, int cursor); + T create(String contents, int position); - static DelayedException create(SimpleCommandExceptionType exception) { - return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor)); + static DelayedException create(SimpleCommandExceptionType type) { + return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position)); } - static DelayedException create(DynamicCommandExceptionType exception, String argument) { - return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor), argument); + static DelayedException create(DynamicCommandExceptionType type, String argument) { + return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position), argument); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java index 20d6c4bb2..66ea4e088 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java @@ -11,28 +11,30 @@ import java.util.function.Supplier; public class Dictionary { private final Map, Entry> terms = new IdentityHashMap<>(); - public NamedRule put(Atom name, Rule rule) { - Entry entry = (Entry)this.terms.computeIfAbsent(name, Entry::new); - if (entry.value != null) { + public NamedRule put(Atom name, Rule entry) { + Entry holder = (Entry)this.terms.computeIfAbsent(name, Entry::new); + if (holder.value != null) { throw new IllegalArgumentException("Trying to override rule: " + name); - } else { - entry.value = rule; - return entry; } + holder.value = entry; + return holder; } - public NamedRule putComplex(Atom name, Term term, Rule.RuleAction ruleAction) { - return this.put(name, Rule.fromTerm(term, ruleAction)); + public NamedRule putComplex(Atom name, Term term, Rule.RuleAction action) { + return this.put(name, Rule.fromTerm(term, action)); } - public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction ruleAction) { - return this.put(name, Rule.fromTerm(term, ruleAction)); + public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction action) { + return this.put(name, Rule.fromTerm(term, action)); } public void checkAllBound() { - List> list = this.terms.entrySet().stream().filter(entry -> entry.getValue() == null).map(Map.Entry::getKey).toList(); - if (!list.isEmpty()) { - throw new IllegalStateException("Unbound names: " + list); + List> unboundNames = this.terms.entrySet().stream() + .filter(entry -> entry.getValue() == null) + .map(Map.Entry::getKey) + .toList(); + if (!unboundNames.isEmpty()) { + throw new IllegalStateException("Unbound names: " + unboundNames); } } @@ -48,8 +50,8 @@ public class Dictionary { return new Reference<>(this.getOrCreateEntry(name), name); } - public Term namedWithAlias(Atom name, Atom alias) { - return new Reference<>(this.getOrCreateEntry(name), alias); + public Term namedWithAlias(Atom nameToParse, Atom nameToStore) { + return new Reference<>(this.getOrCreateEntry(nameToParse), nameToStore); } static class Entry implements NamedRule, Supplier { @@ -79,14 +81,13 @@ public class Dictionary { record Reference(Entry ruleToParse, Atom nameToStore) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - T object = parseState.parse(this.ruleToParse); - if (object == null) { + public boolean parse(ParseState state, Scope scope, Control control) { + T result = state.parse(this.ruleToParse); + if (result == null) { return false; - } else { - scope.put(this.nameToStore, object); - return true; } + scope.put(this.nameToStore, result); + return true; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java index ef0aac7e3..1bb43a0ae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java @@ -13,7 +13,7 @@ public interface ErrorCollector { this.store(cursor, SuggestionSupplier.empty(), reason); } - void finish(int cursor); + void finish(int finalCursor); class LongestOnly implements ErrorCollector { private MutableErrorEntry[] entries = new MutableErrorEntry[16]; @@ -28,8 +28,8 @@ public interface ErrorCollector { } @Override - public void finish(int cursor) { - this.discardErrorsFromShorterParse(cursor); + public void finish(int finalCursor) { + this.discardErrorsFromShorterParse(finalCursor); } @Override @@ -41,39 +41,38 @@ public interface ErrorCollector { } private void addErrorEntry(SuggestionSupplier suggestions, Object reason) { - int i = this.entries.length; - if (this.nextErrorEntry >= i) { - int i1 = MiscUtils.growByHalf(i, this.nextErrorEntry + 1); - MutableErrorEntry[] mutableErrorEntrys = new MutableErrorEntry[i1]; - System.arraycopy(this.entries, 0, mutableErrorEntrys, 0, i); - this.entries = mutableErrorEntrys; + int currentSize = this.entries.length; + if (this.nextErrorEntry >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, this.nextErrorEntry + 1); + MutableErrorEntry[] newEntries = new MutableErrorEntry[newSize]; + System.arraycopy(this.entries, 0, newEntries, 0, currentSize); + this.entries = newEntries; } - int i1 = this.nextErrorEntry++; - MutableErrorEntry mutableErrorEntry = this.entries[i1]; - if (mutableErrorEntry == null) { - mutableErrorEntry = new MutableErrorEntry<>(); - this.entries[i1] = mutableErrorEntry; + int entryIndex = this.nextErrorEntry++; + MutableErrorEntry entry = this.entries[entryIndex]; + if (entry == null) { + entry = new MutableErrorEntry<>(); + this.entries[entryIndex] = entry; } - mutableErrorEntry.suggestions = suggestions; - mutableErrorEntry.reason = reason; + entry.suggestions = suggestions; + entry.reason = reason; } public List> entries() { - int i = this.nextErrorEntry; - if (i == 0) { + int errorCount = this.nextErrorEntry; + if (errorCount == 0) { return List.of(); - } else { - List> list = new ArrayList<>(i); - - for (int i1 = 0; i1 < i; i1++) { - MutableErrorEntry mutableErrorEntry = this.entries[i1]; - list.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); - } - - return list; } + List> result = new ArrayList<>(errorCount); + + for (int i = 0; i < errorCount; i++) { + MutableErrorEntry mutableErrorEntry = this.entries[i]; + result.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); + } + + return result; } public int cursor() { @@ -92,7 +91,7 @@ public interface ErrorCollector { } @Override - public void finish(int cursor) { + public void finish(int finalCursor) { } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java index 5a631476b..2ab971364 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java @@ -9,16 +9,15 @@ public interface ParseState { ErrorCollector errorCollector(); default Optional parseTopRule(NamedRule rule) { - T object = this.parse(rule); - if (object != null) { + T result = this.parse(rule); + if (result != null) { this.errorCollector().finish(this.mark()); } if (!this.scope().hasOnlySingleFrame()) { throw new IllegalStateException("Malformed scope: " + this.scope()); - } else { - return Optional.ofNullable(object); } + return Optional.ofNullable(result); } @Nullable @@ -28,15 +27,11 @@ public interface ParseState { int mark(); - void restore(int cursor); + void restore(int mark); Control acquireControl(); void releaseControl(); ParseState silent(); - - void markNull(int mark); - - boolean isNull(int mark); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java index 6a5728ec4..cfb7403c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java @@ -4,7 +4,7 @@ import javax.annotation.Nullable; public interface Rule { @Nullable - T parse(ParseState parseState); + T parse(ParseState state); static Rule fromTerm(Term child, RuleAction action) { return new WrappedTerm<>(action, child); @@ -17,38 +17,35 @@ public interface Rule { @FunctionalInterface interface RuleAction { @Nullable - T run(ParseState parseState); + T run(ParseState state); } @FunctionalInterface interface SimpleRuleAction extends RuleAction { - T run(Scope scope); + T run(Scope ruleScope); @Override - default T run(ParseState parseState) { - return this.run(parseState.scope()); + default T run(ParseState state) { + return this.run(state.scope()); } } record WrappedTerm(RuleAction action, Term child) implements Rule { @Nullable @Override - public T parse(ParseState parseState) { - Scope scope = parseState.scope(); + public T parse(ParseState state) { + Scope scope = state.scope(); scope.pushFrame(); - T var3; try { - if (!this.child.parse(parseState, scope, Control.UNBOUND)) { + if (!this.child.parse(state, scope, Control.UNBOUND)) { return null; } - var3 = this.action.run(parseState); + return this.action.run(state); } finally { scope.popFrame(); } - - return var3; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java index 81ad7ecfd..d152ecc89 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java @@ -28,13 +28,13 @@ public final class Scope { this.stack[1] = null; } - private int valueIndex(Atom name) { + private int valueIndex(Atom atom) { for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; + Object key = this.stack[i]; - assert object instanceof Atom; + assert key instanceof Atom; - if (object == name) { + if (key == atom) { return i + 1; } } @@ -42,14 +42,14 @@ public final class Scope { return NOT_FOUND; } - public int valueIndexForAny(Atom... names) { + public int valueIndexForAny(Atom... atoms) { for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; + Object key = this.stack[i]; - assert object instanceof Atom; + assert key instanceof Atom; - for (Atom atom : names) { - if (atom == object) { + for (Atom atom : atoms) { + if (atom == key) { return i + 1; } } @@ -58,15 +58,15 @@ public final class Scope { return NOT_FOUND; } - private void ensureCapacity(int requiredCapacitty) { - int i = this.stack.length; - int i1 = this.topEntryKeyIndex + 1; - int i2 = i1 + requiredCapacitty * 2; - if (i2 >= i) { - int i3 = MiscUtils.growByHalf(i, i2 + 1); - Object[] objects = new Object[i3]; - System.arraycopy(this.stack, 0, objects, 0, i); - this.stack = objects; + private void ensureCapacity(int additionalEntryCount) { + int currentSize = this.stack.length; + int currentLastValueIndex = this.topEntryKeyIndex + 1; + int newLastValueIndex = currentLastValueIndex + additionalEntryCount * 2; + if (newLastValueIndex >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, newLastValueIndex + 1); + Object[] newStack = new Object[newSize]; + System.arraycopy(this.stack, 0, newStack, 0, currentSize); + this.stack = newStack; } assert this.validateStructure(); @@ -86,8 +86,8 @@ public final class Scope { assert this.validateStructure(); } - private int getPreviousMarkerIndex(int markerIndex) { - return (Integer)this.stack[markerIndex + 1]; + private int getPreviousMarkerIndex(int markerKeyIndex) { + return (Integer) this.stack[markerKeyIndex + 1]; } public void popFrame() { @@ -100,25 +100,25 @@ public final class Scope { } public void splitFrame() { - int i = this.topMarkerKeyIndex; - int i1 = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; - this.ensureCapacity(i1 + 1); + int currentFrameMarkerIndex = this.topMarkerKeyIndex; + int nonMarkerEntriesInFrame = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; + this.ensureCapacity(nonMarkerEntriesInFrame + 1); this.setupNewFrame(); - int i2 = i + ENTRY_STRIDE; - int i3 = this.topEntryKeyIndex; + int sourceCursor = currentFrameMarkerIndex + ENTRY_STRIDE; + int targetCursor = this.topEntryKeyIndex; - for (int i4 = 0; i4 < i1; i4++) { - i3 += ENTRY_STRIDE; - Object object = this.stack[i2]; + for (int i = 0; i < nonMarkerEntriesInFrame; i++) { + targetCursor += ENTRY_STRIDE; + Object key = this.stack[sourceCursor]; - assert object != null; + assert key != null; - this.stack[i3] = object; - this.stack[i3 + 1] = null; - i2 += ENTRY_STRIDE; + this.stack[targetCursor] = key; + this.stack[targetCursor + 1] = null; + sourceCursor += ENTRY_STRIDE; } - this.topEntryKeyIndex = i3; + this.topEntryKeyIndex = targetCursor; assert this.validateStructure(); } @@ -135,40 +135,40 @@ public final class Scope { public void mergeFrame() { int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); - int i = previousMarkerIndex; - int i1 = this.topMarkerKeyIndex; + int previousFrameCursor = previousMarkerIndex; + int currentFrameCursor = this.topMarkerKeyIndex; - while (i1 < this.topEntryKeyIndex) { - i += ENTRY_STRIDE; - i1 += ENTRY_STRIDE; - Object object = this.stack[i1]; + while (currentFrameCursor < this.topEntryKeyIndex) { + previousFrameCursor += ENTRY_STRIDE; + currentFrameCursor += ENTRY_STRIDE; + Object newKey = this.stack[currentFrameCursor]; - assert object instanceof Atom; + assert newKey instanceof Atom; - Object object1 = this.stack[i1 + 1]; - Object object2 = this.stack[i]; - if (object2 != object) { - this.stack[i] = object; - this.stack[i + 1] = object1; - } else if (object1 != null) { - this.stack[i + 1] = object1; + Object newValue = this.stack[currentFrameCursor + 1]; + Object oldKey = this.stack[previousFrameCursor]; + if (oldKey != newKey) { + this.stack[previousFrameCursor] = newKey; + this.stack[previousFrameCursor + 1] = newValue; + } else if (newValue != null) { + this.stack[previousFrameCursor + 1] = newValue; } } - this.topEntryKeyIndex = i; + this.topEntryKeyIndex = previousFrameCursor; this.topMarkerKeyIndex = previousMarkerIndex; assert this.validateStructure(); } - public void put(Atom atom, @Nullable T value) { - int i = this.valueIndex(atom); - if (i != NOT_FOUND) { - this.stack[i] = value; + public void put(Atom name, @Nullable T value) { + int valueIndex = this.valueIndex(name); + if (valueIndex != NOT_FOUND) { + this.stack[valueIndex] = value; } else { this.ensureCapacity(1); this.topEntryKeyIndex += ENTRY_STRIDE; - this.stack[this.topEntryKeyIndex] = atom; + this.stack[this.topEntryKeyIndex] = name; this.stack[this.topEntryKeyIndex + 1] = value; } @@ -176,77 +176,75 @@ public final class Scope { } @Nullable - public T get(Atom atom) { - int i = this.valueIndex(atom); - return (T)(i != NOT_FOUND ? this.stack[i] : null); + public T get(Atom name) { + int valueIndex = this.valueIndex(name); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); } - public T getOrThrow(Atom atom) { - int i = this.valueIndex(atom); - if (i == NOT_FOUND) { - throw new IllegalArgumentException("No value for atom " + atom); - } else { - return (T)this.stack[i]; + public T getOrThrow(Atom name) { + int valueIndex = this.valueIndex(name); + if (valueIndex == NOT_FOUND) { + throw new IllegalArgumentException("No value for atom " + name); } + return (T) this.stack[valueIndex]; } - public T getOrDefault(Atom atom, T defaultValue) { - int i = this.valueIndex(atom); - return (T)(i != NOT_FOUND ? this.stack[i] : defaultValue); + public T getOrDefault(Atom name, T fallback) { + int valueIndex = this.valueIndex(name); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : fallback); } @Nullable @SafeVarargs - public final T getAny(Atom... atoms) { - int i = this.valueIndexForAny(atoms); - return (T)(i != NOT_FOUND ? this.stack[i] : null); + public final T getAny(Atom... names) { + int valueIndex = this.valueIndexForAny(names); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); } @SafeVarargs - public final T getAnyOrThrow(Atom... atoms) { - int i = this.valueIndexForAny(atoms); - if (i == NOT_FOUND) { - throw new IllegalArgumentException("No value for atoms " + Arrays.toString(atoms)); - } else { - return (T)this.stack[i]; + public final T getAnyOrThrow(Atom... names) { + int valueIndex = this.valueIndexForAny(names); + if (valueIndex == NOT_FOUND) { + throw new IllegalArgumentException("No value for atoms " + Arrays.toString(names)); } + return (T) this.stack[valueIndex]; } @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder(); - boolean flag = true; + StringBuilder result = new StringBuilder(); + boolean afterFrame = true; for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { - Object object = this.stack[i]; - Object object1 = this.stack[i + 1]; - if (object == FRAME_START_MARKER) { - stringBuilder.append('|'); - flag = true; + Object key = this.stack[i]; + Object value = this.stack[i + 1]; + if (key == FRAME_START_MARKER) { + result.append('|'); + afterFrame = true; } else { - if (!flag) { - stringBuilder.append(','); + if (!afterFrame) { + result.append(','); } - flag = false; - stringBuilder.append(object).append(':').append(object1); + afterFrame = false; + result.append(key).append(':').append(value); } } - return stringBuilder.toString(); + return result.toString(); } @VisibleForTesting public Map, ?> lastFrame() { - HashMap, Object> map = new HashMap<>(); + HashMap, Object> result = new HashMap<>(); for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; - Object object1 = this.stack[i + 1]; - map.put((Atom)object, object1); + Object key = this.stack[i]; + Object value = this.stack[i + 1]; + result.put((Atom) key, value); } - return map; + return result; } public boolean hasOnlySingleFrame() { @@ -258,9 +256,8 @@ public final class Scope { if (this.stack[0] != FRAME_START_MARKER) { throw new IllegalStateException("Corrupted stack"); - } else { - return true; } + return true; } private boolean validateStructure() { @@ -285,14 +282,15 @@ public final class Scope { return true; } - @SuppressWarnings({"unchecked","rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes"}) public static Term increaseDepth() { class IncreasingDepthTerm implements Term { public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm(); + @Override - public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + public boolean parse(final ParseState state, final Scope scope, final Control control) { if (++scope.depth > 512) { - parseState.errorCollector().store(parseState.mark(), new IllegalStateException("Too deep")); + state.errorCollector().store(state.mark(), new IllegalStateException("Too deep")); return false; } return true; @@ -301,12 +299,13 @@ public final class Scope { return (Term) IncreasingDepthTerm.INSTANCE; } - @SuppressWarnings({"unchecked","rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes"}) public static Term decreaseDepth() { class DecreasingDepthTerm implements Term { public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm(); + @Override - public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + public boolean parse(final ParseState state, final Scope scope, final Control control) { scope.depth--; return true; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java index ed88ccd42..30c32ac7c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java @@ -3,9 +3,9 @@ package net.momirealms.craftengine.core.util.snbt.parse; import java.util.stream.Stream; public interface SuggestionSupplier { - Stream possibleValues(ParseState parseState); + Stream possibleValues(ParseState state); static SuggestionSupplier empty() { - return parseState -> Stream.empty(); + return state -> Stream.empty(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java index ceab78ef0..383602b0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java @@ -4,20 +4,20 @@ import java.util.ArrayList; import java.util.List; public interface Term { - boolean parse(ParseState parseState, Scope scope, Control control); + boolean parse(ParseState state, Scope scope, Control control); static Term marker(Atom name, T value) { return new Marker<>(name, value); } @SafeVarargs - static Term sequence(Term... elements) { - return new Sequence<>(elements); + static Term sequence(Term... terms) { + return new Sequence<>(terms); } @SafeVarargs - static Term alternative(Term... elements) { - return new Alternative<>(elements); + static Term alternative(Term... terms) { + return new Alternative<>(terms); } static Term optional(Term term) { @@ -32,8 +32,8 @@ public interface Term { return new Repeated<>(element, listName, minRepetitions); } - static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator) { - return repeatedWithTrailingSeparator(element, listName, seperator, 0); + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term separator) { + return repeatedWithTrailingSeparator(element, listName, separator, 0); } static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator, int minRepetitions) { @@ -47,7 +47,7 @@ public interface Term { static Term cut() { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { control.cut(); return true; } @@ -62,7 +62,7 @@ public interface Term { static Term empty() { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { return true; } @@ -73,11 +73,11 @@ public interface Term { }; } - static Term fail(final Object reason) { + static Term fail(final Object message) { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - parseState.errorCollector().store(parseState.mark(), reason); + public boolean parse(ParseState state, Scope scope, Control control) { + state.errorCollector().store(state.mark(), message); return false; } @@ -90,22 +90,22 @@ public interface Term { record Alternative(Term[] elements) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - Control control1 = parseState.acquireControl(); + public boolean parse(ParseState state, Scope scope, Control control) { + Control controlForThis = state.acquireControl(); try { - int i = parseState.mark(); + int mark = state.mark(); scope.splitFrame(); - for (Term term : this.elements) { - if (term.parse(parseState, scope, control1)) { + for (Term element : this.elements) { + if (element.parse(state, scope, controlForThis)) { scope.mergeFrame(); return true; } scope.clearFrameValues(); - parseState.restore(i); - if (control1.hasCut()) { + state.restore(mark); + if (controlForThis.hasCut()) { break; } } @@ -113,24 +113,24 @@ public interface Term { scope.popFrame(); return false; } finally { - parseState.releaseControl(); + state.releaseControl(); } } } record LookAhead(Term term, boolean positive) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - boolean flag = this.term.parse(parseState.silent(), scope, control); - parseState.restore(i); - return this.positive == flag; + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + boolean result = this.term.parse(state.silent(), scope, control); + state.restore(mark); + return this.positive == result; } } record Marker(Atom name, T value) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { scope.put(this.name, this.value); return true; } @@ -138,10 +138,10 @@ public interface Term { record Maybe(Term term) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - if (!this.term.parse(parseState, scope, control)) { - parseState.restore(i); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + if (!this.term.parse(state, scope, control)) { + state.restore(mark); } return true; @@ -150,25 +150,24 @@ public interface Term { record Repeated(NamedRule element, Atom> listName, int minRepetitions) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - List list = new ArrayList<>(this.minRepetitions); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + List elements = new ArrayList<>(this.minRepetitions); while (true) { - int i1 = parseState.mark(); - T object = parseState.parse(this.element); - if (object == null) { - parseState.restore(i1); - if (list.size() < this.minRepetitions) { - parseState.restore(i); + int entryMark = state.mark(); + T parsedElement = state.parse(this.element); + if (parsedElement == null) { + state.restore(entryMark); + if (elements.size() < this.minRepetitions) { + state.restore(mark); return false; - } else { - scope.put(this.listName, list); - return true; } + scope.put(this.listName, elements); + return true; } - list.add(object); + elements.add(parsedElement); } } } @@ -177,56 +176,55 @@ public interface Term { NamedRule element, Atom> listName, Term separator, int minRepetitions, boolean allowTrailingSeparator ) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - List list = new ArrayList<>(this.minRepetitions); - boolean flag = true; + public boolean parse(ParseState state, Scope scope, Control control) { + int listMark = state.mark(); + List elements = new ArrayList<>(this.minRepetitions); + boolean first = true; while (true) { - int i1 = parseState.mark(); - if (!flag && !this.separator.parse(parseState, scope, control)) { - parseState.restore(i1); + int markBeforeSeparator = state.mark(); + if (!first && !this.separator.parse(state, scope, control)) { + state.restore(markBeforeSeparator); break; } - int i2 = parseState.mark(); - T object = parseState.parse(this.element); - if (object == null) { - if (flag) { - parseState.restore(i2); + int markAfterSeparator = state.mark(); + T parsedElement = state.parse(this.element); + if (parsedElement == null) { + if (first) { + state.restore(markAfterSeparator); } else { if (!this.allowTrailingSeparator) { - parseState.restore(i); + state.restore(listMark); return false; } - parseState.restore(i2); + state.restore(markAfterSeparator); } break; } - list.add(object); - flag = false; + elements.add(parsedElement); + first = false; } - if (list.size() < this.minRepetitions) { - parseState.restore(i); + if (elements.size() < this.minRepetitions) { + state.restore(listMark); return false; - } else { - scope.put(this.listName, list); - return true; } + scope.put(this.listName, elements); + return true; } } record Sequence(Term[] elements) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); - for (Term term : this.elements) { - if (!term.parse(parseState, scope, control)) { - parseState.restore(i); + for (Term element : this.elements) { + if (!element.parse(state, scope, control)) { + state.restore(mark); return false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java index 2f13a7448..e93117570 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java @@ -13,41 +13,39 @@ public record Grammar(Dictionary rules, NamedRule parse(ParseState parseState) { - return parseState.parseTopRule(this.top); + public Optional parse(ParseState state) { + return state.parseTopRule(this.top); } public T parse(StringReader reader) throws CommandSyntaxException { - ErrorCollector.LongestOnly longestOnly = new ErrorCollector.LongestOnly<>(); - StringReaderParserState stringReaderParserState = new StringReaderParserState(longestOnly, reader); - Optional optional = this.parse(stringReaderParserState); - if (optional.isPresent()) { - T result = optional.get(); + ErrorCollector.LongestOnly errorCollector = new ErrorCollector.LongestOnly<>(); + StringReaderParserState stringReaderParserState = new StringReaderParserState(errorCollector, reader); + Optional optionalResult = this.parse(stringReaderParserState); + if (optionalResult.isPresent()) { + T result = optionalResult.get(); if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) { result = null; } return result; - } else { - List> list = longestOnly.entries(); - List list1 = list.stream().mapMulti((errorEntry, consumer) -> { - if (errorEntry.reason() instanceof DelayedException delayedException) { - consumer.accept(delayedException.create(reader.getString(), errorEntry.cursor())); - } else if (errorEntry.reason() instanceof Exception exception1) { - consumer.accept(exception1); - } - }).toList(); - - for (Exception exception : list1) { - if (exception instanceof CommandSyntaxException commandSyntaxException) { - throw commandSyntaxException; - } + } + List> errorEntries = errorCollector.entries(); + List exceptions = errorEntries.stream().mapMulti((entry, output) -> { + if (entry.reason() instanceof DelayedException delayedException) { + output.accept(delayedException.create(reader.getString(), entry.cursor())); + } else if (entry.reason() instanceof Exception exception1) { + output.accept(exception1); } + }).toList(); - if (list1.size() == 1 && list1.getFirst() instanceof RuntimeException runtimeException) { - throw runtimeException; - } else { - throw new IllegalStateException("Failed to parse: " + list.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); + for (Exception exception : exceptions) { + if (exception instanceof CommandSyntaxException cse) { + throw cse; } } + + if (exceptions.size() == 1 && exceptions.getFirst() instanceof RuntimeException re) { + throw re; + } + throw new IllegalStateException("Failed to parse: " + errorEntries.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java index 9d5e07b91..58bf9d797 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java @@ -19,16 +19,15 @@ public final class GreedyPatternParseRule implements Rule } @Override - public String parse(ParseState parseState) { - StringReader stringReader = parseState.input(); - String string = stringReader.getString(); - Matcher matcher = this.pattern.matcher(string).region(stringReader.getCursor(), string.length()); + public String parse(ParseState state) { + StringReader input = state.input(); + String fullString = input.getString(); + Matcher matcher = this.pattern.matcher(fullString).region(input.getCursor(), fullString.length()); if (!matcher.lookingAt()) { - parseState.errorCollector().store(parseState.mark(), this.error); + state.errorCollector().store(state.mark(), this.error); return null; - } else { - stringReader.setCursor(matcher.end()); - return matcher.group(0); } + input.setCursor(matcher.end()); + return matcher.group(0); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java index d3c8d8870..3144dcc27 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java @@ -25,24 +25,23 @@ public abstract class GreedyPredicateParseRule implements Rule parseState) { - StringReader stringReader = parseState.input(); - String string = stringReader.getString(); - int cursor = stringReader.getCursor(); - int i = cursor; + public String parse(ParseState state) { + StringReader input = state.input(); + String fullString = input.getString(); + int start = input.getCursor(); + int pos = start; - while (i < string.length() && this.isAccepted(string.charAt(i)) && i - cursor < this.maxSize) { - i++; + while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos)) && pos - start < this.maxSize) { + pos++; } - int i1 = i - cursor; - if (i1 < this.minSize) { - parseState.errorCollector().store(parseState.mark(), this.error); + int length = pos - start; + if (length < this.minSize) { + state.errorCollector().store(state.mark(), this.error); return null; - } else { - stringReader.setCursor(i); - return string.substring(cursor, i); } + input.setCursor(pos); + return fullString.substring(start, pos); } protected abstract boolean isAccepted(char c); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java index 71a919544..dd4e6249b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java @@ -19,28 +19,27 @@ public abstract class NumberRunParseRule implements Rule { @Nullable @Override - public String parse(ParseState parseState) { - StringReader stringReader = parseState.input(); - stringReader.skipWhitespace(); - String string = stringReader.getString(); - int cursor = stringReader.getCursor(); - int i = cursor; + public String parse(ParseState state) { + StringReader input = state.input(); + input.skipWhitespace(); + String fullString = input.getString(); + int start = input.getCursor(); + int pos = start; - while (i < string.length() && this.isAccepted(string.charAt(i))) { - i++; + while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos))) { + pos++; } - int i1 = i - cursor; - if (i1 == 0) { - parseState.errorCollector().store(parseState.mark(), this.noValueError); - return null; - } else if (string.charAt(cursor) != '_' && string.charAt(i - 1) != '_') { - stringReader.setCursor(i); - return string.substring(cursor, i); - } else { - parseState.errorCollector().store(parseState.mark(), this.underscoreNotAllowedError); + int length = pos - start; + if (length == 0) { + state.errorCollector().store(state.mark(), this.noValueError); return null; + } else if (fullString.charAt(start) != '_' && fullString.charAt(pos - 1) != '_') { + input.setCursor(pos); + return fullString.substring(start, pos); } + state.errorCollector().store(state.mark(), this.underscoreNotAllowedError); + return null; } protected abstract boolean isAccepted(char c); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java index 9bf0a87b9..d076fafd4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java @@ -23,7 +23,7 @@ public class StringReaderParserState extends CachedParseState { } @Override - public void restore(int cursor) { - this.input.setCursor(cursor); + public void restore(int mark) { + this.input.setCursor(mark); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java index 12cb7def3..14fe3f3aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java @@ -16,50 +16,49 @@ public interface StringReaderTerms { static Term character(final char value) { return new TerminalCharacters(CharList.of(value)) { @Override - protected boolean isAccepted(char c) { - return value == c; + protected boolean isAccepted(char v) { + return value == v; } }; } - static Term characters(final char value1, final char value2) { - return new TerminalCharacters(CharList.of(value1, value2)) { + static Term characters(final char v1, final char v2) { + return new TerminalCharacters(CharList.of(v1, v2)) { @Override - protected boolean isAccepted(char c) { - return c == value1 || c == value2; + protected boolean isAccepted(char v) { + return v == v1 || v == v2; } }; } - static StringReader createReader(String input, int cursor) { - StringReader stringReader = new StringReader(input); - stringReader.setCursor(cursor); - return stringReader; + static StringReader createReader(String contents, int cursor) { + StringReader reader = new StringReader(contents); + reader.setCursor(cursor); + return reader; } abstract class TerminalCharacters implements Term { private final DelayedException error; private final SuggestionSupplier suggestions; - public TerminalCharacters(CharList characters) { - String string = characters.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); - this.error = DelayedException.create(LITERAL_INCORRECT, string); - this.suggestions = parseState -> characters.intStream().mapToObj(Character::toString); + public TerminalCharacters(CharList values) { + String joinedValues = values.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); + this.error = DelayedException.create(LITERAL_INCORRECT, joinedValues); + this.suggestions = s -> values.intStream().mapToObj(Character::toString); } @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - parseState.input().skipWhitespace(); - int i = parseState.mark(); - if (parseState.input().canRead() && this.isAccepted(parseState.input().read())) { + public boolean parse(ParseState state, Scope scope, Control control) { + state.input().skipWhitespace(); + int cursor = state.mark(); + if (state.input().canRead() && this.isAccepted(state.input().read())) { return true; - } else { - parseState.errorCollector().store(i, this.suggestions, this.error); - return false; } + state.errorCollector().store(cursor, this.suggestions, this.error); + return false; } - protected abstract boolean isAccepted(char c); + protected abstract boolean isAccepted(char value); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java index 1909633c8..2d46092b7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java @@ -19,15 +19,14 @@ public class UnquotedStringParseRule implements Rule { @Nullable @Override - public String parse(ParseState parseState) { - parseState.input().skipWhitespace(); - int i = parseState.mark(); - String unquotedString = parseState.input().readUnquotedString(); - if (unquotedString.length() < this.minSize) { - parseState.errorCollector().store(i, this.error); + public String parse(ParseState state) { + state.input().skipWhitespace(); + int cursor = state.mark(); + String value = state.input().readUnquotedString(); + if (value.length() < this.minSize) { + state.errorCollector().store(cursor, this.error); return null; - } else { - return unquotedString; } + return value; } } From edfdf083e062c2d9f62a44a64dfbb3056ffe6f06 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 07:57:46 +0800 Subject: [PATCH 09/46] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 556 +++++++++++------- .../LocalizedCommandSyntaxException.java | 2 +- 2 files changed, 334 insertions(+), 224 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 74512176c..3dfa6cbde 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -24,7 +24,7 @@ import java.util.stream.LongStream; public class SnbtGrammar { private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( - number -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(number)) + message -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(message)) ); static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) @@ -248,152 +248,151 @@ public class SnbtGrammar { T emptyList = ops.emptyList(); T nullString = ops.createString("null"); boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + Dictionary rules = new Dictionary<>(); + Atom sign = Atom.of("sign"); rules.put( sign, - Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) + ), scope -> scope.getOrThrow(sign) ); + Atom integerSuffix = Atom.of("integer_suffix"); rules.put( integerSuffix, - Term.alternative( - Term.sequence( - StringReaderTerms.characters('u', 'U'), - Term.alternative( + Term.alternative( Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + StringReaderTerms.characters('u', 'U'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG))) + ) ), Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + StringReaderTerms.characters('s', 'S'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG))) + ) ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) - ) - ) + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.alternative( - Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) - ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) - ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) - ) - ) - ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) - ), scope -> scope.getOrThrow(integerSuffix) ); + Atom binaryNumeral = Atom.of("binary_numeral"); rules.put(binaryNumeral, BINARY_NUMERAL); + Atom decimalNumeral = Atom.of("decimal_numeral"); rules.put(decimalNumeral, DECIMAL_NUMERAL); + Atom hexNumeral = Atom.of("hex_numeral"); rules.put(hexNumeral, HEX_NUMERAL); + Atom integerLiteral = Atom.of("integer_literal"); NamedRule integerLiteralRule = rules.put( integerLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - StringReaderTerms.character('0'), - Term.cut(), + Term.sequence( + Term.optional(rules.named(sign)), Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), - Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), - Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(decimalNumeral, "0") - ) - ), - rules.named(decimalNumeral) + Term.sequence( + StringReaderTerms.character('0'), + Term.cut(), + Term.alternative( + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), + Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), + Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(decimalNumeral, "0") + ) + ), + rules.named(decimalNumeral) + ), + Term.optional(rules.named(integerSuffix)) ), - Term.optional(rules.named(integerSuffix)) - ), - scope -> { - IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); - Sign signValue = scope.getOrDefault(sign, Sign.PLUS); - String decimalContents = scope.get(decimalNumeral); - if (decimalContents != null) { - return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + scope -> { + IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); + Sign signValue = scope.getOrDefault(sign, Sign.PLUS); + String decimalContents = scope.get(decimalNumeral); + if (decimalContents != null) { + return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + } + String hexContents = scope.get(hexNumeral); + if (hexContents != null) { + return new IntegerLiteral(signValue, Base.HEX, hexContents, suffix); + } + String binaryContents = scope.getOrThrow(binaryNumeral); + return new IntegerLiteral(signValue, Base.BINARY, binaryContents, suffix); } - String string1 = scope.get(hexNumeral); - if (string1 != null) { - return new IntegerLiteral(signValue, Base.HEX, string1, suffix); - } - String string2 = scope.getOrThrow(binaryNumeral); - return new IntegerLiteral(signValue, Base.BINARY, string2, suffix); - } ); + Atom floatTypeSuffix = Atom.of("float_type_suffix"); rules.put( floatTypeSuffix, - Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) + ), scope -> scope.getOrThrow(floatTypeSuffix) ); + Atom> floatExponentPart = Atom.of("float_exponent_part"); rules.put( floatExponentPart, - Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(rules.named(sign)), rules.named(decimalNumeral)), + Term.sequence( + StringReaderTerms.characters('e', 'E'), + Term.optional(rules.named(sign)), + rules.named(decimalNumeral) + ), scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); + Atom floatWholePart = Atom.of("float_whole_part"); Atom floatFractionPart = Atom.of("float_fraction_part"); Atom floatLiteral = Atom.of("float_literal"); rules.putComplex( floatLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - StringReaderTerms.character('.'), - Term.cut(), - Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - StringReaderTerms.character('.'), - Term.cut(), - rules.namedWithAlias(decimalNumeral, floatFractionPart), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), rules.named(floatExponentPart), Term.cut(), Term.optional(rules.named(floatTypeSuffix))), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), Term.optional(rules.named(floatExponentPart)), rules.named(floatTypeSuffix)) - ) - ), + Term.sequence( + Term.optional(rules.named(sign)), + Term.alternative( + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + StringReaderTerms.character('.'), + Term.cut(), + Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + StringReaderTerms.character('.'), + Term.cut(), + rules.namedWithAlias(decimalNumeral, floatFractionPart), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + rules.named(floatExponentPart), + Term.cut(), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + Term.optional(rules.named(floatExponentPart)), + rules.named(floatTypeSuffix) + ) + ) + ), state -> { Scope scope = state.scope(); Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); @@ -402,116 +401,160 @@ public class SnbtGrammar { Signed exponent = scope.get(floatExponentPart); TypeSuffix typeSuffix = scope.get(floatTypeSuffix); return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); - } + } ); + Atom stringHex2 = Atom.of("string_hex_2"); rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + Atom stringHex4 = Atom.of("string_hex_4"); rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + Atom stringHex8 = Atom.of("string_hex_8"); rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + Atom stringUnicodeName = Atom.of("string_unicode_name"); rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom stringEscapeSequence = Atom.of("string_escape_sequence"); rules.putComplex( stringEscapeSequence, - Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), - Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), - Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), - Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), - Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), rules.named(stringUnicodeName), StringReaderTerms.character('}')) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), + Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), + Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), + Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), + Term.sequence( + StringReaderTerms.character('N'), + StringReaderTerms.character('{'), + rules.named(stringUnicodeName), + StringReaderTerms.character('}') + ) + ), state -> { Scope scope = state.scope(); String plainEscape = scope.getAny(stringEscapeSequence); if (plainEscape != null) { return plainEscape; - } + } String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); if (hexEscape != null) { int codePoint = HexFormat.fromHexDigits(hexEscape); if (!Character.isValidCodePoint(codePoint)) { - state.errorCollector() - .store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); - return null; - } + state.errorCollector().store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); + return null; + } return Character.toString(codePoint); - } + } String character = scope.getOrThrow(stringUnicodeName); int codePoint; - try { - codePoint = Character.codePointOf(character); - } catch (IllegalArgumentException var12x) { - state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); - return null; - } + try { + codePoint = Character.codePointOf(character); + } catch (IllegalArgumentException var12x) { + state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); + return null; + } return Character.toString(codePoint); - } + } ); + Atom stringPlainContents = Atom.of("string_plain_contents"); rules.put(stringPlainContents, PLAIN_STRING_CHUNK); + Atom> stringChunks = Atom.of("string_chunks"); Atom stringContents = Atom.of("string_contents"); Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); - NamedRule namedRule1 = rules.put( + NamedRule singleQuotedStringChunkRule = rules.put( singleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) + ), scope -> scope.getOrThrow(stringContents) ); Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); - rules.put(singleQuotedStringContents, Term.repeated(namedRule1, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + singleQuotedStringContents, + Term.repeated(singleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); - NamedRule namedRule2 = rules.put( + NamedRule doubleQuotedStringChunkRule = rules.put( doubleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) + ), scope -> scope.getOrThrow(stringContents) ); Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); - rules.put(doubleQuotedStringContents, Term.repeated(namedRule2, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + doubleQuotedStringContents, + Term.repeated(doubleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom quotedStringLiteral = Atom.of("quoted_string_literal"); rules.put( quotedStringLiteral, - Term.alternative( - Term.sequence( - StringReaderTerms.character('"'), Term.cut(), Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), StringReaderTerms.character('"') + Term.alternative( + Term.sequence( + StringReaderTerms.character('"'), + Term.cut(), + Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), + StringReaderTerms.character('"') + ), + Term.sequence( + StringReaderTerms.character('\''), + Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), + StringReaderTerms.character('\'') + ) ), - Term.sequence(StringReaderTerms.character('\''), Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), StringReaderTerms.character('\'')) - ), scope -> scope.getOrThrow(stringContents) ); + Atom unquotedString = Atom.of("unquoted_string"); - rules.put(unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + rules.put( + unquotedString, + new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) + ); + Atom literal = Atom.of("literal"); Atom> argumentList = Atom.of("arguments"); rules.put( - argumentList, Term.repeatedWithTrailingSeparator(rules.forward(literal), argumentList, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(argumentList) + argumentList, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + argumentList, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(argumentList) ); + Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); rules.putComplex( unquotedStringOrBuiltIn, - Term.sequence( - rules.named(unquotedString), - Term.optional(Term.sequence(StringReaderTerms.character('('), rules.named(argumentList), StringReaderTerms.character(')'))) - ), + Term.sequence( + rules.named(unquotedString), + Term.optional(Term.sequence( + StringReaderTerms.character('('), + rules.named(argumentList), + StringReaderTerms.character(')') + )) + ), state -> { Scope scope = state.scope(); String contents = scope.getOrThrow(unquotedString); @@ -522,114 +565,192 @@ public class SnbtGrammar { SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); if (operation != null) { return operation.run(ops, arguments, state); - } + } state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); - return null; + return null; } else if (contents.equalsIgnoreCase("true")) { return trueValue; } else if (contents.equalsIgnoreCase("false")) { return falseValue; } else if (contents.equalsIgnoreCase("null")) { - return Objects.requireNonNullElseGet(ops.empty(), () -> { - if (isJavaType) { - return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; - } - return nullString; - }); - } + return Objects.requireNonNullElseGet(ops.empty(), () -> { + if (isJavaType) { + return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; + } + return nullString; + }); + } return ops.createString(contents); - } + } state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); - return null; - } + return null; + } ); + Atom mapKey = Atom.of("map_key"); - rules.put(mapKey, Term.alternative(rules.named(quotedStringLiteral), rules.named(unquotedString)), scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString)); + rules.put( + mapKey, + Term.alternative( + rules.named(quotedStringLiteral), + rules.named(unquotedString) + ), + scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) + ); + Atom> mapEntry = Atom.of("map_entry"); NamedRule> mapEntryRule = rules.putComplex( - mapEntry, Term.sequence(rules.named(mapKey), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), rules.named(literal)), state -> { + mapEntry, + Term.sequence( + rules.named(mapKey), + StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), + rules.named(literal) + ), + state -> { Scope scope = state.scope(); String key = scope.getOrThrow(mapKey); if (key.isEmpty()) { state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); - return null; - } + return null; + } T value = scope.getOrThrow(literal); return Map.entry(key, value); - } + } ); + Atom>> mapEntries = Atom.of("map_entries"); - rules.put(mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(mapEntries)); + rules.put( + mapEntries, + Term.repeatedWithTrailingSeparator( + mapEntryRule, + mapEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(mapEntries) + ); + Atom mapLiteral = Atom.of("map_literal"); - rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { - List> entries = scope.getOrThrow(mapEntries); - if (entries.isEmpty()) { - return emptyMapValue; - } - Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + rules.put( + mapLiteral, + Term.sequence( + StringReaderTerms.character('{'), + Scope.increaseDepth(), + rules.named(mapEntries), + Scope.decreaseDepth(), + StringReaderTerms.character('}') + ), + scope -> { + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; + } + Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + for (Entry e : entries) { + builder.put(ops.createString(e.getKey()), e.getValue()); + } + return ops.createMap(builder.buildKeepingLast()); + } + ); - for (Entry e : entries) { - builder.put(ops.createString(e.getKey()), e.getValue()); - } - - return ops.createMap(builder.buildKeepingLast()); - }); Atom> listEntries = Atom.of("list_entries"); rules.put( - listEntries, Term.repeatedWithTrailingSeparator(rules.forward(literal), listEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(listEntries) + listEntries, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + listEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(listEntries) ); + Atom arrayPrefix = Atom.of("array_prefix"); rules.put( arrayPrefix, - Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) + ), scope -> scope.getOrThrow(arrayPrefix) ); + Atom> intArrayEntries = Atom.of("int_array_entries"); - rules.put(intArrayEntries, Term.repeatedWithTrailingSeparator(integerLiteralRule, intArrayEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(intArrayEntries)); + rules.put( + intArrayEntries, + Term.repeatedWithTrailingSeparator( + integerLiteralRule, + intArrayEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(intArrayEntries) + ); + Atom listLiteral = Atom.of("list_literal"); rules.putComplex( listLiteral, - Term.sequence( - StringReaderTerms.character('['), - Scope.increaseDepth(), - Term.alternative(Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries)), - Scope.decreaseDepth(), - StringReaderTerms.character(']') - ), + Term.sequence( + StringReaderTerms.character('['), + Scope.increaseDepth(), + Term.alternative( + Term.sequence( + rules.named(arrayPrefix), + StringReaderTerms.character(';'), + rules.named(intArrayEntries) + ), + rules.named(listEntries) + ), + Scope.decreaseDepth(), + StringReaderTerms.character(']') + ), state -> { Scope scope = state.scope(); ArrayPrefix arrayType = scope.get(arrayPrefix); if (arrayType != null) { List entries = scope.getOrThrow(intArrayEntries); return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); - } + } List entries = scope.getOrThrow(listEntries); return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); - } + } ); + NamedRule literalRule = rules.putComplex( literal, - Term.alternative( - Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(rules.namedWithAlias(floatLiteral, literal), rules.named(integerLiteral))), - Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), rules.named(quotedStringLiteral)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), rules.namedWithAlias(mapLiteral, literal)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), rules.namedWithAlias(listLiteral, literal)), - rules.namedWithAlias(unquotedStringOrBuiltIn, literal) - ), + Term.alternative( + Term.sequence( + Term.positiveLookahead(NUMBER_LOOKEAHEAD), + Term.alternative( + rules.namedWithAlias(floatLiteral, literal), + rules.named(integerLiteral) + ) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), + Term.cut(), + rules.named(quotedStringLiteral) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('{')), + Term.cut(), + rules.namedWithAlias(mapLiteral, literal) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('[')), + Term.cut(), + rules.namedWithAlias(listLiteral, literal) + ), + rules.namedWithAlias(unquotedStringOrBuiltIn, literal) + ), state -> { Scope scope = state.scope(); String quotedString = scope.get(quotedStringLiteral); if (quotedString != null) { return ops.createString(quotedString); - } + } IntegerLiteral integer = scope.get(integerLiteral); return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); - } + } ); + return new Grammar<>(rules, literalRule); } @@ -646,16 +767,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { ByteList result = new ByteArrayList(); - for (IntegerLiteral entry : entries) { Number number = this.buildNumber(entry, state); if (number == null) { return null; } - result.add(number.byteValue()); } - return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); } }, @@ -669,16 +787,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.intValue()); } - return ops.createIntList(result.build()); } }, @@ -692,16 +807,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.longValue()); } - return ops.createLongList(result.build()); } }; @@ -717,7 +829,6 @@ public class SnbtGrammar { public boolean isAllowed(TypeSuffix type) { return type == this.defaultType || this.additionalTypes.contains(type); } - public abstract T create(DynamicOps ops); @Nullable @@ -742,7 +853,6 @@ public class SnbtGrammar { return !this.isAllowed(type) ? null : type; } } - enum Base { BINARY, DECIMAL, diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java index cb4912898..21204ec40 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java @@ -10,7 +10,7 @@ import java.util.Optional; import java.util.function.Supplier; public class LocalizedCommandSyntaxException extends CommandSyntaxException { - public static final int CONTEXT_AMOUNT = 10; + public static final int CONTEXT_AMOUNT = 50; public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error"; public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here"; private final Message message; From 392bd814e3584e666c07d8a4b65d6a8a219c7e2f Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 08:48:32 +0800 Subject: [PATCH 10/46] =?UTF-8?q?=E5=85=88=E5=86=99=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 67 ++++++++++++++----- .../core/util/snbt/SnbtOperations.java | 3 +- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 3dfa6cbde..c61899b05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -246,11 +246,12 @@ public class SnbtGrammar { T falseValue = ops.createBoolean(false); T emptyMapValue = ops.emptyMap(); T emptyList = ops.emptyList(); - T nullString = ops.createString("null"); - boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + T nullString = ops.createString(SnbtOperations.BUILTIN_NULL); + boolean isJavaType = SnbtOperations.BUILTIN_NULL.equals(nullString); // 确定是 Java 类型的 Dictionary rules = new Dictionary<>(); + // 符号解析规则 Atom sign = Atom.of("sign"); rules.put( sign, @@ -261,6 +262,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(sign) ); + // 整数后缀解析规则 Atom integerSuffix = Atom.of("integer_suffix"); rules.put( integerSuffix, @@ -291,15 +293,19 @@ public class SnbtGrammar { scope -> scope.getOrThrow(integerSuffix) ); + // 二进制解析规则 Atom binaryNumeral = Atom.of("binary_numeral"); rules.put(binaryNumeral, BINARY_NUMERAL); + // 十进制解析规则 Atom decimalNumeral = Atom.of("decimal_numeral"); rules.put(decimalNumeral, DECIMAL_NUMERAL); + // 十六进制解析规则 Atom hexNumeral = Atom.of("hex_numeral"); rules.put(hexNumeral, HEX_NUMERAL); + // 整数常量解析规则 Atom integerLiteral = Atom.of("integer_literal"); NamedRule integerLiteralRule = rules.put( integerLiteral, @@ -336,6 +342,7 @@ public class SnbtGrammar { } ); + // 浮点型后缀解析规则 Atom floatTypeSuffix = Atom.of("float_type_suffix"); rules.put( floatTypeSuffix, @@ -346,6 +353,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(floatTypeSuffix) ); + // 浮点数指数部分解析规则 Atom> floatExponentPart = Atom.of("float_exponent_part"); rules.put( floatExponentPart, @@ -357,8 +365,9 @@ public class SnbtGrammar { scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); - Atom floatWholePart = Atom.of("float_whole_part"); - Atom floatFractionPart = Atom.of("float_fraction_part"); + // 浮点数常量解析规则 + Atom floatWholePart = Atom.of("float_whole_part"); // 整数部分 + Atom floatFractionPart = Atom.of("float_fraction_part"); // 小数部分 Atom floatLiteral = Atom.of("float_literal"); rules.putComplex( floatLiteral, @@ -404,18 +413,23 @@ public class SnbtGrammar { } ); + // 二位十六进制字符串解析规则 Atom stringHex2 = Atom.of("string_hex_2"); rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + // 四位十六进制字符串解析规则 Atom stringHex4 = Atom.of("string_hex_4"); rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + // 八位十六进制字符串解析规则 Atom stringHex8 = Atom.of("string_hex_8"); rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + // Unicode名称字符串解析规则 Atom stringUnicodeName = Atom.of("string_unicode_name"); rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + // 字符串转义序列解析规则 Atom stringEscapeSequence = Atom.of("string_escape_sequence"); rules.putComplex( stringEscapeSequence, @@ -468,11 +482,15 @@ public class SnbtGrammar { } ); + // 纯文本字符串解析规则 Atom stringPlainContents = Atom.of("string_plain_contents"); rules.put(stringPlainContents, PLAIN_STRING_CHUNK); - Atom> stringChunks = Atom.of("string_chunks"); - Atom stringContents = Atom.of("string_contents"); + // 字符串解析规则 + Atom> stringChunks = Atom.of("string_chunks"); // 字符串块 + Atom stringContents = Atom.of("string_contents"); // 字符串内容 + + // 单引号字符串块解析规则 Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); NamedRule singleQuotedStringChunkRule = rules.put( singleQuotedStringChunk, @@ -483,6 +501,8 @@ public class SnbtGrammar { ), scope -> scope.getOrThrow(stringContents) ); + + // 单引号字符串内容解析规则 Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); rules.put( singleQuotedStringContents, @@ -490,6 +510,7 @@ public class SnbtGrammar { scope -> joinList(scope.getOrThrow(stringChunks)) ); + // 双引号字符串块解析规则 Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); NamedRule doubleQuotedStringChunkRule = rules.put( doubleQuotedStringChunk, @@ -500,6 +521,8 @@ public class SnbtGrammar { ), scope -> scope.getOrThrow(stringContents) ); + + // 双引号字符串内容解析规则 Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); rules.put( doubleQuotedStringContents, @@ -507,6 +530,7 @@ public class SnbtGrammar { scope -> joinList(scope.getOrThrow(stringChunks)) ); + // 带引号的字符串字面量解析规则 Atom quotedStringLiteral = Atom.of("quoted_string_literal"); rules.put( quotedStringLiteral, @@ -526,14 +550,16 @@ public class SnbtGrammar { scope -> scope.getOrThrow(stringContents) ); + // 不带引号的字符串解析规则 Atom unquotedString = Atom.of("unquoted_string"); rules.put( unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) ); - Atom literal = Atom.of("literal"); - Atom> argumentList = Atom.of("arguments"); + // 列表解析规则 + Atom literal = Atom.of("literal"); // 字面量 + Atom> argumentList = Atom.of("arguments"); // 参数 rules.put( argumentList, Term.repeatedWithTrailingSeparator( @@ -544,6 +570,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(argumentList) ); + // 不带引号的字符串或内置表达式解析规则 Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); rules.putComplex( unquotedStringOrBuiltIn, @@ -558,9 +585,9 @@ public class SnbtGrammar { state -> { Scope scope = state.scope(); String contents = scope.getOrThrow(unquotedString); - if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { + if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { // 非空且是合法开头字符 List arguments = scope.get(argumentList); - if (arguments != null) { + if (arguments != null) { // 带参数尝试解析内置表达式 SnbtOperations.BuiltinKey key = new SnbtOperations.BuiltinKey(contents, arguments.size()); SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); if (operation != null) { @@ -568,16 +595,17 @@ public class SnbtGrammar { } state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); return null; - } else if (contents.equalsIgnoreCase("true")) { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_TRUE)) { // 解析不带引号的 true 为布尔值 return trueValue; - } else if (contents.equalsIgnoreCase("false")) { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_FALSE)) { // 解析不带引号的 false 为布尔值 return falseValue; - } else if (contents.equalsIgnoreCase("null")) { - return Objects.requireNonNullElseGet(ops.empty(), () -> { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_NULL)) { // 解析不带引号的 null 为空值,该功能并非标准 SNBT 语法 + return Objects.requireNonNullElseGet(ops.empty() /*一般来说这里都不会是null*/, () -> { if (isJavaType) { + // 如果是 null 一般就是使用 Object 对象的 Java 类型,可以安全的强行转换 return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; } - return nullString; + return nullString; // 意外情况保持 SNBT 默认行为 }); } return ops.createString(contents); @@ -587,6 +615,7 @@ public class SnbtGrammar { } ); + // 映射键解析规则 Atom mapKey = Atom.of("map_key"); rules.put( mapKey, @@ -597,6 +626,7 @@ public class SnbtGrammar { scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) ); + // 映射条目解析规则 Atom> mapEntry = Atom.of("map_entry"); NamedRule> mapEntryRule = rules.putComplex( mapEntry, @@ -617,6 +647,7 @@ public class SnbtGrammar { } ); + // 映射条目集合解析规则 Atom>> mapEntries = Atom.of("map_entries"); rules.put( mapEntries, @@ -628,6 +659,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(mapEntries) ); + // 映射字面量解析规则 Atom mapLiteral = Atom.of("map_literal"); rules.put( mapLiteral, @@ -651,6 +683,7 @@ public class SnbtGrammar { } ); + // 列表条目集合解析规则 Atom> listEntries = Atom.of("list_entries"); rules.put( listEntries, @@ -662,6 +695,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(listEntries) ); + // 数组前缀解析规则 Atom arrayPrefix = Atom.of("array_prefix"); rules.put( arrayPrefix, @@ -673,6 +707,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(arrayPrefix) ); + // 整数数组条目集合解析规则 Atom> intArrayEntries = Atom.of("int_array_entries"); rules.put( intArrayEntries, @@ -684,6 +719,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(intArrayEntries) ); + // 列表字面量解析规则 Atom listLiteral = Atom.of("list_literal"); rules.putComplex( listLiteral, @@ -713,6 +749,7 @@ public class SnbtGrammar { } ); + // 基本规则解析规则 NamedRule literalRule = rules.putComplex( literal, Term.alternative( diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java index 589426cad..53a6fa7f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -23,6 +23,7 @@ public class SnbtOperations { ); public static final String BUILTIN_TRUE = "true"; public static final String BUILTIN_FALSE = "false"; + public static final String BUILTIN_NULL = "null"; public static final Map BUILTIN_OPERATIONS = Map.of( new BuiltinKey("bool", 1), new BuiltinOperation() { @Override @@ -67,7 +68,7 @@ public class SnbtOperations { ); public static final SuggestionSupplier BUILTIN_IDS = new SuggestionSupplier<>() { private final Set keys = Stream.concat( - Stream.of(BUILTIN_FALSE, BUILTIN_TRUE), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) + Stream.of(BUILTIN_FALSE, BUILTIN_TRUE, BUILTIN_NULL), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) ) .collect(Collectors.toSet()); From 5d9ede392adcae03f6a3a5e8e2d627845c2772bc Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 08:50:15 +0800 Subject: [PATCH 11/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=BD=9C=E5=9C=A8?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index c61899b05..0ff4982f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -10,9 +10,11 @@ import com.mojang.serialization.JavaOps; import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteList; import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.snbt.parse.*; import net.momirealms.craftengine.core.util.snbt.parse.Dictionary; import net.momirealms.craftengine.core.util.snbt.parse.grammar.*; +import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; import javax.annotation.Nullable; import java.nio.ByteBuffer; @@ -878,7 +880,7 @@ public class SnbtGrammar { state.errorCollector().store(state.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); return null; } - return (Number) entry.create(JavaOps.INSTANCE, actualType, state); + return (Number) entry.create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE, actualType, state); } @Nullable From ac48881ae029bbca6e319e99bbe8e9c5a7c74e8a Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 10:47:27 +0800 Subject: [PATCH 12/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E6=8A=9B=E5=87=BA=E6=9C=AA=E8=AF=BB=E5=AE=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/zh_cn.yml | 18 +++++++------- .../item/modifier/ComponentsModifier.java | 2 +- .../craftengine/core/util/snbt/TagParser.java | 24 +++++++++++++------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 160e44580..bd6ace4e4 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,22 +88,22 @@ warning.config.type.vector3f: "在文件 发现问题 - 无法 warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" -warning.config.type.snbt.invalid_syntax.parse_error: ", 位于第 个字符: " +warning.config.type.snbt.invalid_syntax.parse_error: ",位于第个字符:" warning.config.type.snbt.invalid_syntax.here: "<--[此处]" -warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效 UUID 的字符串" +warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效UUID的字符串" warning.config.type.snbt.parser.expected_number_or_boolean: "应为数字或布尔型" warning.config.type.snbt.parser.trailing: "多余的尾随数据" warning.config.type.snbt.parser.expected.compound: "应为复合标签" -warning.config.type.snbt.parser.number_parse_failure: "解析数字失败: " -warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为 " -warning.config.type.snbt.parser.invalid_codepoint: "无效的 Unicode 字符码位: " +warning.config.type.snbt.parser.number_parse_failure: "解析数字失败:" +warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为" +warning.config.type.snbt.parser.invalid_codepoint: "无效的Unicode字符码位:" warning.config.type.snbt.parser.no_such_operation: "不存在的操作: " warning.config.type.snbt.parser.expected_integer_type: "应为整数" warning.config.type.snbt.parser.expected_float_type: "应为浮点数" warning.config.type.snbt.parser.expected_non_negative_number: "应为非负数" -warning.config.type.snbt.parser.invalid_character_name: "无效的 Unicode 字符名称" +warning.config.type.snbt.parser.invalid_character_name: "无效的Unicode字符名称" warning.config.type.snbt.parser.invalid_array_element_type: "无效的数组元素类型" -warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字 0-9、+ 或 - 开头" +warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字0-9、+或-开头" warning.config.type.snbt.parser.expected_unquoted_string: "应为有效的无引号字符串" warning.config.type.snbt.parser.invalid_string_contents: "无效的字符串内容" warning.config.type.snbt.parser.expected_binary_numeral: "应为二进制数" @@ -111,9 +111,9 @@ warning.config.type.snbt.parser.underscore_not_allowed: "数字的开头 warning.config.type.snbt.parser.expected_decimal_numeral: "应为十进制数" warning.config.type.snbt.parser.expected_hex_numeral: "应为十六进制数" warning.config.type.snbt.parser.empty_key: "键不能为空" -warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以 0 开头" +warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以0开头" warning.config.type.snbt.parser.infinity_not_allowed: "不允许使用非有限数的数值" -warning.config.type.snbt.parser.incorrect: "应为字面量 " +warning.config.type.snbt.parser.incorrect: "应为字面量" warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" warning.config.number.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的数字类型 ''" warning.config.number.missing_argument: "在文件 发现问题 - 配置项 '' 缺少数字参数" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index 113f33324..e5f551690 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -46,7 +46,7 @@ public class ComponentsModifier implements ItemDataModifier { } else if (string.startsWith("(snbt) ")) { String snbt = string.substring("(snbt) ".length()); try { - return TagParser.parseCompoundFully(snbt); + return TagParser.parseTagFully(snbt); } catch (CommandSyntaxException e) { throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java index d399e6151..f963c635a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -15,6 +15,7 @@ import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; import net.momirealms.sparrow.nbt.codec.NBTOps; +@SuppressWarnings("unused") public class TagParser { public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType( new LocalizedMessage("warning.config.type.snbt.parser.trailing") @@ -24,8 +25,8 @@ public class TagParser { ); public static final char ELEMENT_SEPARATOR = ','; public static final char NAME_VALUE_SEPARATOR = ':'; - private static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); - private static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); + public static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); + public static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); private final DynamicOps ops; private final Grammar grammar; @@ -48,15 +49,20 @@ public class TagParser { } throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); } - public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException { StringReader reader = new StringReader(input); - return parseCompoundAsArgument(reader); + Tag result = NBT_OPS_PARSER.parseFully(reader); + return castToCompoundOrThrow(reader, result); + } + + public static Tag parseTagFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return NBT_OPS_PARSER.parseFully(reader); } public static Object parseObjectFully(String input) throws CommandSyntaxException { StringReader reader = new StringReader(input); - return parseObjectAsArgument(reader); + return JAVA_OPS_PARSER.parseFully(reader); } public T parseFully(String input) throws CommandSyntaxException { @@ -77,8 +83,12 @@ public class TagParser { } public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException { - Tag tag = NBT_OPS_PARSER.parseAsArgument(reader); - return castToCompoundOrThrow(reader, tag); + Tag result = parseTagAsArgument(reader); + return castToCompoundOrThrow(reader, result); + } + + public static Tag parseTagAsArgument(StringReader reader) throws CommandSyntaxException { + return NBT_OPS_PARSER.parseAsArgument(reader); } public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException { From 02630c835057f5948948778f1bd480a4fe1f011e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 15:49:58 +0800 Subject: [PATCH 13/46] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=B8=8D=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 3 ++- .../momirealms/craftengine/core/pack/host/impl/SelfHost.java | 2 +- .../craftengine/core/pack/host/impl/SelfHostHttpServer.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 95815b4c5..4050974da 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -198,7 +198,8 @@ resource-pack: # Generates a single-use, time-limited download link for each player. one-time-token: true # Enhances validation for deny-non-minecraft-request and one-time-token. - strict-validation: true + # Do not enable this on an offline server + strict-validation: false rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads max-bandwidth-per-second: 5_000_000 # 5MB/s diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java index 93cede575..9d08e7c78 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -77,7 +77,7 @@ public class SelfHost implements ResourcePackHost { boolean oneTimeToken = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("one-time-token", true), "one-time-token"); String protocol = arguments.getOrDefault("protocol", "http").toString(); boolean denyNonMinecraftRequest = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("deny-non-minecraft-request", true), "deny-non-minecraft-request"); - boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", true), "strict-validation"); + boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", false), "strict-validation"); Bandwidth limit = null; Map rateLimitingSection = ResourceConfigUtils.getAsMapOrNull(arguments.get("rate-limiting"), "rate-limiting"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index da20a14e3..39961f3aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -69,7 +69,7 @@ public class SelfHostHttpServer { private String url; private boolean denyNonMinecraft = true; private boolean useToken; - private boolean strictValidation = true; + private boolean strictValidation = false; private long globalUploadRateLimit = 0; private long minDownloadSpeed = 50_000; @@ -234,7 +234,7 @@ public class SelfHostHttpServer { boolean nonMinecraftClient = userAgent == null || !userAgent.startsWith("Minecraft Java/"); if (strictValidation && !nonMinecraftClient) { String clientVersion = request.headers().get("X-Minecraft-Version"); - nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring(15)); + nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring("Minecraft Java/".length())); } if (nonMinecraftClient) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid client"); From 6f1e730d032ea5654a35b5a229071f8cc9c51ea6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 16:09:10 +0800 Subject: [PATCH 14/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 3 +-- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 4050974da..294a644e2 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -197,8 +197,7 @@ resource-pack: deny-non-minecraft-request: true # Generates a single-use, time-limited download link for each player. one-time-token: true - # Enhances validation for deny-non-minecraft-request and one-time-token. - # Do not enable this on an offline server + # Improved validation for deny-non-minecraft-request and one-time-token; do not enable on offline servers or validation will fail. strict-validation: false rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads diff --git a/gradle.properties b/gradle.properties index 46db05f12..a8e0f9e03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.15 +project_version=0.0.65.15.1 config_version=60 lang_version=41 project_group=net.momirealms From f0f2181a186552a7a532bc1e812d5739d128e73a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 16:45:26 +0800 Subject: [PATCH 15/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E5=89=94=E9=99=A4=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/command/BukkitCommandManager.java | 1 + .../SetEntityViewDistanceScaleCommand.java | 10 +++ .../feature/ToggleEntityCullingCommand.java | 57 +++++++++++++++ .../plugin/user/BukkitServerPlayer.java | 73 ++++++++++++------- common-files/src/main/resources/commands.yml | 6 ++ .../src/main/resources/translations/en.yml | 5 +- .../core/entity/player/Player.java | 4 + .../core/plugin/locale/MessageConstants.java | 1 + 8 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java index 00dbb694b..fb43eac52 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java @@ -43,6 +43,7 @@ public class BukkitCommandManager extends AbstractCommandManager new TestCommand(this, plugin), new SetLocaleCommand(this, plugin), new SetEntityViewDistanceScaleCommand(this, plugin), + new ToggleEntityCullingCommand(this, plugin), new UnsetLocaleCommand(this, plugin), new DebugGetBlockStateRegistryIdCommand(this, plugin), new DebugGetBlockInternalIdCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index 17d5ca2e3..df4afd16e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -1,12 +1,14 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -27,6 +29,14 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { + if (!Config.enableEntityCulling()) { + context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + return; + } + if (Config.entityCullingViewDistance() <= 0) { + context.sender().sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); + return; + } Player player = context.get("player"); double scale = context.get("scale"); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java new file mode 100644 index 000000000..b46f47699 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.BooleanParser; +import org.incendo.cloud.parser.standard.DoubleParser; + +import java.util.Optional; + +public class ToggleEntityCullingCommand extends BukkitCommandFeature { + + public ToggleEntityCullingCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .flag(FlagKeys.SILENT_FLAG) + .required("player", PlayerParser.playerParser()) + .optional("state", BooleanParser.booleanParser()) + .handler(context -> { + if (!Config.enableEntityCulling()) { + context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + return; + } + Player player = context.get("player"); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Optional state = context.optional("state"); + boolean isEnabled = serverPlayer.enableEntityCulling(); + if (state.isPresent()) { + serverPlayer.setEnableEntityCulling(state.get()); + handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(state.get()), Component.text(player.getName())); + } else { + serverPlayer.setEnableEntityCulling(!isEnabled); + handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(!isEnabled), Component.text(player.getName())); + } + }); + } + + @Override + public String getFeatureID() { + return "toggle_entity_culling"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index fc12b179f..e7bdc3bea 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -74,6 +74,7 @@ import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); + public static final Key ENABLE_ENTITY_CULLING = Key.of("craftengine:enable_entity_culling"); private final BukkitCraftEngine plugin; // connection state @@ -146,6 +147,8 @@ public class BukkitServerPlayer extends Player { private final EntityCulling culling; private Vec3d firstPersonCameraVec3; private Vec3d thirdPersonCameraVec3; + // 是否启用实体剔除 + private boolean enableEntityCulling; // 玩家眼睛所在位置 private Location eyeLocation; @@ -174,6 +177,7 @@ public class BukkitServerPlayer extends Player { byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); + this.enableEntityCulling = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN)).orElse(true); this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); this.selectedLocale = TranslationManager.parseLocale(locale); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(512, 0.5f); @@ -597,38 +601,44 @@ public class BukkitServerPlayer extends Player { public void entityCullingTick() { this.culling.restoreTokenOnTick(); boolean useRayTracing = Config.entityCullingRayTracing(); - for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - CullingData cullingData = cullableObject.cullable.cullingData(); - if (cullingData != null) { - boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); - // 之前可见 - if (cullableObject.isShown) { - boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); - if (!firstPersonVisible && !thirdPersonVisible) { - cullableObject.setShown(this, false); + if (this.enableEntityCulling) { + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); + // 之前可见 + if (cullableObject.isShown) { + boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); + if (!firstPersonVisible && !thirdPersonVisible) { + cullableObject.setShown(this, false); + } } - } - // 之前不可见 - else { - // 但是第一人称可见了 - if (firstPersonVisible) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + // 之前不可见 + else { + // 但是第一人称可见了 + if (firstPersonVisible) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); continue; } - cullableObject.setShown(this, true); - continue; - } - if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; + if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); } - cullableObject.setShown(this, true); + // 仍然不可见 } - // 仍然不可见 + } else { + cullableObject.setShown(this, true); } - } else { + } + } else { + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { cullableObject.setShown(this, true); } } @@ -1341,6 +1351,17 @@ public class BukkitServerPlayer extends Player { platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); } + @Override + public void setEnableEntityCulling(boolean enable) { + this.enableEntityCulling = enable; + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN, enable); + } + + @Override + public boolean enableEntityCulling() { + return enableEntityCulling; + } + @Override public void giveExperiencePoints(int xpPoints) { platformPlayer().giveExp(xpPoints); diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index ffd2eadee..41fbb153b 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -135,6 +135,12 @@ set_entity_view_distance_scale: usage: - /ce feature entity-view-distance-scale set +toggle_entity_culling: + enable: true + permission: ce.command.admin.toggle_entity_culling + usage: + - /ce feature toggle-entity-culling + # Debug commands debug_set_block: enable: true diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..c7e538f21 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -72,9 +72,10 @@ command.upload.on_progress: "Started uploading progress. Check the consol command.send_resource_pack.success.single: "Sent resource pack to ." command.send_resource_pack.success.multiple: "Send resource packs to players." command.locale.set.failure: "Invalid locale format: " -command.locale.set.success: "Updated selected locale to for " +command.locale.set.success: "Selected locale has been set to for " command.locale.unset.success: "Cleared selected locale for " -command.entity_view_distance_scale.set.success: "Updated entity view distance scale to for " +command.entity_view_distance_scale.set.success: "Entity view distance scale updated to for " +command.entity_culling.toggle.success: "Entity culling status updated to for " warning.network.resource_pack.unverified_uuid: "Player is attempting to request a resource pack using a UUID () that is not authenticated by the server." warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." warning.config.yaml.duplicated_key: "Issue found in file - Found duplicated key '' at line , this might cause unexpected results." diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 847fe6d1c..689374c51 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -193,6 +193,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setEntityCullingViewDistanceScale(double value); + public abstract void setEnableEntityCulling(boolean enable); + + public abstract boolean enableEntityCulling(); + public abstract void giveExperiencePoints(int xpPoints); public abstract void giveExperienceLevels(int levels); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java index 81174c5d0..f8cfbd946 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java @@ -42,4 +42,5 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_SINGLE = Component.translatable().key("command.item.clear.test.single"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_MULTIPLE = Component.translatable().key("command.item.clear.test.multiple"); TranslatableComponent.Builder COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.entity_view_distance_scale.set.success"); + TranslatableComponent.Builder COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS = Component.translatable().key("command.entity_culling.toggle.success"); } From d2b794c70d8d6dba3769607f20660fc37dfcfe01 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 01:48:44 +0800 Subject: [PATCH 16/46] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84part1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 53 +- .../event/AsyncResourcePackCacheEvent.java | 2 +- .../api/event/FurnitureAttemptPlaceEvent.java | 8 +- .../api/event/FurnitureInteractEvent.java | 2 +- .../behavior/NearLiquidBlockBehavior.java | 2 +- .../block/behavior/OnLiquidBlockBehavior.java | 2 +- .../ItemDisplayBlockEntityElementConfig.java | 9 +- .../TextDisplayBlockEntityElementConfig.java | 2 +- .../bukkit/entity/BukkitItemEntity.java | 2 +- .../entity/data/ItemDisplayEntityData.java | 2 +- .../furniture/BukkitCustomFurniture.java | 74 --- .../entity/furniture/BukkitFurniture.java | 363 +++++--------- .../furniture/BukkitFurnitureElement.java | 177 ------- .../furniture/BukkitFurnitureManager.java | 452 +++++++++--------- .../furniture/FurnitureEventListener.java | 158 +++--- .../entity/furniture/ItemColorSource.java | 6 + .../BukkitFurnitureElementConfigs.java | 15 + .../element/ItemDisplayFurnitureElement.java | 22 + .../ItemDisplayFurnitureElementConfig.java | 169 +++++++ .../furniture/hitbox/BukkitHitBoxTypes.java | 2 +- .../furniture/hitbox/CustomHitBoxConfig.java | 60 +-- .../hitbox/HappyGhastHitBoxConfig.java | 3 +- .../hitbox/InteractionHitBoxConfig.java | 3 +- .../furniture/hitbox/ShulkerHitBoxConfig.java | 3 +- .../item/behavior/FurnitureItemBehavior.java | 259 +++++----- .../bukkit/plugin/BukkitCraftEngine.java | 2 + .../bukkit/plugin/BukkitPlatform.java | 4 +- .../command/feature/DebugItemDataCommand.java | 2 +- .../feature/DebugSpawnFurnitureCommand.java | 10 +- .../feature/ToggleEntityCullingCommand.java | 1 - .../plugin/network/BukkitNetworkManager.java | 16 +- .../handler/ProjectilePacketHandler.java | 2 +- .../plugin/network/payload/PayloadHelper.java | 4 +- .../default/configuration/furniture/bench.yml | 5 +- .../src/main/resources/translations/de.yml | 4 +- .../src/main/resources/translations/en.yml | 5 +- .../src/main/resources/translations/es.yml | 4 +- .../src/main/resources/translations/fr_fr.yml | 4 +- .../src/main/resources/translations/ru_ru.yml | 4 +- .../src/main/resources/translations/tr.yml | 4 +- .../src/main/resources/translations/zh_cn.yml | 4 +- .../core/block/AbstractBlockManager.java | 8 +- .../craftengine/core/block/BlockBehavior.java | 2 +- .../craftengine/core/block/BlockSettings.java | 23 +- .../behavior/IsPathFindableBlockBehavior.java | 2 +- .../core/block/entity/BlockEntity.java | 2 +- .../block/entity/BlockEntityTypeKeys.java | 1 + .../core/block/entity/BlockEntityTypes.java | 1 + .../block/entity/InactiveBlockEntity.java | 21 + .../craftengine/core/entity/CustomEntity.java | 96 ++++ .../core/entity/CustomEntityType.java | 14 + .../core/entity/CustomEntityTypeKeys.java | 10 + .../core/entity/CustomEntityTypes.java | 18 + .../core/entity/InactiveCustomEntity.java | 54 +++ .../core/entity/{ => display}/Billboard.java | 2 +- .../{ => display}/ItemDisplayContext.java | 2 +- .../furniture/AbstractCustomFurniture.java | 89 ---- .../furniture/AbstractFurnitureElement.java | 92 ---- .../furniture/AbstractFurnitureManager.java | 124 ++--- .../core/entity/furniture/AnchorType.java | 1 + .../entity/furniture/CustomFurniture.java | 60 --- .../core/entity/furniture/Furniture.java | 54 +-- .../entity/furniture/FurnitureConfig.java | 67 +++ .../entity/furniture/FurnitureConfigImpl.java | 145 ++++++ ...raData.java => FurnitureDataAccessor.java} | 95 ++-- .../entity/furniture/FurnitureElement.java | 59 --- .../entity/furniture/FurnitureManager.java | 6 +- .../entity/furniture/FurnitureSettings.java | 25 +- .../entity/furniture/FurnitureVariant.java | 13 + .../core/entity/furniture/HitBoxConfig.java | 34 -- .../behavior/EmptyFurnitureBehavior.java | 7 + .../furniture/behavior/FurnitureBehavior.java | 15 + .../furniture/element/FurnitureElement.java | 14 + .../element/FurnitureElementConfig.java | 9 + .../FurnitureElementConfigFactory.java | 8 + .../element/FurnitureElementConfigs.java | 31 ++ .../furniture/hitbox/AbstractHitBox.java | 26 +- .../{ => hitbox}/AbstractHitBoxConfig.java | 8 +- .../entity/furniture/{ => hitbox}/HitBox.java | 2 +- .../entity/furniture/hitbox/HitBoxConfig.java | 20 + .../{ => hitbox}/HitBoxConfigFactory.java | 2 +- .../furniture/{ => hitbox}/HitBoxPart.java | 2 +- .../furniture/{ => hitbox}/HitBoxTypes.java | 3 +- .../furniture/tick/FurnitureTicker.java | 8 + .../core/entity/{ => item}/ItemEntity.java | 2 +- .../entity/projectile/ProjectileMeta.java | 4 +- .../craftengine/core/item/Item.java | 2 +- .../craftengine/core/item/ItemSettings.java | 10 +- .../recipe/CustomSmithingTransformRecipe.java | 2 +- .../item/recipe/CustomSmithingTrimRecipe.java | 2 +- .../function/ApplyBonusCountFunction.java | 4 +- .../core/pack/model/ItemModels.java | 2 +- .../model/condition/ConditionProperties.java | 2 +- .../RangeDispatchProperties.java | 2 +- .../pack/model/select/SelectProperties.java | 2 +- .../pack/model/special/SignSpecialModel.java | 2 +- .../pack/model/special/SpecialModels.java | 2 +- .../core/pack/model/tint/Tints.java | 2 +- .../core/pack/obfuscation/ObfA.java | 2 +- .../plugin/command/sender/AbstractSender.java | 2 +- .../core/plugin/config/Config.java | 4 +- .../plugin/config/StringKeyConstructor.java | 10 +- .../argument/ExpressionTemplateArgument.java | 2 +- .../template/argument/TemplateArguments.java | 2 +- .../context/function/DamageFunction.java | 2 +- .../context/function/OpenWindowFunction.java | 2 +- .../function/RemoveFurnitureFunction.java | 2 +- .../function/ReplaceFurnitureFunction.java | 2 +- .../function/SpawnFurnitureFunction.java | 31 +- .../context/function/ToastFunction.java | 2 +- .../parameter/EntityParameterProvider.java | 2 +- .../core/plugin/locale/LangData.java | 2 +- .../core/registry/BuiltInRegistries.java | 8 +- .../craftengine/core/registry/Registries.java | 6 +- .../craftengine/core/sound/SoundData.java | 2 +- .../craftengine/core/util/Pair.java | 8 +- .../core/util/ReflectionUtils.java | 2 +- .../core/util/ResourceConfigUtils.java | 110 +++-- .../craftengine/core/util/SNBTReader.java | 2 +- .../craftengine/core/util/Tuple.java | 12 +- .../craftengine/core/util/TypeUtils.java | 14 +- .../craftengine/core/world/chunk/CEChunk.java | 34 +- .../DefaultBlockEntitySerializer.java | 7 +- .../serialization/DefaultChunkSerializer.java | 13 +- .../DefaultEntitySerializer.java | 55 +++ 125 files changed, 1827 insertions(+), 1753 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => display}/Billboard.java (80%) rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => display}/ItemDisplayContext.java (87%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{FurnitureExtraData.java => FurnitureDataAccessor.java} (57%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java => core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java (54%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/AbstractHitBoxConfig.java (86%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBox.java (87%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxConfigFactory.java (65%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxPart.java (73%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxTypes.java (91%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => item}/ItemEntity.java (67%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 4a47cd615..8f61765d3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -8,9 +8,8 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; @@ -46,7 +45,7 @@ public final class CraftEngineFurniture { * @return a non-null map containing all loaded custom furniture */ @NotNull - public static Map loadedFurniture() { + public static Map loadedFurniture() { return BukkitFurnitureManager.instance().loadedFurniture(); } @@ -56,7 +55,7 @@ public final class CraftEngineFurniture { * @param id id * @return the custom furniture */ - public static CustomFurniture byId(@NotNull Key id) { + public static FurnitureConfig byId(@NotNull Key id) { return BukkitFurnitureManager.instance().furnitureById(id).orElse(null); } @@ -69,9 +68,11 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture place(Location location, Key furnitureId) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return place(location, furnitureId, furniture.getAnyAnchorType()); + // fixme API +// return place(location, furnitureId, furniture.getAnyAnchorType()); + return null; } /** @@ -79,14 +80,17 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @return the loaded furniture */ @Nullable + @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); + return null; } /** @@ -94,12 +98,15 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) { - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); + @Deprecated(since = "0.0.66", forRemoval = true) + public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType) { + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); + return null; } /** @@ -107,15 +114,18 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @param playSound whether to play place sounds * @return the loaded furniture */ @Nullable + @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); + return null; } /** @@ -123,13 +133,16 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @param playSound whether to play place sounds * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) { - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); + @Deprecated(since = "0.0.66", forRemoval = true) + public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType, boolean playSound) { + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); + return null; } /** @@ -286,7 +299,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!furniture.isValid()) return; - Location location = ((BukkitFurniture) furniture).dropLocation(); + Location location = ((BukkitFurniture) furniture).getDropLocation(); furniture.destroy(); LootTable lootTable = (LootTable) furniture.config().lootTable(); World world = new BukkitWorld(location.getWorld()); @@ -295,7 +308,7 @@ public final class CraftEngineFurniture { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); if (player != null) { Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); builder.withParameter(DirectContextParameters.PLAYER, player) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java index dfa064fa3..62099bba8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java @@ -49,7 +49,7 @@ public final class AsyncResourcePackCacheEvent extends Event { * Adds an external resource pack to the cache. *

* This method accepts either a .zip file or a directory path representing a resource pack. - * The resource pack will be added to the appropriate cache collection based on its type. + * The resource pack will be added to the appropriate cache collection based on its id. *

* * @param path the file system path to the resource pack. Must be either a .zip file or a directory. diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index 6d0a54209..dd1920ca0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); private boolean cancelled; - private final CustomFurniture furniture; + private final FurnitureConfig furniture; private final Location location; private final AnchorType anchorType; private final BlockFace clickedFace; @@ -23,7 +23,7 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can private final InteractionHand hand; public FurnitureAttemptPlaceEvent(@NotNull Player player, - @NotNull CustomFurniture furniture, + @NotNull FurnitureConfig furniture, @NotNull AnchorType anchorType, @NotNull Location location, @NotNull BlockFace clickedFace, @@ -69,7 +69,7 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can } @NotNull - public CustomFurniture furniture() { + public FurnitureConfig furniture() { return furniture; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java index 1e1adcf47..ebe8850fb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.core.entity.furniture.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java index 9c9b02c65..060282f7d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java @@ -45,7 +45,7 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); List positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index e71780c7c..3e8a3fed1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -40,7 +40,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); return new OnLiquidBlockBehavior(block, delay, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java index daa220d7d..dbda0e1e0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java @@ -6,8 +6,8 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; @@ -19,7 +19,6 @@ import org.joml.Vector3f; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -173,8 +172,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"), - ItemDisplayContext.valueOf(arguments.getOrDefault("display-context", "none").toString().toUpperCase(Locale.ROOT)), - Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)), + ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), + ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength") ); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java index 12012724d..dc450d1fe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; -import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.AdventureHelper; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java index 449fc5ebb..7ee3115ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.entity; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.core.entity.ItemEntity; +import net.momirealms.craftengine.core.entity.item.ItemEntity; import org.bukkit.entity.Item; public class BukkitItemEntity extends BukkitEntity implements ItemEntity { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java index 09d39ebdc..091f8654e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java @@ -6,7 +6,7 @@ public class ItemDisplayEntityData extends DisplayEntityData { // Item display only public static final ItemDisplayEntityData DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY); /** - * Display type: + * Display id: * 0 = NONE * 1 = THIRD_PERSON_LEFT_HAND * 2 = THIRD_PERSON_RIGHT_HAND diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java deleted file mode 100644 index dcad38455..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java +++ /dev/null @@ -1,74 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import net.momirealms.craftengine.core.entity.furniture.AbstractCustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureSettings; -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; - -public class BukkitCustomFurniture extends AbstractCustomFurniture { - - protected BukkitCustomFurniture(@NotNull Key id, - @NotNull FurnitureSettings settings, - @NotNull Map placements, - @NotNull Map>> events, - @Nullable LootTable lootTable) { - super(id, settings, placements, events, lootTable); - } - - public static Builder builder() { - return new BuilderImpl(); - } - - public static class BuilderImpl implements Builder { - private Key id; - private Map placements; - private FurnitureSettings settings; - private Map>> events; - private LootTable lootTable; - - @Override - public CustomFurniture build() { - return new BukkitCustomFurniture(id, settings, placements, events, lootTable); - } - - @Override - public Builder id(Key id) { - this.id = id; - return this; - } - - @Override - public Builder placement(Map placements) { - this.placements = placements; - return this; - } - - @Override - public Builder settings(FurnitureSettings settings) { - this.settings = settings; - return this; - } - - @Override - public Builder lootTable(LootTable lootTable) { - this.lootTable = lootTable; - return this; - } - - @Override - public Builder events(Map>> events) { - this.events = events; - return this; - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 37d64bbf6..fe9bec331 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,184 +1,166 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.BukkitEntity; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.*; -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.io.IOException; import java.lang.ref.WeakReference; -import java.util.*; +import java.util.Optional; +import java.util.UUID; public class BukkitFurniture implements Furniture { - private final CustomFurniture furniture; - private final CustomFurniture.Placement placement; - private FurnitureExtraData extraData; - // location - private final Location location; - // base entity - private final WeakReference baseEntity; - private final int baseEntityId; - // colliders - private final Collider[] colliderEntities; - // cache - private final List fakeEntityIds; - private final List entityIds; - private final Map hitBoxes = new Int2ObjectArrayMap<>(); - private final Map hitBoxParts = new Int2ObjectArrayMap<>(); - private final boolean minimized; - private final boolean hasExternalModel; - // cached spawn packet - private Object cachedSpawnPacket; - private Object cachedMinimizedSpawnPacket; + private static final UUID INVALID_UUID = new UUID(0, 0); + @NotNull + private final FurnitureConfig config; + @NotNull + private final FurnitureDataAccessor dataAccessor; - public BukkitFurniture(Entity baseEntity, - CustomFurniture furniture, - FurnitureExtraData extraData) { - this.extraData = extraData; - this.baseEntityId = baseEntity.getEntityId(); - this.location = baseEntity.getLocation(); - this.baseEntity = new WeakReference<>(baseEntity); - this.furniture = furniture; - this.minimized = furniture.settings().minimized(); - this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType)); + private WeakReference baseEntity; + private FurnitureVariant currentVariant; + private Location location; + private boolean valid; + private UUID uuid; + private int entityId; - List fakeEntityIds = new IntArrayList(); - List mainEntityIds = new IntArrayList(); - mainEntityIds.add(this.baseEntityId); + private FurnitureElement[] elements; - // 绑定外部模型 - Optional optionalExternal = placement.externalModel(); - if (optionalExternal.isPresent()) { - try { - optionalExternal.get().bindModel(new BukkitEntity(baseEntity)); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e); - } - this.hasExternalModel = true; - } else { - this.hasExternalModel = false; - } + public BukkitFurniture(@NotNull FurnitureConfig config, + @NotNull CompoundTag data) { + this.dataAccessor = FurnitureDataAccessor.of(data); + this.currentVariant = Optional.ofNullable(getVariant()).orElseGet(config::anyVariant); + this.config = config; + this.baseEntity = new WeakReference<>(null); + this.valid = false; + this.entityId = -1; + this.uuid = INVALID_UUID; + } - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); - List packets = new ArrayList<>(); - List minimizedPackets = new ArrayList<>(); - List colliders = new ArrayList<>(4); + private void sync() { WorldPosition position = position(); - - - // 初始化家具的元素 - for (FurnitureElement element : placement.elements()) { - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - fakeEntityIds.add(entityId); - element.initPackets(this, entityId, conjugated, packet -> { - packets.add(packet); - if (this.minimized) minimizedPackets.add(packet); - }); + FurnitureElementConfig[] elementConfigs = this.currentVariant.elements(); + FurnitureElement[] elements = new FurnitureElement[elementConfigs.length]; + for (int i = 0; i < elementConfigs.length; i++) { + FurnitureElement o = elementConfigs[i].create(position); + elements[i] = o; } + this.elements = elements; + } - // 初始化碰撞箱 - for (HitBoxConfig hitBoxConfig : this.placement.hitBoxConfigs()) { - int[] ids = hitBoxConfig.acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); - List aabbs = new ArrayList<>(); - - hitBoxConfig.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> { - packets.add(packet); - if (this.minimized && !canBeMinimized) { - minimizedPackets.add(packet); - } - }, colliders::add, part -> { - this.hitBoxParts.put(part.entityId(), part); - aabbs.add(part); - }); - - BukkitHitBox hitBox = new BukkitHitBox(this, hitBoxConfig, aabbs.toArray(new HitBoxPart[0])); - for (int entityId : ids) { - fakeEntityIds.add(entityId); - mainEntityIds.add(entityId); - this.hitBoxes.put(entityId, hitBox); - } - } - - // 初始化缓存的家具包 - try { - this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - if (this.minimized) { - this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id(), e); - } - - - this.fakeEntityIds = fakeEntityIds; - this.entityIds = mainEntityIds; - this.colliderEntities = colliders.toArray(new Collider[0]); + public void addToWorld(Location location) { + this.setLocation(location); + this.valid = true; + Entity baseEntity = null; // fixme 处理生成 + this.baseEntity = new WeakReference<>(baseEntity); + this.uuid = baseEntity.getUniqueId(); + this.entityId = baseEntity.getEntityId(); + this.sync(); } @Override - public void initializeColliders() { - Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); - for (Collider entity : this.colliderEntities) { - FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); - Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); - bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); + public void show(Player player) { + for (FurnitureElement element : this.elements) { + element.show(player); } } - @NotNull - public Object spawnPacket(Player player) { - // TODO hasPermission might be slow, can we use a faster way in the future? - // TODO Make it based on conditions. So we can dynamically control which furniture should be sent to the player - if (!this.minimized || player.hasPermission(FurnitureManager.FURNITURE_ADMIN_NODE)) { - return this.cachedSpawnPacket; - } else { - return this.cachedMinimizedSpawnPacket; + @Override + public void hide(Player player) { + for (FurnitureElement element : this.elements) { + element.hide(player); } } + @Nullable + @Override + public CullingData cullingData() { + return this.config.cullingData(); + } + + @NotNull + @Override + public FurnitureConfig config() { + return this.config; + } + + @NotNull + @Override + public FurnitureDataAccessor dataAccessor() { + return this.dataAccessor; + } + @Override public WorldPosition position() { return LocationUtils.toWorldPosition(this.location); } - @NotNull - public Location location() { - return this.location.clone(); - } - - @NotNull - public Entity baseEntity() { - Entity entity = this.baseEntity.get(); - if (entity == null) { - throw new RuntimeException("Base entity not found. It might be unloaded."); - } - return entity; + @Override + public boolean isValid() { + return this.valid; } @Override - public boolean isValid() { - return baseEntity().isValid(); + public void destroy() { + this.valid = false; + Optional.ofNullable(this.baseEntity.get()).ifPresent(Entity::remove); } - @NotNull - public Location dropLocation() { - Optional dropOffset = this.placement.dropOffset(); + @Override + public UUID uuid() { + return this.uuid; + } + + @Override + public int entityId() { + return this.entityId; + } + + public void teleport(WorldPosition position) { + Location newLocation = LocationUtils.toLocation(position); + if (newLocation.equals(this.location)) { + return; + } + this.setLocation(newLocation); + this.sync(); + } + + public Location location() { + return location; + } + + private void setLocation(Location location) { + this.location = location; + } + + public FurnitureVariant getVariant() { + return this.config.getVariant(this.dataAccessor.variant().orElseGet(this.config::anyVariantName)); + } + + public void setVariant(String variant) { + FurnitureVariant newVariant = this.config.getVariant(variant); + if (newVariant != this.currentVariant) { + this.currentVariant = newVariant; + this.sync(); + } + } + + // 获取掉落物的位置,受到家具变种的影响 + public Location getDropLocation() { + Optional dropOffset = this.getVariant().dropOffset(); if (dropOffset.isEmpty()) { return location(); } @@ -186,105 +168,4 @@ public class BukkitFurniture implements Furniture { Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); } - - @Override - public void destroy() { - if (!isValid()) { - return; - } - this.baseEntity().remove(); - this.destroyColliders(); - this.destroySeats(); - } - - @Override - public void destroyColliders() { - for (Collider entity : this.colliderEntities) { - if (entity != null) - entity.destroy(); - } - } - - @Override - public void destroySeats() { - for (HitBox hitBox : this.hitBoxes.values()) { - for (Seat seat : hitBox.seats()) { - seat.destroy(); - } - } - } - - @Override - public UUID uuid() { - return this.baseEntity().getUniqueId(); - } - - @Override - public int baseEntityId() { - return this.baseEntityId; - } - - @NotNull - public List entityIds() { - return Collections.unmodifiableList(this.entityIds); - } - - @NotNull - public List fakeEntityIds() { - return Collections.unmodifiableList(this.fakeEntityIds); - } - - public Collider[] collisionEntities() { - return this.colliderEntities; - } - - @Override - public @Nullable HitBox hitBoxByEntityId(int id) { - return this.hitBoxes.get(id); - } - - @Override - public @Nullable HitBoxPart hitBoxPartByEntityId(int id) { - return this.hitBoxParts.get(id); - } - - @Override - public @NotNull AnchorType anchorType() { - return this.placement.anchorType(); - } - - @Override - public @NotNull Key id() { - return this.furniture.id(); - } - - @Override - public @NotNull CustomFurniture config() { - return this.furniture; - } - - @Override - public boolean hasExternalModel() { - return hasExternalModel; - } - - @Override - public FurnitureExtraData extraData() { - return this.extraData; - } - - @Override - public void setExtraData(FurnitureExtraData extraData) { - this.extraData = extraData; - this.save(); - } - - @Override - public void save() { - try { - this.baseEntity().getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, this.extraData.toBytes()); - } catch (IOException e) { - CraftEngine.instance().logger().warn("Failed to save furniture data.", e); - } - } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java deleted file mode 100644 index 83ca3fa4d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java +++ /dev/null @@ -1,177 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureElement; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.data.FireworkExplosion; -import net.momirealms.craftengine.core.util.Color; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; - -public class BukkitFurnitureElement extends AbstractFurnitureElement { - private final List commonValues; - - public BukkitFurnitureElement(Key item, - Billboard billboard, - ItemDisplayContext transform, - Vector3f scale, - Vector3f translation, - Vector3f position, - Quaternionf rotation, - float shadowRadius, - float shadowStrength, - boolean applyDyedColor) { - super(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor); - this.commonValues = new ArrayList<>(); - ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues); - ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues); - ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues); - ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues); - ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues); - ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(shadowRadius, this.commonValues); - ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(shadowStrength, this.commonValues); - } - - @Override - public void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer packets) { - WorldPosition position = furniture.position(); - Vector3f offset = conjugated.transform(new Vector3f(position())); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(), - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - if (applyDyedColor()) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues( - furniture.extraData().dyedColor().orElse(null), - furniture.extraData().fireworkExplosionColors().orElse(null) - ))); - } else { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(null, null))); - } - } - - private synchronized List getCachedValues(@Nullable Color color, int @Nullable [] colors) { - List cachedValues = new ArrayList<>(this.commonValues); - Item item = BukkitItemManager.instance().createWrappedItem(item(), null); - if (item == null) { - item = BukkitItemManager.instance().wrap(new ItemStack(Material.BARRIER)); - } else { - if (color != null) { - item.dyedColor(color); - } - if (colors != null) { - item.fireworkExplosion(new FireworkExplosion( - FireworkExplosion.Shape.SMALL_BALL, - new IntArrayList(colors), - new IntArrayList(), - false, - false - )); - } - } - ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), cachedValues); - return cachedValues; - } - - public static Builder builder() { - return new BuilderImpl(); - } - - public static class BuilderImpl implements Builder { - private boolean applyDyedColor; - private Key item; - private Billboard billboard; - private ItemDisplayContext transform; - private Vector3f scale; - private Vector3f translation; - private Vector3f position; - private Quaternionf rotation; - private float shadowRadius; - private float shadowStrength; - - @Override - public Builder applyDyedColor(boolean applyDyedColor) { - this.applyDyedColor = applyDyedColor; - return this; - } - - @Override - public Builder item(Key item) { - this.item = item; - return this; - } - - @Override - public Builder billboard(Billboard billboard) { - this.billboard = billboard; - return this; - } - - @Override - public Builder transform(ItemDisplayContext transform) { - this.transform = transform; - return this; - } - - @Override - public Builder scale(Vector3f scale) { - this.scale = scale; - return this; - } - - @Override - public Builder translation(Vector3f translation) { - this.translation = translation; - return this; - } - - @Override - public Builder position(Vector3f position) { - this.position = position; - return this; - } - - @Override - public Builder rotation(Quaternionf rotation) { - this.rotation = rotation; - return this; - } - - @Override - public Builder shadowStrength(float shadowStrength) { - this.shadowStrength = shadowStrength; - return this; - } - - @Override - public Builder shadowRadius(float shadowRadius) { - this.shadowRadius = shadowRadius; - return this; - } - - @Override - public FurnitureElement build() { - return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index f946e4888..969727e89 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -1,34 +1,23 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig; -import net.momirealms.craftengine.bukkit.nms.CollisionEntity; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.sound.SoundData; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.*; -import org.bukkit.entity.*; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Interaction; import org.bukkit.event.HandlerList; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); @@ -56,30 +45,32 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { - return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound); + public Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { +// return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); + return null; } - public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { - Optional optionalAnchorType = extraData.anchorType(); - if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { - extraData.anchorType(furniture.getAnyAnchorType()); - } - Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { - ItemDisplay display = (ItemDisplay) entity; - display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); - try { - display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); - } catch (IOException e) { - this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); - } - handleBaseEntityLoadEarly(display); - }); - if (playSound) { - SoundData data = furniture.settings().sounds().placeSound(); - location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); - } - return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); + public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound) { +// Optional optionalAnchorType = extraData.anchorType(); +// if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { +// extraData.anchorType(furniture.getAnyAnchorType()); +// } +// Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { +// ItemDisplay display = (ItemDisplay) entity; +// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); +// try { +// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); +// } catch (IOException e) { +// this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); +// } +// handleBaseEntityLoadEarly(display); +// }); +// if (playSound) { +// SoundData data = furniture.settings().sounds().placeSound(); +// location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); +// } +// return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); + return null; } @Override @@ -88,34 +79,34 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT; COLLISION_ENTITY_TYPE = Config.colliderType(); Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin()); - if (VersionHelper.isFolia()) { - BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); - for (World world : Bukkit.getWorlds()) { - List entities = world.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay display) { - taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); - } else if (entity instanceof Interaction interaction) { - taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); - } else if (entity instanceof Boat boat) { - taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); - } - } - } - } else { - for (World world : Bukkit.getWorlds()) { - List entities = world.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay display) { - handleBaseEntityLoadEarly(display); - } else if (entity instanceof Interaction interaction) { - handleCollisionEntityLoadOnEntitiesLoad(interaction); - } else if (entity instanceof Boat boat) { - handleCollisionEntityLoadOnEntitiesLoad(boat); - } - } - } - } +// if (VersionHelper.isFolia()) { +// BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); +// for (World world : Bukkit.getWorlds()) { +// List entities = world.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay display) { +// taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); +// } else if (entity instanceof Interaction interaction) { +// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); +// } else if (entity instanceof Boat boat) { +// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); +// } +// } +// } +// } else { +// for (World world : Bukkit.getWorlds()) { +// List entities = world.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay display) { +// handleBaseEntityLoadEarly(display); +// } else if (entity instanceof Interaction interaction) { +// handleCollisionEntityLoadOnEntitiesLoad(interaction); +// } else if (entity instanceof Boat boat) { +// handleCollisionEntityLoadOnEntitiesLoad(boat); +// } +// } +// } +// } } @Override @@ -141,174 +132,165 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return this.furnitureByEntityId.get(entityId); } - @Override - protected CustomFurniture.Builder furnitureBuilder() { - return BukkitCustomFurniture.builder(); - } - - @Override - protected FurnitureElement.Builder furnitureElementBuilder() { - return BukkitFurnitureElement.builder(); - } - - protected void handleBaseEntityUnload(Entity entity) { - int id = entity.getEntityId(); - BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); - if (furniture != null) { - Location location = entity.getLocation(); - boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (!isPreventing) { - furniture.destroySeats(); - } - for (int sub : furniture.entityIds()) { - this.furnitureByEntityId.remove(sub); - } - } - } - - protected void handleCollisionEntityUnload(Entity entity) { - int id = entity.getEntityId(); - this.furnitureByRealEntityId.remove(id); - } - - @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` - protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { - // must be a furniture item - String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); - if (id == null) return; - - Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); - if (optionalFurniture.isEmpty()) return; - - CustomFurniture customFurniture = optionalFurniture.get(); - BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); - if (previous != null) return; - - Location location = display.getLocation(); - boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); - boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (above1_20_1) { - if (!preventChange) { - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - furniture.initializeColliders(); - for (Player player : display.getTrackedPlayers()) { - BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); - } - } - } else { - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - for (Player player : display.getTrackedPlayers()) { - BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); - } - if (preventChange) { - this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - } else { - furniture.initializeColliders(); - } - } - if (depth > 2) return; - this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - } - - protected void handleCollisionEntityLoadLate(Entity entity, int depth) { - // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit - if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { - return; - } - // not a collision entity - Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); - if (flag == null || flag != 1) { - return; - } - - Location location = entity.getLocation(); - World world = location.getWorld(); - int chunkX = location.getBlockX() >> 4; - int chunkZ = location.getBlockZ() >> 4; - if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { - entity.remove(); - return; - } - - if (depth > 2) return; - plugin.scheduler().sync().runLater(() -> { - handleCollisionEntityLoadLate(entity, depth + 1); - }, 1, world, chunkX, chunkZ); - } - - public void handleBaseEntityLoadEarly(ItemDisplay display) { - String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); - if (id == null) return; - // Remove the entity if it's not a valid furniture - if (Config.handleInvalidFurniture()) { - String mapped = Config.furnitureMappings().get(id); - if (mapped != null) { - if (mapped.isEmpty()) { - display.remove(); - return; - } else { - id = mapped; - display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); - } - } - } - - Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); - if (optionalFurniture.isPresent()) { - CustomFurniture customFurniture = optionalFurniture.get(); - BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); - if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - furniture.initializeColliders(); // safely do it here - } - } - - public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { - // faster - if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { - collisionEntity.remove(); - return; - } - - // not a collision entity - Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); - if (flag == null || flag != 1) { - return; - } - - collisionEntity.remove(); - } - - private FurnitureExtraData getFurnitureExtraData(Entity baseEntity) throws IOException { - byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); - if (extraData == null) return FurnitureExtraData.builder().build(); - return FurnitureExtraData.fromBytes(extraData); - } - - private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) { - FurnitureExtraData extraData; - try { - extraData = getFurnitureExtraData(display); - } catch (IOException e) { - extraData = FurnitureExtraData.builder().build(); - plugin.logger().warn("Furniture extra data could not be loaded", e); - } - BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); - this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.entityIds()) { - this.furnitureByEntityId.put(entityId, bukkitFurniture); - } - for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { - int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); - this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); - } - return bukkitFurniture; - } +// +// protected void handleBaseEntityUnload(Entity entity) { +// int id = entity.getEntityId(); +// BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); +// if (furniture != null) { +// Location location = entity.getLocation(); +// boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// if (!isPreventing) { +// furniture.destroySeats(); +// } +// for (int sub : furniture.entityIds()) { +// this.furnitureByEntityId.remove(sub); +// } +// } +// } +// +// protected void handleCollisionEntityUnload(Entity entity) { +// int id = entity.getEntityId(); +// this.furnitureByRealEntityId.remove(id); +// } +// +// @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` +// protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { +// // must be a furniture item +// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); +// if (id == null) return; +// +// Key key = Key.of(id); +// Optional optionalFurniture = furnitureById(key); +// if (optionalFurniture.isEmpty()) return; +// +// FurnitureConfig customFurniture = optionalFurniture.get(); +// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); +// if (previous != null) return; +// +// Location location = display.getLocation(); +// boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); +// boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// if (above1_20_1) { +// if (!preventChange) { +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// furniture.initializeColliders(); +// for (Player player : display.getTrackedPlayers()) { +// BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); +// this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); +// } +// } +// } else { +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// for (Player player : display.getTrackedPlayers()) { +// BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); +// serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); +// this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); +// } +// if (preventChange) { +// this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// } else { +// furniture.initializeColliders(); +// } +// } +// if (depth > 2) return; +// this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// } +// +// protected void handleCollisionEntityLoadLate(Entity entity, int depth) { +// // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit +// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { +// return; +// } +// // not a collision entity +// Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); +// if (flag == null || flag != 1) { +// return; +// } +// +// Location location = entity.getLocation(); +// World world = location.getWorld(); +// int chunkX = location.getBlockX() >> 4; +// int chunkZ = location.getBlockZ() >> 4; +// if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { +// entity.remove(); +// return; +// } +// +// if (depth > 2) return; +// plugin.scheduler().sync().runLater(() -> { +// handleCollisionEntityLoadLate(entity, depth + 1); +// }, 1, world, chunkX, chunkZ); +// } +// +// public void handleBaseEntityLoadEarly(ItemDisplay display) { +// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); +// if (id == null) return; +// // Remove the entity if it's not a valid furniture +// if (Config.handleInvalidFurniture()) { +// String mapped = Config.furnitureMappings().get(id); +// if (mapped != null) { +// if (mapped.isEmpty()) { +// display.remove(); +// return; +// } else { +// id = mapped; +// display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); +// } +// } +// } +// +// Key key = Key.of(id); +// Optional optionalFurniture = furnitureById(key); +// if (optionalFurniture.isPresent()) { +// FurnitureConfig customFurniture = optionalFurniture.get(); +// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); +// if (previous != null) return; +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// furniture.initializeColliders(); // safely do it here +// } +// } +// +// public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { +// // faster +// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { +// collisionEntity.remove(); +// return; +// } +// +// // not a collision entity +// Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); +// if (flag == null || flag != 1) { +// return; +// } +// +// collisionEntity.remove(); +// } +// +// private FurnitureDataAccessor getFurnitureExtraData(Entity baseEntity) throws IOException { +// byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); +// if (extraData == null) return FurnitureDataAccessor.builder().build(); +// return FurnitureDataAccessor.fromBytes(extraData); +// } +// +// private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { +// FurnitureDataAccessor extraData; +// try { +// extraData = getFurnitureExtraData(display); +// } catch (IOException e) { +// extraData = FurnitureDataAccessor.builder().build(); +// plugin.logger().warn("Furniture extra data could not be loaded", e); +// } +// BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); +// this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); +// for (int entityId : bukkitFurniture.entityIds()) { +// this.furnitureByEntityId.put(entityId, bukkitFurniture); +// } +// for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { +// int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); +// this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); +// } +// return bukkitFurniture; +// } @Override protected HitBoxConfig defaultHitBox() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index ce4864b90..5fa382018 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -1,18 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; -import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.EntitiesLoadEvent; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; - -import java.util.List; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; @@ -21,77 +9,77 @@ public class FurnitureEventListener implements Listener { this.manager = manager; } - /* - * Load Entities - */ - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onEntitiesLoadEarly(EntitiesLoadEvent event) { - List entities = event.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadEarly(itemDisplay); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onWorldLoad(WorldLoadEvent event) { - List entities = event.getWorld().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadEarly(itemDisplay); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onEntityLoad(EntityAddToWorldEvent event) { - Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadLate(itemDisplay, 0); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadLate(entity, 0); - } - } - - /* - * Unload Entities - */ - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onChunkUnload(ChunkUnloadEvent event) { - Entity[] entities = event.getChunk().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onWorldUnload(WorldUnloadEvent event) { - List entities = event.getWorld().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onEntityUnload(EntityRemoveFromWorldEvent event) { - Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } +// /* +// * Load Entities +// */ +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onEntitiesLoadEarly(EntitiesLoadEvent event) { +// List entities = event.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadEarly(itemDisplay); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onWorldLoad(WorldLoadEvent event) { +// List entities = event.getWorld().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadEarly(itemDisplay); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onEntityLoad(EntityAddToWorldEvent event) { +// Entity entity = event.getEntity(); +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadLate(itemDisplay, 0); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadLate(entity, 0); +// } +// } +// +// /* +// * Unload Entities +// */ +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onChunkUnload(ChunkUnloadEvent event) { +// Entity[] entities = event.getChunk().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onWorldUnload(WorldUnloadEvent event) { +// List entities = event.getWorld().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onEntityUnload(EntityRemoveFromWorldEvent event) { +// Entity entity = event.getEntity(); +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java new file mode 100644 index 000000000..339dad09d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.bukkit.entity.furniture; + +import net.momirealms.craftengine.core.util.Color; + +public record ItemColorSource(Color dyedColor, int[] fireworkColors) { +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java new file mode 100644 index 000000000..4ce2d57f2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; + +public class BukkitFurnitureElementConfigs extends FurnitureElementConfigs { + + static { + register(ITEM_DISPLAY, ItemDisplayFurnitureElementConfig.FACTORY); + } + + private BukkitFurnitureElementConfigs() {} + + public static void init() { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java new file mode 100644 index 000000000..5c92715cf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.player.Player; + +public class ItemDisplayFurnitureElement implements FurnitureElement { + private final ItemDisplayFurnitureElementConfig config; + + public ItemDisplayFurnitureElement(ItemDisplayFurnitureElementConfig config) { + this.config = config; + } + + @Override + public void show(Player player) { + + } + + @Override + public void hide(Player player) { + + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java new file mode 100644 index 000000000..8edc5d489 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -0,0 +1,169 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; + +public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { + public static final Factory FACTORY = new Factory(); + private final BiFunction> lazyMetadataPacket; + private final BiFunction> item; + private final Vector3f scale; + private final Vector3f position; + private final Vector3f translation; + private final float xRot; + private final float yRot; + private final Quaternionf rotation; + private final ItemDisplayContext displayContext; + private final Billboard billboard; + private final float shadowRadius; + private final float shadowStrength; + private final boolean applyDyedColor; + + public ItemDisplayFurnitureElementConfig(BiFunction> item, + Vector3f scale, + Vector3f position, + Vector3f translation, + float xRot, + float yRot, + Quaternionf rotation, + ItemDisplayContext displayContext, + Billboard billboard, + float shadowRadius, + float shadowStrength, + boolean applyDyedColor) { + this.scale = scale; + this.position = position; + this.translation = translation; + this.xRot = xRot; + this.yRot = yRot; + this.rotation = rotation; + this.displayContext = displayContext; + this.billboard = billboard; + this.shadowRadius = shadowRadius; + this.shadowStrength = shadowStrength; + this.applyDyedColor = applyDyedColor; + this.item = item; + this.lazyMetadataPacket = (player, source) -> { + List dataValues = new ArrayList<>(); + ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player, source).getLiteralObject(), dataValues); + ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); + ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); + ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); + ItemDisplayEntityData.Translation.addEntityData(this.translation, dataValues); + ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); + ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); + ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); + return dataValues; + }; + } + + public Vector3f scale() { + return scale; + } + + public Vector3f position() { + return position; + } + + public Vector3f translation() { + return translation; + } + + public float xRot() { + return xRot; + } + + public float yRot() { + return yRot; + } + + public Quaternionf rotation() { + return rotation; + } + + public ItemDisplayContext displayContext() { + return displayContext; + } + + public Billboard billboard() { + return billboard; + } + + public float shadowRadius() { + return shadowRadius; + } + + public float shadowStrength() { + return shadowStrength; + } + + public boolean applyDyedColor() { + return applyDyedColor; + } + + @Override + public ItemDisplayFurnitureElement create(@NotNull WorldPosition position) { + return new ItemDisplayFurnitureElement(this); + } + + public static class Factory implements FurnitureElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public FurnitureElementConfig create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); + boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); + return (FurnitureElementConfig) new ItemDisplayFurnitureElementConfig( + (player, colorSource) -> { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + }, + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"), + ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), + ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), + applyDyedColor + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java index 4d92bd7c8..017824a2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; public class BukkitHitBoxTypes extends HitBoxTypes { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java index fa8e974a8..2b5abc32e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java @@ -1,42 +1,34 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.entity.EntityType; -import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; +import java.util.Map; public class CustomHitBoxConfig extends AbstractHitBoxConfig { public static final Factory FACTORY = new Factory(); private final float scale; private final EntityType entityType; - private final List cachedValues = new ArrayList<>(); - public CustomHitBoxConfig(SeatConfig[] seats, Vector3f position, EntityType type, float scale, boolean blocksBuilding, boolean canBeHitByProjectile) { + public CustomHitBoxConfig(SeatConfig[] seats, + Vector3f position, + EntityType type, + float scale, + boolean blocksBuilding, + boolean canBeHitByProjectile) { super(seats, position, false, blocksBuilding, canBeHitByProjectile); this.scale = scale; this.entityType = type; - BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues); - BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues); - BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); } public EntityType entityType() { @@ -52,41 +44,13 @@ public class CustomHitBoxConfig extends AbstractHitBoxConfig { return HitBoxTypes.CUSTOM; } - @Override - public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(), - FastNMS.INSTANCE.method$CraftEntityType$toNMSEntityType(this.entityType), 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId[0], Collections.singletonList(attributeInstance)), false); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct custom hitbox spawn packet", e); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - public static class Factory implements HitBoxConfigFactory { @Override public HitBoxConfig create(Map arguments) { Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); - String type = (String) arguments.getOrDefault("entity-type", "slime"); + String type = (String) arguments.getOrDefault("entity-id", "slime"); EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); if (entityType == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java index 5663b9f87..df7d249fe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java @@ -7,7 +7,8 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java index 60bec26d4..0d29c0ad2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java @@ -5,7 +5,8 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java index 57034cc9a..f24f79b10 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java @@ -9,7 +9,8 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeH import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 81df0accb..b92b4440e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -1,43 +1,18 @@ package net.momirealms.craftengine.bukkit.item.behavior; -import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptPlaceEvent; -import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.bukkit.util.EventUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.bukkit.Location; -import org.bukkit.World; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -import java.util.Optional; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); @@ -57,121 +32,121 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { - Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); - if (optionalCustomFurniture.isEmpty()) { - CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); - return InteractionResult.FAIL; - } - CustomFurniture customFurniture = optionalCustomFurniture.get(); - - Direction clickedFace = context.getClickedFace(); - AnchorType anchorType = switch (clickedFace) { - case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; - case UP -> AnchorType.GROUND; - case DOWN -> AnchorType.CEILING; - }; - - CustomFurniture.Placement placement = customFurniture.getPlacement(anchorType); - if (placement == null) { - return InteractionResult.FAIL; - } - - Player player = context.getPlayer(); - // todo adventure check - if (player != null && player.isAdventureMode()) { - return InteractionResult.FAIL; - } - - Vec3d clickedPosition = context.getClickLocation(); - - // trigger event - org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; - World world = (World) context.getLevel().platformWorld(); - - // get position and rotation for placement - Vec3d finalPlacePosition; - double furnitureYaw; - if (anchorType == AnchorType.WALL) { - furnitureYaw = Direction.getYaw(clickedFace); - if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); - finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); - } else { - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); - finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); - } - } else { - furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); - finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); - } - - Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); - - List aabbs = new ArrayList<>(); - for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { - hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); - } - if (!aabbs.isEmpty()) { - if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { - return InteractionResult.FAIL; - } - } - - if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { - return InteractionResult.FAIL; - } - - if (player != null) { - FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), - DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); - if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { - return InteractionResult.FAIL; - } - } - - Item item = context.getItem(); - // 不可能 - if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; - - BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( - furnitureLocation.clone(), customFurniture, - FurnitureExtraData.builder() - .item(item.copyWithCount(1)) - .anchorType(anchorType) - .dyedColor(item.dyedColor().orElse(null)) - .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) - .build(), false); - - if (player != null) { - FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); - if (EventUtils.fireAndCheckCancel(placeEvent)) { - bukkitFurniture.destroy(); - return InteractionResult.FAIL; - } - } - - Cancellable dummy = Cancellable.dummy(); - PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() - .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) - .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) - .withParameter(DirectContextParameters.EVENT, dummy) - .withParameter(DirectContextParameters.HAND, context.getHand()) - .withParameter(DirectContextParameters.ITEM_IN_HAND, item) - ); - customFurniture.execute(functionContext, EventTrigger.PLACE); - if (dummy.isCancelled()) { - return InteractionResult.SUCCESS_AND_CANCEL; - } - - if (player != null) { - if (!player.canInstabuild()) { - item.count(item.count() - 1); - } - player.swingHand(context.getHand()); - } - - context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); +// Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); +// if (optionalCustomFurniture.isEmpty()) { +// CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); +// return InteractionResult.FAIL; +// } +// FurnitureConfig customFurniture = optionalCustomFurniture.get(); +// +// Direction clickedFace = context.getClickedFace(); +// AnchorType anchorType = switch (clickedFace) { +// case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; +// case UP -> AnchorType.GROUND; +// case DOWN -> AnchorType.CEILING; +// }; +// +// FurnitureConfig.Variant placement = customFurniture.getPlacement(anchorType); +// if (placement == null) { +// return InteractionResult.FAIL; +// } +// +// Player player = context.getPlayer(); +// // todo adventure check +// if (player != null && player.isAdventureMode()) { +// return InteractionResult.FAIL; +// } +// +// Vec3d clickedPosition = context.getClickLocation(); +// +// // trigger event +// org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; +// World world = (World) context.getLevel().platformWorld(); +// +// // get position and rotation for placement +// Vec3d finalPlacePosition; +// double furnitureYaw; +// if (anchorType == AnchorType.WALL) { +// furnitureYaw = Direction.getYaw(clickedFace); +// if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); +// finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); +// } else { +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); +// finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); +// } +// } else { +// furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); +// finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); +// } +// +// Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); +// +// List aabbs = new ArrayList<>(); +// for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { +// hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); +// } +// if (!aabbs.isEmpty()) { +// if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { +// return InteractionResult.FAIL; +// } +// } +// +// if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { +// return InteractionResult.FAIL; +// } +// +// if (player != null) { +// FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), +// DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); +// if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { +// return InteractionResult.FAIL; +// } +// } +// +// Item item = context.getItem(); +// // 不可能 +// if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; +// +// BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( +// furnitureLocation.clone(), customFurniture, +// FurnitureDataAccessor.builder() +// .item(item.copyWithCount(1)) +// .anchorType(anchorType) +// .dyedColor(item.dyedColor().orElse(null)) +// .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) +// .build(), false); +// +// if (player != null) { +// FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); +// if (EventUtils.fireAndCheckCancel(placeEvent)) { +// bukkitFurniture.destroy(); +// return InteractionResult.FAIL; +// } +// } +// +// Cancellable dummy = Cancellable.dummy(); +// PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() +// .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) +// .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) +// .withParameter(DirectContextParameters.EVENT, dummy) +// .withParameter(DirectContextParameters.HAND, context.getHand()) +// .withParameter(DirectContextParameters.ITEM_IN_HAND, item) +// ); +// customFurniture.execute(functionContext, EventTrigger.PLACE); +// if (dummy.isCancelled()) { +// return InteractionResult.SUCCESS_AND_CANCEL; +// } +// +// if (player != null) { +// if (!player.canInstabuild()) { +// item.count(item.count() - 1); +// } +// player.swingHand(context.getHand()); +// } +// +// context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index ce298bf65..d312a801d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.entity.furniture.element.BukkitFurnitureElementConfigs; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; @@ -162,6 +163,7 @@ public class BukkitCraftEngine extends CraftEngine { BukkitItemBehaviors.init(); BukkitHitBoxTypes.init(); BukkitBlockEntityElementConfigs.init(); + BukkitFurnitureElementConfigs.init(); // 初始化 onload 阶段的兼容性 super.compatibilityManager().onLoad(); // 创建网络管理器 diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java index 9a6729b0a..352ffd619 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java @@ -39,7 +39,7 @@ public class BukkitPlatform implements Platform { Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); } } @@ -55,7 +55,7 @@ public class BukkitPlatform implements Platform { CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java index 861c01eb5..1b4b1a942 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java @@ -115,7 +115,7 @@ public class DebugItemDataCommand extends BukkitCommandFeature { } else if (nbt instanceof short[]) { value = Arrays.toString((short[]) nbt); } else { - value = "Unknown array type"; + value = "Unknown array id"; } } else { value = nbt.toString(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index a6333f2d5..e1d29f243 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; @@ -42,19 +42,19 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { NamespacedKey namespacedKey = context.get("id"); Key id = KeyUtils.namespacedKey2Key(namespacedKey); BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); - Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); if (optionalCustomFurniture.isEmpty()) { return; } Location location = context.get("location"); - CustomFurniture customFurniture = optionalCustomFurniture.get(); - AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType()); + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); boolean playSound = context.flags().hasFlag("silent"); CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); }); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java index b46f47699..29971f70a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -15,7 +15,6 @@ import org.bukkit.entity.Player; import org.incendo.cloud.Command; import org.incendo.cloud.bukkit.parser.PlayerParser; import org.incendo.cloud.parser.standard.BooleanParser; -import org.incendo.cloud.parser.standard.DoubleParser; import java.util.Optional; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 8ea7189f7..92686686d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -59,8 +59,8 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.furniture.HitBox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxPart; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxPart; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -3711,11 +3711,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (actionType == 1) { // ATTACK boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeBoolean(usingSecondaryAction); } @@ -3765,11 +3765,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes float z = buf.readFloat(); InteractionHand hand = buf.readVarInt() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeFloat(x).writeFloat(y).writeFloat(z); buf.writeVarInt(hand == InteractionHand.MAIN_HAND ? 0 : 1); @@ -3864,11 +3864,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } else if (actionType == 0) { int hand = buf.readVarInt(); boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeVarInt(hand); buf.writeBoolean(usingSecondaryAction); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java index 81ad7c893..62390bef7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java @@ -63,7 +63,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { public void convertAddCustomProjectilePacket(FriendlyByteBuf buf, ByteBufPacketEvent event) { UUID uuid = buf.readUUID(); - buf.readVarInt(); // type + buf.readVarInt(); // id double x = buf.readDouble(); double y = buf.readDouble(); double z = buf.readDouble(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java index 716ad2f0d..ba1b1804a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java @@ -35,7 +35,7 @@ public class PayloadHelper { @SuppressWarnings("unchecked") NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(data.type()); if (codec == null) { - CraftEngine.instance().logger().warn("Unknown data type class: " + data.getClass().getName()); + CraftEngine.instance().logger().warn("Unknown data id class: " + data.getClass().getName()); return; } FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); @@ -65,7 +65,7 @@ public class PayloadHelper { @SuppressWarnings("unchecked") NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); if (codec == null) { - Debugger.COMMON.debug(() -> "Unknown data type received: " + type); + Debugger.COMMON.debug(() -> "Unknown data id received: " + type); return; } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml index be22a435c..06523d269 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -12,12 +12,9 @@ items: sounds: break: minecraft:block.bamboo_wood.break place: minecraft:block.bamboo_wood.place - placement: + variants: ground: loot-spawn-offset: 0.5,0.5,0 - rules: - rotation: FOUR - alignment: CENTER elements: - item: default:bench display-transform: NONE diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 5ce8d8ef8..1d523d817 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -160,8 +160,8 @@ warning.config.sound.missing_name: "Problem in Datei gefunden - warning.config.jukebox_song.duplicate: "Problem in Datei gefunden - Doppelter Jukebox-Song ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." warning.config.jukebox_song.missing_sound: "Problem in Datei gefunden - Beim Jukebox-Song '' fehlt das erforderliche 'sound'-Argument." warning.config.furniture.duplicate: "Problem in Datei gefunden - Doppeltes Furniture ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.furniture.missing_placement: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'placement'-Argument." -warning.config.furniture.element.missing_item: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'item'-Argument für eines seiner Elemente." +warning.config.furniture.missing_variants: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'variants'-Argument." +warning.config.furniture.element.item_display.missing_item: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'item'-Argument für eines seiner Elemente." warning.config.furniture.settings.unknown: "Problem in Datei gefunden - Das Furniture '' verwendet einen unbekannten Einstellungs-Typ ''." warning.config.furniture.hitbox.invalid_type: "Problem in Datei gefunden - Das Furniture '' verwendet einen ungültigen Hitbox-Typ ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei gefunden - Das Furniture '' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entity-Typ ''." diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index c7e538f21..013865146 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -190,8 +190,9 @@ warning.config.sound.missing_name: "Issue found in file - The so warning.config.jukebox_song.duplicate: "Issue found in file - Duplicated jukebox song ''. Please check if there is the same configuration in other files." warning.config.jukebox_song.missing_sound: "Issue found in file - The jukebox song '' is missing the required 'sound' argument." warning.config.furniture.duplicate: "Issue found in file - Duplicated furniture ''. Please check if there is the same configuration in other files." -warning.config.furniture.missing_placement: "Issue found in file - The furniture '' is missing the required 'placement' argument." -warning.config.furniture.element.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for one of its elements." +warning.config.furniture.missing_variants: "Issue found in file - The furniture '' is missing the required 'variants' argument." +warning.config.furniture.element.invalid_type: "Issue found in file - The furniture '' is using an invalid element type ''." +warning.config.furniture.element.item_display.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'item_display' element." warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." diff --git a/common-files/src/main/resources/translations/es.yml b/common-files/src/main/resources/translations/es.yml index 0a262c0f3..c52e59568 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -111,8 +111,8 @@ warning.config.sound.missing_name: "Problema encontrado en el archivo Problema encontrado en el archivo - Canción de tocadiscos duplicada ''. Verifica si hay la misma configuración en otros archivos." warning.config.jukebox_song.missing_sound: "Problema encontrado en el archivo - La canción de tocadiscos '' carece del argumento requerido 'sound'." warning.config.furniture.duplicate: "Problema encontrado en el archivo - Mueble duplicado ''. Verifica si hay la misma configuración en otros archivos." -warning.config.furniture.missing_placement: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'placement'." -warning.config.furniture.element.missing_item: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'item' para uno de sus elementos." +warning.config.furniture.missing_variants: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'variants'." +warning.config.furniture.element.item_display.missing_item: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'item' para uno de sus elementos." warning.config.furniture.settings.unknown: "Problema encontrado en el archivo - El mueble '' está usando un tipo de configuración desconocido ''." warning.config.furniture.hitbox.invalid_type: "Problema encontrado en el archivo - El mueble '' está usando un tipo de hitbox inválido ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problema encontrado en el archivo - El mueble '' está usando un hitbox personalizado con un tipo de entidad inválido ''." diff --git a/common-files/src/main/resources/translations/fr_fr.yml b/common-files/src/main/resources/translations/fr_fr.yml index 49bad7ee0..cf519f058 100644 --- a/common-files/src/main/resources/translations/fr_fr.yml +++ b/common-files/src/main/resources/translations/fr_fr.yml @@ -175,8 +175,8 @@ warning.config.sound.missing_name: "Problème trouvé dans le fichier Problème trouvé dans le fichier - Chanson de jukebox dupliquée ''. Vérifiez s’il existe la même configuration dans d’autres fichiers." warning.config.jukebox_song.missing_sound: "Problème trouvé dans le fichier - La chanson de jukebox '' manque l’argument obligatoire 'sound'." warning.config.furniture.duplicate: "Problème trouvé dans le fichier - Meuble dupliqué ''. Vérifiez s’il existe la même configuration dans d’autres fichiers." -warning.config.furniture.missing_placement: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'placement'." -warning.config.furniture.element.missing_item: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'item' pour un de ses éléments." +warning.config.furniture.missing_variants: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'variants'." +warning.config.furniture.element.item_display.missing_item: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'item' pour un de ses éléments." warning.config.furniture.settings.unknown: "Problème trouvé dans le fichier - Le meuble '' utilise un type de paramètre inconnu ''." warning.config.furniture.hitbox.invalid_type: "Problème trouvé dans le fichier - Le meuble '' utilise un type de hitbox invalide ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problème trouvé dans le fichier - Le meuble '' utilise un hitbox personnalisé avec un type d’entité invalide ''." diff --git a/common-files/src/main/resources/translations/ru_ru.yml b/common-files/src/main/resources/translations/ru_ru.yml index af586317c..6e55efa40 100644 --- a/common-files/src/main/resources/translations/ru_ru.yml +++ b/common-files/src/main/resources/translations/ru_ru.yml @@ -148,8 +148,8 @@ warning.config.sound.missing_name: "Проблема найдена в warning.config.jukebox_song.duplicate: "Проблема найдена в файле - Дублированная песня музыкального автомата ''. Проверьте, есть ли такая же конфигурация в других файлах." warning.config.jukebox_song.missing_sound: "Проблема найдена в файле - В песне музыкального автомата '' отсутствует необходимый 'sound' аргумент." warning.config.furniture.duplicate: "Проблема найдена в файле - Дублированная мебель ''. Проверьте, есть ли такая же конфигурация в других файлах." -warning.config.furniture.missing_placement: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'placement' аргумент." -warning.config.furniture.element.missing_item: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'item' аргумент для одного из его элементов." +warning.config.furniture.missing_variants: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'variants' аргумент." +warning.config.furniture.element.item_display.missing_item: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'item' аргумент для одного из его элементов." warning.config.furniture.settings.unknown: "Проблема найдена в файле - Мебель '' использует неизвестный тип настройки ''." warning.config.furniture.hitbox.invalid_type: "Проблема найдена в файле - Мебель '' использует недопустимый тип хитбокса ''." warning.config.furniture.hitbox.custom.invalid_entity: "Проблема найдена в файле - Мебель '' использует пользовательский хитбокс с недопустимым типом сущности ''." diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index 94a56309f..f826f3cc7 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -110,8 +110,8 @@ warning.config.sound.missing_name: " dosyasında sorun bulundu - warning.config.jukebox_song.duplicate: " dosyasında sorun bulundu - Yinelenen müzik çalar şarkısı ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." warning.config.jukebox_song.missing_sound: " dosyasında sorun bulundu - '' müzik çalar şarkısı gerekli 'sound' argümanı eksik." warning.config.furniture.duplicate: " dosyasında sorun bulundu - Yinelenen mobilya ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." -warning.config.furniture.missing_placement: " dosyasında sorun bulundu - '' mobilyası gerekli 'placement' argümanı eksik." -warning.config.furniture.element.missing_item: " dosyasında sorun bulundu - '' mobilyası, elementlerinden biri için gerekli 'item' argümanı eksik." +warning.config.furniture.missing_variants: " dosyasında sorun bulundu - '' mobilyası gerekli 'variants' argümanı eksik." +warning.config.furniture.element.item_display.missing_item: " dosyasında sorun bulundu - '' mobilyası, elementlerinden biri için gerekli 'item' argümanı eksik." warning.config.furniture.settings.unknown: " dosyasında sorun bulundu - '' mobilyası bilinmeyen bir ayar türü '' kullanıyor." warning.config.furniture.hitbox.invalid_type: " dosyasında sorun bulundu - '' mobilyası geçersiz bir hitbox türü '' kullanıyor." warning.config.furniture.hitbox.custom.invalid_entity: " dosyasında sorun bulundu - '' mobilyası, geçersiz varlık türü '' olan özel bir hitbox kullanıyor." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..1bcf6cb8f 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -183,8 +183,8 @@ warning.config.sound.missing_name: "在文件 发现问题 - 音 warning.config.jukebox_song.duplicate: "在文件 发现问题 - 重复的唱片机歌曲 '' 请检查其他文件中是否存在相同配置" warning.config.jukebox_song.missing_sound: "在文件 发现问题 - 唱片机歌曲 '' 缺少必需的 'sound' 参数" warning.config.furniture.duplicate: "在文件 发现问题 - 重复的家具 '' 请检查其他文件中是否存在相同配置" -warning.config.furniture.missing_placement: "在文件 发现问题 - 家具 '' 缺少必需的 'placement' 参数" -warning.config.furniture.element.missing_item: "在文件 发现问题 - 家具 '' 的某个元素缺少必需的 'item' 参数" +warning.config.furniture.missing_variants: "在文件 发现问题 - 家具 '' 缺少必需的 'variants' 参数" +warning.config.furniture.element.item_display.missing_item: "在文件 发现问题 - 家具 '' 的 'item_display' 元素缺少必需的 'item' 参数" warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index 5b0dbf02b..5707d7b2f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -711,12 +711,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } private CullingData parseCullingData(Object arguments) { - if (arguments instanceof Boolean b && !b) { - return null; - } - if (!(arguments instanceof Map)) { - return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true); - } + if (arguments instanceof Boolean b && !b) return null; + if (!(arguments instanceof Map)) return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true); Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"), diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index 6d794219f..b32258402 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -78,7 +78,7 @@ public abstract class BlockBehavior { return (boolean) superMethod.call(); } - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id // 1.20.5+ BlockState state, PathComputationType pathComputationType public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return (boolean) superMethod.call(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index e2e78a9a4..50a18c120 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -41,6 +41,7 @@ public class BlockSettings { float friction = 0.6f; float speedFactor = 1f; float jumpFactor = 1f; + Map, Object> customData = new IdentityHashMap<>(4); private BlockSettings() {} @@ -107,9 +108,29 @@ public class BlockSettings { newSettings.speedFactor = settings.speedFactor; newSettings.jumpFactor = settings.jumpFactor; newSettings.friction = settings.friction; + newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } + @SuppressWarnings("unchecked") + public T getCustomData(CustomDataType type) { + return (T) this.customData.get(type); + } + + public void clearCustomData() { + this.customData.clear(); + } + + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + + public void addCustomData(CustomDataType key, T value) { + this.customData.put(key, value); + } + public Set tags() { return tags; } @@ -542,7 +563,7 @@ public class BlockSettings { })); } - private static void registerFactory(String id, Modifier.Factory factory) { + public static void registerFactory(String id, Modifier.Factory factory) { FACTORIES.put(id, factory); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java index 7fe7d8e06..dca06a1bc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java @@ -4,7 +4,7 @@ import java.util.concurrent.Callable; public interface IsPathFindableBlockBehavior { - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id // 1.20.5+ BlockState state, PathComputationType pathComputationType boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java index 78dc7210a..fffeaaa89 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java @@ -24,7 +24,7 @@ public abstract class BlockEntity { this.type = type; } - public final CompoundTag saveAsTag() { + public CompoundTag saveAsTag() { CompoundTag tag = new CompoundTag(); this.saveId(tag); this.savePos(tag); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java index d7ca645e6..9c5f1d2e3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.util.Key; public final class BlockEntityTypeKeys { private BlockEntityTypeKeys() {} + public static final Key INACTIVE = Key.of("craftengine:inactive"); public static final Key UNSAFE_COMPOSITE = Key.of("craftengine:unsafe_composite"); public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage"); public static final Key SIMPLE_PARTICLE = Key.of("craftengine:simple_particle"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java index c4b725b06..46083cc4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; public abstract class BlockEntityTypes { + public static final BlockEntityType INACTIVE = register(BlockEntityTypeKeys.INACTIVE); public static BlockEntityType register(Key id) { BlockEntityType type = new BlockEntityType<>(id); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java new file mode 100644 index 000000000..3aa928f83 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.sparrow.nbt.CompoundTag; + +public class InactiveBlockEntity extends BlockEntity { + private final CompoundTag tag; + + public InactiveBlockEntity(BlockPos pos, + ImmutableBlockState blockState, + CompoundTag tag) { + super(BlockEntityTypes.INACTIVE, pos, blockState); + this.tag = tag; + } + + @Override + public CompoundTag saveAsTag() { + return this.tag; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java new file mode 100644 index 000000000..2ea1439a4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java @@ -0,0 +1,96 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.Cullable; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; + +import java.util.UUID; + +public abstract class CustomEntity implements Cullable { + protected final CustomEntityType type; + protected final UUID uuid; + protected WorldPosition position; + protected boolean valid = true; + + protected CustomEntity(CustomEntityType type, WorldPosition position, UUID uuid) { + this.position = position; + this.type = type; + this.uuid = uuid; + } + + public CompoundTag saveAsTag() { + CompoundTag tag = new CompoundTag(); + this.saveId(tag); + this.savePos(tag); + this.saveCustomData(tag); + return tag; + } + + private void savePos(CompoundTag tag) { + tag.putDouble("x", this.position.x()); + tag.putDouble("y", this.position.y()); + tag.putDouble("z", this.position.z()); + tag.putFloat("x_rot", this.position.xRot()); + tag.putFloat("y_rot", this.position.yRot()); + } + + public boolean isValid() { + return this.valid; + } + + public UUID uuid() { + return uuid; + } + + public double x() { + return this.position.x(); + } + + public double y() { + return this.position.y(); + } + + public double z() { + return this.position.z(); + } + + public float yRot() { + return this.position.yRot(); + } + + public float xRot() { + return this.position.xRot(); + } + + public CustomEntityType entityType() { + return this.type; + } + + protected void saveCustomData(CompoundTag tag) { + } + + public void loadCustomData(CompoundTag tag) { + } + + private void saveId(CompoundTag tag) { + tag.putString("id", this.type.id().asString()); + } + + public void destroy() { + this.valid = false; + } + + public static UUID readUUID(CompoundTag tag) { + return tag.getUUID("uuid"); + } + + public static WorldPosition readPos(CEWorld world, CompoundTag tag) { + double x = tag.getDouble("x"); + double y = tag.getDouble("y"); + double z = tag.getDouble("z"); + float xRot = tag.getFloat("x_rot"); + float yRot = tag.getFloat("y_rot"); + return new WorldPosition(world.world, x, y, z, xRot, yRot); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java new file mode 100644 index 000000000..a7cd7cb33 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.UUID; + +public record CustomEntityType(Key id, Factory factory) { + + public interface Factory { + + T create(UUID uuid, WorldPosition position); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java new file mode 100644 index 000000000..57e36fadc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; + +public final class CustomEntityTypeKeys { + private CustomEntityTypeKeys() {} + + public static final Key FURNITURE = Key.of("craftengine:furniture"); + public static final Key INACTIVE = Key.of("craftengine:inactive"); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java new file mode 100644 index 000000000..c5697d9c2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public class CustomEntityTypes { + public static final CustomEntityType INACTIVE = register(CustomEntityTypeKeys.INACTIVE, InactiveCustomEntity::new); + + public static CustomEntityType register(Key id, CustomEntityType.Factory factory) { + CustomEntityType type = new CustomEntityType<>(id, factory); + ((WritableRegistry>) BuiltInRegistries.ENTITY_TYPE) + .register(ResourceKey.create(Registries.ENTITY_TYPE.location(), id), type); + return type; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java new file mode 100644 index 000000000..1c870110a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.UUID; + +public class InactiveCustomEntity extends CustomEntity { + public static final CompoundTag INVALID_TAG = new CompoundTag(Map.of("type", NBT.createString(CustomEntityTypes.INACTIVE.id().asMinimalString()))); + private final CompoundTag data; + + public InactiveCustomEntity(UUID uuid, WorldPosition position) { + super(CustomEntityTypes.INACTIVE, position, uuid); + this.data = INVALID_TAG; + } + + public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { + super(CustomEntityTypes.INACTIVE, position, uuid); + this.data = data; + } + + @Override + public CompoundTag saveAsTag() { + return this.data; + } + + @Override + public boolean isValid() { + // 不正常的数据不要存储 + return this.data != INVALID_TAG; + } + + @Override + public void destroy() { + } + + @Override + public void show(Player player) { + } + + @Override + public void hide(Player player) { + } + + @Override + public @Nullable CullingData cullingData() { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java b/core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java index 7533b658b..a415a8e35 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.display; public enum Billboard { FIXED(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java b/core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java similarity index 87% rename from core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java index 239488019..fdbe09050 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.display; public enum ItemDisplayContext { NONE(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java deleted file mode 100644 index 3f4d4bae4..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public abstract class AbstractCustomFurniture implements CustomFurniture { - private final Key id; - private final FurnitureSettings settings; - private final Map placements; - private final Map>> events; - @Nullable - private final LootTable lootTable; - - private final AnchorType anyType; - - protected AbstractCustomFurniture(@NotNull Key id, - @NotNull FurnitureSettings settings, - @NotNull Map placements, - @NotNull Map>> events, - @Nullable LootTable lootTable) { - this.id = id; - this.settings = settings; - this.placements = placements; - this.lootTable = lootTable; - this.events = events; - this.anyType = placements.keySet().stream().findFirst().orElse(null); - } - - @Override - public void execute(Context context, EventTrigger trigger) { - for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { - function.run(context); - } - } - - @Override - public Key id() { - return this.id; - } - - @Override - public Map placements() { - return this.placements; - } - - @Override - public FurnitureSettings settings() { - return this.settings; - } - - @Override - public @Nullable LootTable lootTable() { - return this.lootTable; - } - - @Override - public AnchorType getAnyAnchorType() { - return this.anyType; - } - - @Override - public boolean isAllowedPlacement(AnchorType anchorType) { - return this.placements.containsKey(anchorType); - } - - @Override - public Placement getPlacement(AnchorType anchorType) { - return this.placements.get(anchorType); - } - - @Override - public Placement getValidPlacement(AnchorType anchorType) { - Placement placement = this.placements.get(anchorType); - if (placement == null) { - return this.placements.get(getAnyAnchorType()); - } - return placement; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java deleted file mode 100644 index 9e470cb6d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.util.Key; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -public abstract class AbstractFurnitureElement implements FurnitureElement { - private final Key item; - private final Billboard billboard; - private final ItemDisplayContext transform; - private final Vector3f scale; - private final Vector3f translation; - private final Vector3f position; - private final Quaternionf rotation; - private final boolean applyDyedColor; - private final float shadowRadius; - private final float shadowStrength; - - public AbstractFurnitureElement(Key item, - Billboard billboard, - ItemDisplayContext transform, - Vector3f scale, - Vector3f translation, - Vector3f position, - Quaternionf rotation, - float shadowRadius, - float shadowStrength, - boolean applyDyedColor) { - this.billboard = billboard; - this.transform = transform; - this.scale = scale; - this.translation = translation; - this.item = item; - this.rotation = rotation; - this.position = position; - this.applyDyedColor = applyDyedColor; - this.shadowRadius = shadowRadius; - this.shadowStrength = shadowStrength; - } - - @Override - public float shadowRadius() { - return shadowRadius; - } - - @Override - public float shadowStrength() { - return shadowStrength; - } - - @Override - public boolean applyDyedColor() { - return applyDyedColor; - } - - @Override - public Quaternionf rotation() { - return rotation; - } - - @Override - public Key item() { - return item; - } - - @Override - public Billboard billboard() { - return billboard; - } - - @Override - public ItemDisplayContext transform() { - return transform; - } - - @Override - public Vector3f scale() { - return scale; - } - - @Override - public Vector3f translation() { - return translation; - } - - @Override - public Vector3f position() { - return position; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 52ea8252d..dd67036f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -1,19 +1,24 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.collision.AABB; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -21,7 +26,7 @@ import java.nio.file.Path; import java.util.*; public abstract class AbstractFurnitureManager implements FurnitureManager { - protected final Map byId = new HashMap<>(); + protected final Map byId = new HashMap<>(); private final CraftEngine plugin; private final FurnitureParser furnitureParser; // Cached command suggestions @@ -56,12 +61,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { } @Override - public Optional furnitureById(Key id) { + public Optional furnitureById(Key id) { return Optional.ofNullable(this.byId.get(id)); } @Override - public Map loadedFurniture() { + public Map loadedFurniture() { return Collections.unmodifiableMap(this.byId); } @@ -72,10 +77,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { protected abstract HitBoxConfig defaultHitBox(); - protected abstract FurnitureElement.Builder furnitureElementBuilder(); - - protected abstract CustomFurniture.Builder furnitureBuilder(); - public class FurnitureParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; private final List pendingConfigSections = new ArrayList<>(); @@ -107,91 +108,72 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return LoadingSequence.FURNITURE; } - @SuppressWarnings("unchecked") @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractFurnitureManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.furniture.duplicate"); } - EnumMap placements = new EnumMap<>(AnchorType.class); - Object placementObj = section.get("placement"); - Map placementMap = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(placementObj, "warning.config.furniture.missing_placement"), false); - if (placementMap.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement"); - } - for (Map.Entry entry : placementMap.entrySet()) { - // anchor type - AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); - Map placementArguments = MiscUtils.castToMap(entry.getValue(), false); - Optional optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - // furniture display elements - List elements = new ArrayList<>(); - List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); - for (Map element : elementConfigs) { - FurnitureElement furnitureElement = furnitureElementBuilder() - .item(Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(element.get("item"), "warning.config.furniture.element.missing_item"))) - .applyDyedColor(ResourceConfigUtils.getAsBoolean(element.getOrDefault("apply-dyed-color", true), "apply-dyed-color")) - .billboard(ResourceConfigUtils.getOrDefault(element.get("billboard"), o -> Billboard.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), Billboard.FIXED)) - .transform(ResourceConfigUtils.getOrDefault(ResourceConfigUtils.get(element, "transform", "display-transform"), o -> ItemDisplayContext.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), ItemDisplayContext.NONE)) - .scale(ResourceConfigUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale")) - .position(ResourceConfigUtils.getAsVector3f(element.getOrDefault("position", "0"), "position")) - .translation(ResourceConfigUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation")) - .rotation(ResourceConfigUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation")) - .shadowRadius(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-radius", 0f), "shadow-radius")) - .shadowStrength(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-strength", 1f), "shadow-strength")) - .build(); - elements.add(furnitureElement); - } - // external model providers + Map variantsMap = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "variants", "placement", "variant"), "warning.config.furniture.missing_variants"), "variants"); + if (variantsMap.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.furniture.missing_variants"); + } + + Map variants = new HashMap<>(); + for (Map.Entry e0 : variantsMap.entrySet()) { + String variantName = e0.getKey(); + Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); + Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + + // fixme 外部模型不应该在这 Optional externalModel; - if (placementArguments.containsKey("model-engine")) { - externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", placementArguments.get("model-engine").toString())); - } else if (placementArguments.containsKey("better-model")) { - externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", placementArguments.get("better-model").toString())); + if (variantArguments.containsKey("model-engine")) { + externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", variantArguments.get("model-engine").toString())); + } else if (variantArguments.containsKey("better-model")) { + externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", variantArguments.get("better-model").toString())); } else { externalModel = Optional.empty(); } - // add hitboxes - List hitboxes = ResourceConfigUtils.parseConfigAsList(placementArguments.get("hitboxes"), HitBoxTypes::fromMap); + List hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), HitBoxTypes::fromMap); if (hitboxes.isEmpty() && externalModel.isEmpty()) { hitboxes = List.of(defaultHitBox()); } - // rules - Map ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true); - if (ruleSection != null) { - placements.put(anchorType, new CustomFurniture.Placement( - anchorType, - elements.toArray(new FurnitureElement[0]), - hitboxes.toArray(new HitBoxConfig[0]), - ResourceConfigUtils.getOrDefault(ruleSection.get("rotation"), o -> RotationRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), RotationRule.ANY), - ResourceConfigUtils.getOrDefault(ruleSection.get("alignment"), o -> AlignmentRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), AlignmentRule.CENTER), - externalModel, - optionalLootSpawnOffset - )); - } else { - placements.put(anchorType, new CustomFurniture.Placement( - anchorType, - elements.toArray(new FurnitureElement[0]), - hitboxes.toArray(new HitBoxConfig[0]), - RotationRule.ANY, - AlignmentRule.CENTER, - externalModel, - optionalLootSpawnOffset - )); - } + variants.put(variantName, new FurnitureVariant( + elements.toArray(new FurnitureElementConfig[0]), + hitboxes.toArray(new HitBoxConfig[0]), + externalModel, + optionalLootSpawnOffset + )); } - CustomFurniture furniture = furnitureBuilder() + AABB maxAABB = null; + + FurnitureConfig furniture = FurnitureConfig.builder() .id(id) .settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true))) - .placement(placements) + .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) + .cullingData(parseCullingData(section.get("entity-culling"), maxAABB)) .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } + + private CullingData parseCullingData(Object arguments, AABB maxHitbox) { + if (arguments instanceof Boolean b && !b) + return null; + if (!(arguments instanceof Map)) + return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true); + Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); + return new CullingData( + ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"), + ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") + ); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index d3a099b6f..cefebe26b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; +@Deprecated(since = "0.0.66", forRemoval = true) public enum AnchorType { GROUND(0), WALL(1), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java deleted file mode 100644 index 19afdf32f..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ /dev/null @@ -1,60 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -// TODO 家具的设计存在问题。家具也应该存在不同的状态,而不是根据放置规则直接决定状态类型 -public interface CustomFurniture { - - void execute(Context context, EventTrigger trigger); - - Key id(); - - Map placements(); - - FurnitureSettings settings(); - - @Nullable - LootTable lootTable(); - - AnchorType getAnyAnchorType(); - - boolean isAllowedPlacement(AnchorType anchorType); - - Placement getPlacement(AnchorType anchorType); - - Placement getValidPlacement(AnchorType anchorType); - - interface Builder { - - Builder id(Key id); - - Builder placement(Map placements); - - Builder settings(FurnitureSettings settings); - - Builder lootTable(LootTable lootTable); - - Builder events(Map>> events); - - CustomFurniture build(); - } - - record Placement(AnchorType anchorType, - FurnitureElement[] elements, - HitBoxConfig[] hitBoxConfigs, - RotationRule rotationRule, - AlignmentRule alignmentRule, - Optional externalModel, - Optional dropOffset) { - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index e5a2bcd59..1b4661ba1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,48 +1,28 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.entity.CustomEntity; +import net.momirealms.craftengine.core.entity.CustomEntityType; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.UUID; +public abstract class Furniture extends CustomEntity { + protected final FurnitureConfig config; + protected final FurnitureDataAccessor dataAccessor; -public interface Furniture { - void initializeColliders(); - - WorldPosition position(); - - boolean isValid(); - - void destroy(); - - void destroyColliders(); - - void destroySeats(); - - UUID uuid(); - - int baseEntityId(); - - @Nullable - HitBox hitBoxByEntityId(int id); - - @Nullable HitBoxPart hitBoxPartByEntityId(int id); + public Furniture(CustomEntityType type, WorldPosition position, FurnitureConfig config, CompoundTag data) { + super(type, position); + this.dataAccessor = new FurnitureDataAccessor(data); + this.config = config; + } @NotNull - AnchorType anchorType(); + public FurnitureConfig config() { + return this.config; + } @NotNull - Key id(); - - @NotNull - CustomFurniture config(); - - boolean hasExternalModel(); - - FurnitureExtraData extraData(); - - void setExtraData(FurnitureExtraData extraData); - - void save(); + public FurnitureDataAccessor dataAccessor() { + return this.dataAccessor; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java new file mode 100644 index 000000000..c6f075e60 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -0,0 +1,67 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; +import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public interface FurnitureConfig { + + void execute(Context context, EventTrigger trigger); + + Key id(); + + FurnitureSettings settings(); + + @Nullable + LootTable lootTable(); + + Map variants(); + + default FurnitureVariant anyVariant() { + return variants().values().stream().findFirst().get(); + } + + default String anyVariantName() { + return variants().keySet().stream().findFirst().get(); + } + + @Nullable + FurnitureVariant getVariant(String variantName); + + @NotNull + FurnitureBehavior behavior(); + + CullingData cullingData(); + + static Builder builder() { + return new FurnitureConfigImpl.BuilderImpl(); + } + + interface Builder { + + Builder id(Key id); + + Builder variants(Map variants); + + Builder settings(FurnitureSettings settings); + + Builder lootTable(LootTable lootTable); + + Builder events(Map>> events); + + Builder behavior(FurnitureBehavior behavior); + + Builder cullingData(CullingData cullingData); + + FurnitureConfig build(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java new file mode 100644 index 000000000..8d6fe94b8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -0,0 +1,145 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import com.google.common.collect.ImmutableMap; +import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior; +import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; +import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +class FurnitureConfigImpl implements FurnitureConfig { + private final Key id; + private final FurnitureSettings settings; + private final Map variants; + private final Map>> events; + private final FurnitureBehavior behavior; + private final CullingData cullingData; + @Nullable + private final LootTable lootTable; + + private FurnitureConfigImpl(@NotNull Key id, + @NotNull FurnitureSettings settings, + @NotNull Map variants, + @NotNull Map>> events, + @NotNull FurnitureBehavior behavior, + @Nullable CullingData cullingData, + @Nullable LootTable lootTable) { + this.id = id; + this.settings = settings; + this.variants = ImmutableMap.copyOf(variants); + this.lootTable = lootTable; + this.behavior = behavior; + this.cullingData = cullingData; + this.events = events; + } + + @Override + public void execute(Context context, EventTrigger trigger) { + for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { + function.run(context); + } + } + + @Override + public Key id() { + return this.id; + } + + @Override + public FurnitureSettings settings() { + return this.settings; + } + + @Override + public @Nullable LootTable lootTable() { + return this.lootTable; + } + + @Override + public Map variants() { + return this.variants; + } + + @Override + public @NotNull FurnitureBehavior behavior() { + return this.behavior; + } + + @Override + public CullingData cullingData() { + return this.cullingData; + } + + @Nullable + @Override + public FurnitureVariant getVariant(String variantName) { + return this.variants.get(variantName); + } + + public static class BuilderImpl implements Builder { + private Key id; + private Map variants; + private FurnitureSettings settings; + private Map>> events; + private LootTable lootTable; + private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE; + private CullingData cullingData; + + @Override + public FurnitureConfig build() { + return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.cullingData, this.lootTable); + } + + @Override + public Builder id(Key id) { + this.id = id; + return this; + } + + @Override + public Builder variants(Map variants) { + this.variants = variants; + return this; + } + + @Override + public Builder settings(FurnitureSettings settings) { + this.settings = settings; + return this; + } + + @Override + public Builder lootTable(LootTable lootTable) { + this.lootTable = lootTable; + return this; + } + + @Override + public Builder events(Map>> events) { + this.events = events; + return this; + } + + @Override + public Builder cullingData(CullingData cullingData) { + this.cullingData = cullingData; + return this; + } + + @Override + public Builder behavior(FurnitureBehavior behavior) { + this.behavior = behavior; + return this; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java similarity index 57% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 72587f2a1..07a6ccbb5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -6,34 +6,53 @@ import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.util.Color; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; +import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Optional; -public class FurnitureExtraData { +public class FurnitureDataAccessor { public static final String ITEM = "item"; public static final String DYED_COLOR = "dyed_color"; public static final String FIREWORK_EXPLOSION_COLORS = "firework_explosion_colors"; + public static final String VARIANT = "variant"; + @ApiStatus.Obsolete public static final String ANCHOR_TYPE = "anchor_type"; private final CompoundTag data; - public FurnitureExtraData(CompoundTag data) { + public FurnitureDataAccessor(CompoundTag data) { this.data = data; } - public static FurnitureExtraData of(CompoundTag data) { - return new FurnitureExtraData(data); + public static FurnitureDataAccessor of(CompoundTag data) { + return new FurnitureDataAccessor(data); } public CompoundTag copyTag() { return this.data.copy(); } + @ApiStatus.Internal public CompoundTag unsafeTag() { return this.data; } + public void addCustomData(String key, Tag value) { + this.data.put(key, value); + } + + @Nullable + public Tag getCustomData(String key) { + return this.data.get(key); + } + + public void removeCustomData(String key) { + this.data.remove(key); + } + public Optional> item() { byte[] data = this.data.getByteArray(ITEM); if (data == null) return Optional.empty(); @@ -45,73 +64,57 @@ public class FurnitureExtraData { } } + public void setItem(Item item) { + this.data.putByteArray(ITEM, item.toByteArray()); + } + public Optional fireworkExplosionColors() { if (this.data.containsKey(FIREWORK_EXPLOSION_COLORS)) return Optional.of(this.data.getIntArray(FIREWORK_EXPLOSION_COLORS)); return Optional.empty(); } + public void setFireworkExplosionColors(int[] colors) { + this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); + } + public Optional dyedColor() { if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR))); return Optional.empty(); } + public void setDyedColor(Color color) { + this.data.putInt(DYED_COLOR, color.color()); + } + + public Optional variant() { + return Optional.ofNullable(this.data.getString(VARIANT)); + } + + public void setVariant(String variant) { + this.data.putString(VARIANT, variant); + } + + @ApiStatus.Obsolete public Optional anchorType() { if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); return Optional.empty(); } - public FurnitureExtraData anchorType(AnchorType type) { + @ApiStatus.Obsolete + public FurnitureDataAccessor anchorType(AnchorType type) { this.data.putInt(ANCHOR_TYPE, type.getId()); return this; } - public static Builder builder() { - return new Builder(); + public static FurnitureDataAccessor fromBytes(final byte[] data) throws IOException { + return new FurnitureDataAccessor(NBT.fromBytes(data)); } - public static FurnitureExtraData fromBytes(final byte[] data) throws IOException { - return new FurnitureExtraData(NBT.fromBytes(data)); - } - - public static byte[] toBytes(final FurnitureExtraData data) throws IOException { + public static byte[] toBytes(final FurnitureDataAccessor data) throws IOException { return NBT.toBytes(data.data); } public byte[] toBytes() throws IOException { return toBytes(this); } - - public static class Builder { - private final CompoundTag data; - - public Builder() { - this.data = new CompoundTag(); - } - - public Builder item(Item item) { - this.data.putByteArray(ITEM, item.toByteArray()); - return this; - } - - public Builder dyedColor(Color color) { - if (color == null) return this; - this.data.putInt(DYED_COLOR, color.color()); - return this; - } - - public Builder fireworkExplosionColors(int[] colors) { - if (colors == null) return this; - this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); - return this; - } - - public Builder anchorType(AnchorType type) { - this.data.putInt(ANCHOR_TYPE, type.getId()); - return this; - } - - public FurnitureExtraData build() { - return new FurnitureExtraData(data); - } - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java deleted file mode 100644 index 785b24c68..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java +++ /dev/null @@ -1,59 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.function.Consumer; - -public interface FurnitureElement { - Quaternionf rotation(); - - Key item(); - - Billboard billboard(); - - ItemDisplayContext transform(); - - float shadowRadius(); - - float shadowStrength(); - - boolean applyDyedColor(); - - Vector3f scale(); - - Vector3f translation(); - - Vector3f position(); - - void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer packets); - - interface Builder { - - Builder item(Key item); - - Builder billboard(Billboard billboard); - - Builder transform(ItemDisplayContext transform); - - Builder scale(Vector3f scale); - - Builder translation(Vector3f translation); - - Builder position(Vector3f position); - - Builder rotation(Quaternionf rotation); - - Builder applyDyedColor(boolean applyDyedColor); - - Builder shadowStrength(float shadowStrength); - - Builder shadowRadius(float shadowRadius); - - FurnitureElement build(); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index e57baec7d..5f8ca181a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -25,11 +25,11 @@ public interface FurnitureManager extends Manageable { Collection cachedSuggestions(); - Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound); + Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound); - Optional furnitureById(Key id); + Optional furnitureById(Key id); - Map loadedFurniture(); + Map loadedFurniture(); boolean isFurnitureRealEntity(int entityId); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java index 298dbc877..f6bf10314 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java @@ -1,11 +1,13 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.CustomDataType; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; public class FurnitureSettings { @@ -13,6 +15,7 @@ public class FurnitureSettings { FurnitureSounds sounds = FurnitureSounds.EMPTY; @Nullable Key itemId; + Map, Object> customData = new IdentityHashMap<>(4); private FurnitureSettings() {} @@ -29,6 +32,7 @@ public class FurnitureSettings { newSettings.sounds = settings.sounds; newSettings.itemId = settings.itemId; newSettings.minimized = settings.minimized; + newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -45,6 +49,25 @@ public class FurnitureSettings { return settings; } + @SuppressWarnings("unchecked") + public T getCustomData(CustomDataType type) { + return (T) this.customData.get(type); + } + + public void clearCustomData() { + this.customData.clear(); + } + + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + + public void addCustomData(CustomDataType key, T value) { + this.customData.put(key, value); + } + public FurnitureSounds sounds() { return sounds; } @@ -103,7 +126,7 @@ public class FurnitureSettings { })); } - private static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) { + public static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) { FACTORIES.put(id, factory); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java new file mode 100644 index 000000000..946f201cd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import org.joml.Vector3f; + +import java.util.Optional; + +public record FurnitureVariant(FurnitureElementConfig[] elements, + HitBoxConfig[] hitBoxConfigs, + Optional externalModel, + Optional dropOffset) { +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java deleted file mode 100644 index fdc574861..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public interface HitBoxConfig { - - Key type(); - - void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, - BiConsumer packets, Consumer collider, Consumer aabb); - - void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs); - - int[] acquireEntityIds(Supplier entityIdSupplier); - - SeatConfig[] seats(); - - Vector3f position(); - - boolean blocksBuilding(); - - boolean canBeHitByProjectile(); - - boolean canUseItemOn(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java new file mode 100644 index 000000000..439348957 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +public final class EmptyFurnitureBehavior implements FurnitureBehavior { + private EmptyFurnitureBehavior() {} + + public static final EmptyFurnitureBehavior INSTANCE = new EmptyFurnitureBehavior(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java new file mode 100644 index 000000000..655168a5a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker; + +public interface FurnitureBehavior { + + default FurnitureTicker createSyncFurnitureTicker(T furniture) { + return null; + } + + default FurnitureTicker createAsyncBlockEntityTicker(T furniture) { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java new file mode 100644 index 000000000..118d5bd92 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.player.Player; + +public interface FurnitureElement { + + void show(Player player); + + void hide(Player player); + + default void deactivate() {} + + default void activate() {} +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java new file mode 100644 index 000000000..c291a43be --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import net.momirealms.craftengine.core.world.WorldPosition; +import org.jetbrains.annotations.NotNull; + +public interface FurnitureElementConfig { + + E create(@NotNull WorldPosition position); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java new file mode 100644 index 000000000..438124560 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import java.util.Map; + +public interface FurnitureElementConfigFactory { + + FurnitureElementConfig create(Map args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java new file mode 100644 index 000000000..8e375a743 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.Map; +import java.util.Optional; + +public class FurnitureElementConfigs { + public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display"); + public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); + public static final Key ITEM = Key.of("craftengine:item"); + + public static void register(Key key, FurnitureElementConfigFactory type) { + ((WritableRegistry) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) + .register(ResourceKey.create(Registries.FURNITURE_ELEMENT_TYPE.location(), key), type); + } + + public static FurnitureElementConfig fromMap(Map arguments) { + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); + FurnitureElementConfigFactory factory = BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.element.invalid_type", type.toString()); + } + return factory.create(arguments); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java similarity index 54% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java index 8f725a94b..185fec9de 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java @@ -1,40 +1,26 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; -import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.HitBox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.HitBoxPart; import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.sparrow.nbt.CompoundTag; import java.util.Optional; -public class BukkitHitBox implements HitBox { +public abstract class AbstractHitBox implements HitBox { private final Furniture furniture; private final HitBoxConfig config; private final HitBoxPart[] parts; - private final Seat[] seats; + private Seat[] seats; - public BukkitHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { + public AbstractHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { this.parts = parts; this.config = config; this.furniture = furniture; - this.seats = createSeats(config); } - @SuppressWarnings("unchecked") - private Seat[] createSeats(HitBoxConfig config) { - SeatConfig[] seatConfigs = config.seats(); - Seat[] seats = new Seat[seatConfigs.length]; - for (int i = 0; i < seatConfigs.length; i++) { - seats[i] = new BukkitSeat<>(this, seatConfigs[i]); - } - return seats; - } + protected abstract void createSeats(HitBoxConfig config); @Override public HitBoxPart[] parts() { @@ -65,6 +51,6 @@ public class BukkitHitBox implements HitBox { @Override public void saveCustomData(CompoundTag data) { data.putString("type", "furniture"); - data.putInt("entity_id", this.furniture.baseEntityId()); + data.putInt("entity_id", this.furniture.entityId()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java index d7f8e3021..d9a5755f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import org.joml.Vector3f; @@ -30,16 +30,16 @@ public abstract class AbstractHitBoxConfig implements HitBoxConfig { @Override public boolean blocksBuilding() { - return blocksBuilding; + return this.blocksBuilding; } @Override public boolean canBeHitByProjectile() { - return canBeHitByProjectile; + return this.canBeHitByProjectile; } @Override public boolean canUseItemOn() { - return canUseItemOn; + return this.canUseItemOn; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java similarity index 87% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java index 961ea7420..32d31c3e1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java new file mode 100644 index 000000000..f14f7a064 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java @@ -0,0 +1,20 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.Key; +import org.joml.Vector3f; + +public interface HitBoxConfig { + + Key type(); + + SeatConfig[] seats(); + + Vector3f position(); + + boolean blocksBuilding(); + + boolean canBeHitByProjectile(); + + boolean canUseItemOn(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java index 32fb5f355..df9a2b101 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import java.util.Map; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java similarity index 73% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java index cfa336889..fe5619281 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java similarity index 91% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java index c005ee619..853fa8318 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; @@ -14,6 +14,7 @@ public class HitBoxTypes { public static final Key INTERACTION = Key.of("minecraft:interaction"); public static final Key SHULKER = Key.of("minecraft:shulker"); public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast"); + public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); public static void register(Key key, HitBoxConfigFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java new file mode 100644 index 000000000..1f034cbd9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.tick; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; + +public interface FurnitureTicker { + + void tick(T furniture); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java similarity index 67% rename from core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java index 77483b9c1..120c168f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.item; import net.momirealms.craftengine.core.item.Item; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java index 7801b8f9d..ca0c73679 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.entity.projectile; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.util.Key; import org.joml.Quaternionf; import org.joml.Vector3f; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 5b7c2f563..f48a6bf39 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -28,7 +28,7 @@ import java.util.Optional; * This interface provides methods for managing item properties such as custom model data, * damage, display name, lore, enchantments, and tags. * - * @param the type of the item implementation + * @param the id of the item implementation */ public interface Item { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 186588e32..0c125097d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.item; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment; import net.momirealms.craftengine.core.item.equipment.Equipment; @@ -134,6 +134,12 @@ public class ItemSettings { this.customData.clear(); } + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + public void addCustomData(CustomDataType key, T value) { this.customData.put(key, value); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 190ee8004..206655f05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -190,7 +190,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Override public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List template = MiscUtils.getAsStringList(arguments.get("template-id")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); boolean mergeEnchantments = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-enchantments", false), "merge-enchantments"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index 7119a5f48..c3a749435 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -141,7 +141,7 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe @Override public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List template = MiscUtils.getAsStringList(arguments.get("template-id")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index c0475500d..b5d118862 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -87,12 +87,12 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< public static Formula fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("number type cannot be null"); + throw new NullPointerException("number id cannot be null"); } Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); FormulaFactory factory = BuiltInRegistries.FORMULA_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown formula type: " + type); + throw new IllegalArgumentException("Unknown formula id: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index e6d151250..c79c52a8a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -75,7 +75,7 @@ public class ItemModels { Key key = Key.withDefaultNamespace(type, "minecraft"); ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid item model type: " + key); + throw new IllegalArgumentException("Invalid item model id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index f2d5022fa..ce19f4e75 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -80,7 +80,7 @@ public class ConditionProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid condition property type: " + key); + throw new IllegalArgumentException("Invalid condition property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index a8f09bcc0..0d98ca88b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -71,7 +71,7 @@ public class RangeDispatchProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid range dispatch property type: " + key); + throw new IllegalArgumentException("Invalid range dispatch property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index e6f5b46e0..464c79fc3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -71,7 +71,7 @@ public class SelectProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid select property type: " + key); + throw new IllegalArgumentException("Invalid select property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index b6a83032c..cdfffd3bb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -45,7 +45,7 @@ public class SignSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); - String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type"); + String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-id"), "warning.config.item.model.special.sign.missing_wood_type"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture"); return new SignSpecialModel(type, woodType, texture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index c27c4bba3..8b222da2b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -80,7 +80,7 @@ public class SpecialModels { Key key = Key.withDefaultNamespace(type, "minecraft"); SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid special model type: " + key); + throw new IllegalArgumentException("Invalid special model id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index d44d3102d..8148873b5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -65,7 +65,7 @@ public class Tints { Key key = Key.withDefaultNamespace(type, "minecraft"); TintReader reader = BuiltInRegistries.TINT_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid tint type: " + type); + throw new IllegalArgumentException("Invalid tint id: " + type); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java index 9d545f67d..7833ea8fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java @@ -36,7 +36,7 @@ public enum ObfA { return type; } } - throw new IllegalArgumentException("Unknown resource type: " + xclf); + throw new IllegalArgumentException("Unknown resource id: " + xclf); } public static final byte[] VALUES = new byte[] { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java index ff1e71fa1..96a87ede0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java @@ -9,7 +9,7 @@ import java.util.UUID; /** * Simple implementation of {@link Sender} using a {@link SenderFactory} * - * @param the command sender type + * @param the command sender id */ public final class AbstractSender implements Sender { private final Plugin plugin; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index df09a8b08..2ec08dbdf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -448,10 +448,10 @@ public class Config { // furniture furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true); - furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH)); + furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-id", "interaction").toUpperCase(Locale.ENGLISH)); // equipment - equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail").toLowerCase(Locale.ENGLISH); + equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.id", "chainmail").toLowerCase(Locale.ENGLISH); if (!AbstractPackManager.ALLOWED_VANILLA_EQUIPMENT.contains(equipment$sacrificed_vanilla_armor$type)) { TranslationManager.instance().log("warning.config.equipment.invalid_sacrificed_armor", equipment$sacrificed_vanilla_armor$type); equipment$sacrificed_vanilla_armor$type = "chainmail"; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index 10f7f8bea..4c3ea46fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -259,7 +259,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.byteValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -271,7 +271,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.shortValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -283,7 +283,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.longValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -295,7 +295,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.floatValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -307,7 +307,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.doubleValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index cf02eeb46..790641d5d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -61,7 +61,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { public TemplateArgument create(Map arguments) { return new ExpressionTemplateArgument( arguments.getOrDefault("expression", "").toString(), - ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT)) + ValueType.valueOf(arguments.getOrDefault("value-id", "double").toString().toUpperCase(Locale.ROOT)) ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java index 9b1a8ef91..4c0e9baa7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java @@ -58,7 +58,7 @@ public class TemplateArguments { Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE); TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown argument type: " + type); + throw new IllegalArgumentException("Unknown argument id: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java index 97e197b55..fa33c8b7f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -43,7 +43,7 @@ public class DamageFunction extends AbstractConditionalFunc @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic"))); + Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-id", "generic"))); NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java index bbe2fe3d0..1d1b17e4e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java @@ -65,7 +65,7 @@ public class OpenWindowFunction extends AbstractConditional @Override public Function create(Map arguments) { String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null); - String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type"); + String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-id"), "warning.config.function.open_window.missing_gui_type"); try { GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH)); return new OpenWindowFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), type, title); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java index 88a2eb2ff..796358ef3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java @@ -46,7 +46,7 @@ public class RemoveFurnitureFunction extends AbstractCondit ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); Player player = optionalPlayer.orElse(null); if (player != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 520863010..154c66687 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -94,7 +94,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); + AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-id"), AnchorType.class, null); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java index 39a879c3e..1db66e416 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java @@ -1,8 +1,5 @@ package net.momirealms.craftengine.core.plugin.context.function; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; @@ -15,7 +12,6 @@ import net.momirealms.craftengine.core.world.WorldPosition; import java.util.List; import java.util.Map; -import java.util.Optional; public class SpawnFurnitureFunction extends AbstractConditionalFunction { private final Key furnitureId; @@ -24,7 +20,7 @@ public class SpawnFurnitureFunction extends AbstractConditi private final NumberProvider z; private final NumberProvider pitch; private final NumberProvider yaw; - private final AnchorType anchorType; + private final String variant; private final boolean playSound; public SpawnFurnitureFunction( @@ -34,7 +30,7 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z, NumberProvider pitch, NumberProvider yaw, - AnchorType anchorType, + String variant, boolean playSound, List> predicates ) { @@ -45,7 +41,7 @@ public class SpawnFurnitureFunction extends AbstractConditi this.z = z; this.pitch = pitch; this.yaw = yaw; - this.anchorType = anchorType; + this.variant = variant; this.playSound = playSound; } @@ -59,17 +55,18 @@ public class SpawnFurnitureFunction extends AbstractConditi float pitchValue = this.pitch.getFloat(ctx); float yawValue = this.yaw.getFloat(ctx); WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue); - spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); + // fixme api +// spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); }); } - public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { - CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { - AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); - FurnitureExtraData extraData = FurnitureExtraData.builder().anchorType(anchor).build(); - CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); - }); - } +// public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { +// CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { +// AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); +// FurnitureDataAccessor extraData = FurnitureDataAccessor.builder().anchorType(anchor).build(); +// CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); +// }); +// } @Override public Key type() { @@ -90,9 +87,9 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-id")); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, playSound, getPredicates(arguments)); + return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java index 853cc7c28..ec63aa261 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java @@ -63,7 +63,7 @@ public class ToastFunction extends AbstractConditionalFunct @Override public Function create(Map arguments) { AdvancementType advancementType; - String advancementName = arguments.getOrDefault("advancement-type", "goal").toString(); + String advancementName = arguments.getOrDefault("advancement-id", "goal").toString(); try { advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java index 0fd065ee2..273e16b2b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.entity.Entity; -import net.momirealms.craftengine.core.entity.ItemEntity; +import net.momirealms.craftengine.core.entity.item.ItemEntity; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; import net.momirealms.craftengine.core.util.MiscUtils; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java index b677d18a5..b0f6d19dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java @@ -53,7 +53,7 @@ public class LangData { temp.put(result, entry.getValue()); } }, - () -> CraftEngine.instance().logger().warn("Unknown lang type: " + key) + () -> CraftEngine.instance().logger().warn("Unknown lang id: " + key) ); } else { temp.put(key, entry.getValue()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index f09e3a159..8cab5409b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -88,9 +90,11 @@ public class BuiltInRegistries { public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16); public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16); public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); - public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 128); + public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); + public static final Registry> ENTITY_TYPE = createConstantBoundRegistry(Registries.ENTITY_TYPE, 32); + public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 33a34a9d9..5258d2e4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -93,4 +95,6 @@ public class Registries { public static final ResourceKey>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type")); public static final ResourceKey> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); + public static final ResourceKey>> ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("entity_type")); + public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index af0356c6e..cc7919e9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -22,7 +22,7 @@ public record SoundData(Key id, SoundValue volume, SoundValue pitch) { SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume); return new SoundData(id, volumeValue, pitchValue); } else { - throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); + throw new IllegalArgumentException("Illegal object id for sound data: " + obj.getClass()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java index de9774f5c..7d5e95ce6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java @@ -6,8 +6,8 @@ import java.util.Objects; * A generic class representing a pair of values. * This class provides methods to create and access pairs of values. * - * @param the type of the left value - * @param the type of the right value + * @param the id of the left value + * @param the id of the right value */ public record Pair(L left, R right) { @@ -16,8 +16,8 @@ public record Pair(L left, R right) { * * @param left the left value * @param right the right value - * @param the type of the left value - * @param the type of the right value + * @param the id of the left value + * @param the id of the right value * @return a new {@link Pair} with the specified values */ public static Pair of(final L left, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java index 703571af4..30472678c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java @@ -439,7 +439,7 @@ public class ReflectionUtils { public static List getMethods(@NotNull Class clazz, @NotNull Class returnType, @NotNull Class... parameterTypes) { List list = new ArrayList<>(); for (Method method : clazz.getMethods()) { - if (!returnType.isAssignableFrom(method.getReturnType()) // check type + if (!returnType.isAssignableFrom(method.getReturnType()) // check id || method.getParameterCount() != parameterTypes.length // check length ) continue; Class[] types = method.getParameterTypes(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index 1a98323d1..4461e5d93 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -137,13 +137,13 @@ public final class ResourceConfigUtils { try { return Integer.parseInt(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.int", e, s, option); } } case Boolean b -> { return b ? 1 : 0; } - default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option); + default -> throw new LocalizedResourceConfigException("warning.config.id.int", o.toString(), option); } } @@ -162,11 +162,11 @@ public final class ResourceConfigUtils { try { return Double.parseDouble(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.double", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.double", o.toString(), option); } } } @@ -183,14 +183,14 @@ public final class ResourceConfigUtils { try { return Float.parseFloat(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.float", e, s, option); } } case Number number -> { return number.floatValue(); } default -> { - throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.float", o.toString(), option); } } } @@ -206,15 +206,15 @@ public final class ResourceConfigUtils { case Number n -> { if (n.byteValue() == 0) return false; if (n.byteValue() == 1) return true; - throw new LocalizedResourceConfigException("warning.config.type.boolean", String.valueOf(n), option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", String.valueOf(n), option); } case String s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; - throw new LocalizedResourceConfigException("warning.config.type.boolean", s, option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", s, option); } default -> { - throw new LocalizedResourceConfigException("warning.config.type.boolean", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", o.toString(), option); } } } @@ -234,11 +234,11 @@ public final class ResourceConfigUtils { try { return Long.parseLong(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.long", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.long", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.type.long", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.long", o.toString(), option); } } } @@ -248,7 +248,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); } @SuppressWarnings("unchecked") @@ -259,7 +259,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); } public static Vector3f getAsVector3f(Object o, String option) { @@ -274,7 +274,7 @@ public final class ResourceConfigUtils { } else if (split.length == 1) { return new Vector3f(Float.parseFloat(split[0])); } else { - throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.id.vector3f", stringFormat, option); } } } @@ -293,7 +293,7 @@ public final class ResourceConfigUtils { } else if (split.length == 1) { return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0); } else { - throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.id.quaternionf", stringFormat, option); } } } @@ -311,7 +311,7 @@ public final class ResourceConfigUtils { double d = Double.parseDouble(split[0]); return new Vec3d(d, d, d); } else { - throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.id.vec3d", stringFormat, option); } } } @@ -344,50 +344,54 @@ public final class ResourceConfigUtils { } public static AABB getAsAABB(Object o, String option) { - if (o == null) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option); - } - if (o instanceof Number number) { - double min = -(number.doubleValue() / 2); - double max = number.doubleValue() / 2; - return new AABB(min, min, min, max, max, max); - } else { - double[] args; - if (o instanceof List list) { - args = new double[list.size()]; - for (int i = 0; i < args.length; i++) { - if (list.get(i) instanceof Number number) { - args[i] = number.doubleValue(); - } else { + switch (o) { + case null -> throw new LocalizedResourceConfigException("warning.config.id.aabb", "null", option); + case AABB aabb -> { + return aabb; + } + case Number number -> { + double min = -(number.doubleValue() / 2); + double max = number.doubleValue() / 2; + return new AABB(min, min, min, max, max, max); + } + default -> { + double[] args; + if (o instanceof List list) { + args = new double[list.size()]; + for (int i = 0; i < args.length; i++) { + if (list.get(i) instanceof Number number) { + args[i] = number.doubleValue(); + } else { + try { + args[i] = Double.parseDouble(list.get(i).toString()); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + } + } + } + } else { + String[] split = o.toString().split(","); + args = new double[split.length]; + for (int i = 0; i < args.length; i++) { try { - args[i] = Double.parseDouble(list.get(i).toString()); + args[i] = Double.parseDouble(split[i]); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); } } } - } else { - String[] split = o.toString().split(","); - args = new double[split.length]; - for (int i = 0; i < args.length; i++) { - try { - args[i] = Double.parseDouble(split[i]); - } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); - } + if (args.length == 1) { + return new AABB(-args[0] / 2, -args[0] / 2, -args[0] / 2, args[0] / 2, args[0] / 2, args[0] / 2); + } else if (args.length == 2) { + return new AABB(-args[0] / 2, -args[1] / 2, -args[0] / 2, args[0] / 2, args[1] / 2, args[0] / 2); + } else if (args.length == 3) { + return new AABB(-args[0] / 2, -args[1] / 2, -args[2] / 2, args[0] / 2, args[1] / 2, args[2] / 2); + } else if (args.length == 6) { + return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); + } else { + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); } } - if (args.length == 1) { - return new AABB(-args[0]/2, -args[0]/2, -args[0]/2, args[0]/2, args[0]/2, args[0]/2); - } else if (args.length == 2) { - return new AABB(-args[0]/2, -args[1]/2, -args[0]/2, args[0]/2, args[1]/2, args[0]/2); - } else if (args.length == 3) { - return new AABB(-args[0]/2, -args[1]/2, -args[2]/2, args[0]/2, args[1]/2, args[2]/2); - } else if (args.length == 6) { - return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); - } else { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); - } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 0be81388d..27fd39740 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -156,7 +156,7 @@ public final class SNBTReader extends DefaultStringReader { // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element type at pos " + getCursor()); + // throw new IllegalArgumentException("Error element id at pos " + getCursor()); if (!(element instanceof Number number)) throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java index e4cf6baf5..d91a2afaf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java @@ -6,9 +6,9 @@ import java.util.Objects; * A generic class representing a tuple with three values. * This class provides methods for creating and accessing tuples with three values. * - * @param the type of the left value - * @param the type of the middle value - * @param the type of the right value + * @param the id of the left value + * @param the id of the middle value + * @param the id of the right value */ public record Tuple(L left, M mid, R right) { @@ -18,9 +18,9 @@ public record Tuple(L left, M mid, R right) { * @param left the left value * @param mid the middle value * @param right the right value - * @param the type of the left value - * @param the type of the middle value - * @param the type of the right value + * @param the id of the left value + * @param the id of the middle value + * @param the id of the right value * @return a new {@link Tuple} with the specified values */ public static Tuple of(final L left, final M mid, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java index 8e3b3dc1a..02687838f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java @@ -8,18 +8,18 @@ public class TypeUtils { private TypeUtils() {} /** - * Checks if the provided object is of the specified type. + * Checks if the provided object is of the specified id. * If not, throws an IllegalArgumentException with a detailed message. * * @param object The object to check. - * @param expectedType The expected class type. - * @param The type parameter for expectedType. - * @return The object cast to the expected type if it matches. - * @throws IllegalArgumentException if the object's type does not match the expected type. + * @param expectedType The expected class id. + * @param The id parameter for expectedType. + * @return The object cast to the expected id if it matches. + * @throws IllegalArgumentException if the object's id does not match the expected id. */ public static T checkType(Object object, Class expectedType) { if (!expectedType.isInstance(object)) { - throw new IllegalArgumentException("Expected type: " + expectedType.getName() + + throw new IllegalArgumentException("Expected id: " + expectedType.getName() + ", but got: " + (object == null ? "null" : object.getClass().getName())); } return expectedType.cast(object); @@ -48,7 +48,7 @@ public class TypeUtils { } yield bytes; } - default -> throw new IllegalStateException("Unsupported type: " + type.toLowerCase(Locale.ENGLISH)); + default -> throw new IllegalStateException("Unsupported id: " + type.toLowerCase(Locale.ENGLISH)); }; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index 50eda3140..0aee4a4a1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRen import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.tick.*; +import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -18,6 +19,7 @@ import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,11 +32,12 @@ public class CEChunk { public final ChunkPos chunkPos; public final CESection[] sections; public final WorldHeight worldHeightAccessor; - public final Map blockEntities; // 从区域线程上访问,安全 - public final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 - public final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 - public final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 - public final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + private final Map entities; // 从区域线程上访问,安全 + private final Map blockEntities; // 从区域线程上访问,安全 + private final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 + private final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 + private final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + private final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock(); private volatile boolean dirty; private volatile boolean loaded; @@ -50,10 +53,11 @@ public class CEChunk { this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag itemDisplayBlockRenders) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag blockEntityRenders, @Nullable ListTag entities) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); @@ -80,15 +84,27 @@ public class CEChunk { } else { this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); } - if (itemDisplayBlockRenders != null) { - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(itemDisplayBlockRenders.size(), 10), 0.5f); - List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, itemDisplayBlockRenders); + if (blockEntityRenders != null) { + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), 10), 0.5f); + List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, blockEntityRenders); for (BlockPos pos : blockEntityRendererPoses) { this.addConstantBlockEntityRenderer(pos); } } else { this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); } + if (entities != null) { + this.entities = new Object2ObjectOpenHashMap<>(Math.max(entities.size(), 10), 0.5f); + for (CustomEntity entity : DefaultEntitySerializer.deserialize(world, entities)) { + this.entities.put(entity.uuid(), entity); + } + } else { + this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); + } + } + + public Map entities() { + return Collections.unmodifiableMap(this.entities); } public void spawnBlockEntities(Player player) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java index 3c331d193..46d340b54 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.InactiveBlockEntity; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.Key; @@ -37,11 +38,13 @@ public final class DefaultBlockEntitySerializer { CompoundTag data = tag.getCompound(i); Key id = Key.of(data.getString("id")); BlockEntityType type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id); + BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); + ImmutableBlockState blockState = chunk.getBlockState(pos); if (type == null) { Debugger.BLOCK.debug(() -> "Unknown block entity type: " + id); + BlockEntity blockEntity = new InactiveBlockEntity(pos, blockState, data); + blockEntities.add(blockEntity); } else { - BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); - ImmutableBlockState blockState = chunk.getBlockState(pos); if (blockState.blockEntityType() == type) { Optional entityBlockBehavior = blockState.behavior().getAs(EntityBlockBehavior.class); if (entityBlockBehavior.isPresent()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 2f851fe49..e98376e7e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.world.chunk.serialization; +import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -9,6 +10,9 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; +import java.util.UUID; + public final class DefaultChunkSerializer { private DefaultChunkSerializer() {} @@ -36,6 +40,10 @@ public final class DefaultChunkSerializer { if (!blockEntityRenders.isEmpty()) { chunkNbt.put("block_entity_renderers", blockEntityRenders); } + ListTag listTag = new ListTag(); + Map entities = chunk.entities(); + + return chunkNbt; } @@ -54,7 +62,8 @@ public final class DefaultChunkSerializer { } } ListTag blockEntities = chunkNbt.getList("block_entities"); - ListTag itemDisplayBlockRenders = chunkNbt.getList("block_entity_renderers"); - return new CEChunk(world, pos, sectionArray, blockEntities, itemDisplayBlockRenders); + ListTag blockEntityRenders = chunkNbt.getList("block_entity_renderers"); + ListTag entities = chunkNbt.getList("entities"); + return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, entities); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java new file mode 100644 index 000000000..df24bb530 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java @@ -0,0 +1,55 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import net.momirealms.craftengine.core.entity.CustomEntity; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.InactiveCustomEntity; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; +import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +public class DefaultEntitySerializer { + + public static ListTag serialize(@NotNull Collection entity) { + ListTag entities = new ListTag(); + for (CustomEntity customEntity : entity) { + if (customEntity.isValid()) { + entities.add(customEntity.saveAsTag()); + } + } + return entities; + } + + public static List deserialize(CEWorld world, ListTag entitiesTag) { + List entities = new ArrayList<>(entitiesTag.size()); + for (Tag tag : entitiesTag) { + if (tag instanceof CompoundTag entityTag) { + WorldPosition worldPosition = CustomEntity.readPos(world, entityTag); + UUID uuid = CustomEntity.readUUID(entityTag); + Key type = Key.of(entityTag.getString("type")); + CustomEntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(type); + if (entityType == null) { + InactiveCustomEntity entity = new InactiveCustomEntity(uuid, worldPosition, entityTag); + entities.add(entity); + } else { + CustomEntity entity = entityType.factory().create(uuid, worldPosition); + entity.loadCustomData(entityTag); + // 加载时无效则直接放弃 + if (entity.isValid()) { + entities.add(entity); + } + } + } + } + return entities; + } +} From 13c76821d4b3e136b01fdf87121f5982c1716283 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 01:52:52 +0800 Subject: [PATCH 17/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=89=E8=90=BD?= =?UTF-8?q?=E7=BB=8F=E9=AA=8C=E6=96=B9=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/BukkitBlockBehaviors.java | 2 + .../behavior/DropExperienceBlockBehavior.java | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index 11133b67d..090512f4c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -47,6 +47,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block"); public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); + public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -92,5 +93,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY); register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); + register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java new file mode 100644 index 000000000..ef629c558 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java @@ -0,0 +1,104 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.BlockSettings; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class DropExperienceBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final NumberProvider amount; + private final Condition conditions; + + public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Condition conditions) { + super(customBlock); + this.amount = amount; + this.conditions = conditions; + } + + @Override + public void spawnAfterBreak(Object thisBlock, Object[] args, Callable superMethod) { + boolean dropExperience = (boolean) args[4]; // 通常来说是 false + Item item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(args[3])); + if (!dropExperience) { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (state == null) { + return; + } + BlockSettings settings = state.settings(); + if (settings.requireCorrectTool()) { + if (item.isEmpty()) { + return; + } + boolean cannotBreak = !settings.isCorrectTool(item.id()) + && (!settings.respectToolComponent() + || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(args[3], state.customBlockState().literalObject())); + if (cannotBreak) { + return; + } + } + } + World world = BukkitWorldManager.instance().wrap(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1])); + BlockPos pos = LocationUtils.fromBlockPos(args[2]); + tryDropExperience(world, pos, item); + } + + private void tryDropExperience(World world, BlockPos pos, Item item) { + Vec3d dropPos = Vec3d.atCenterOf(pos); + ContextHolder holder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, new WorldPosition(world, dropPos)) + .withParameter(DirectContextParameters.ITEM_IN_HAND, item) + .build(); + LootContext context = new LootContext(world, null, 1.0f, holder); + if (this.conditions != null && !this.conditions.test(context)) { + return; + } + int finalAmount = this.amount.getInt(context); + if (finalAmount <= 0) { + return; + } + world.dropExp(dropPos, finalAmount); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 0)); + Condition conditions = null; + List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap); + if (conditionList.size() == 1) { + conditions = conditionList.getFirst(); + } else if (!conditionList.isEmpty()) { + conditions = new AllOfCondition<>(conditionList); + } + return new DropExperienceBlockBehavior(block, amount, conditions); + } + } +} From db27567b12d8e41e2e9a74f9455982e82dc227e1 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 01:58:07 +0800 Subject: [PATCH 18/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../default/configuration/blocks/topaz_ore.yml | 16 ++++++++++++---- .../configuration/templates/loot_tables.yml | 3 --- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml index 1a5a03297..2435ae04e 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml @@ -27,13 +27,17 @@ items: texture: minecraft:item/custom/topaz blocks: default:topaz_ore: + behavior: + type: drop_experience_block + amount: 3~7 + condition: + type: enchantment + predicate: minecraft:silk_touch<=0 loot: template: default:loot_table/ore arguments: ore_drop: default:topaz ore_block: default:topaz_ore - min_exp: 3 - max_exp: 7 settings: template: default:settings/ore arguments: @@ -45,13 +49,17 @@ blocks: arguments: path: minecraft:block/custom/topaz_ore default:deepslate_topaz_ore: + behavior: + type: drop_experience_block + amount: 3~7 + condition: + type: enchantment + predicate: minecraft:silk_touch<=0 loot: template: default:loot_table/ore arguments: ore_drop: default:topaz ore_block: default:deepslate_topaz_ore - min_exp: 3 - max_exp: 7 settings: template: default:settings/deepslate_ore arguments: diff --git a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml index 24d3c1878..c089ddca6 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml @@ -180,7 +180,6 @@ templates: # ore_block: the ore block # ore_drop: the drops of the ore material # ore_drop_count: the amount of the ore materials - # exp: the exp to drop default:loot_table/ore: pools: - rolls: 1 @@ -203,8 +202,6 @@ templates: formula: type: ore_drops - type: explosion_decay - - type: drop_exp - count: ${exp:-2~4} # Using Silk Touch or shears will cause the leaves block itself to drop. # Using Fortune, however, increases the drop rates of sticks and saplings. From d020e5dfd2647749ff4c566b7dd75b1ca78ba595 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 02:09:59 +0800 Subject: [PATCH 19/46] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AE=9E=E4=BD=93=E6=9E=84=E9=80=A0=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/entity/CustomEntity.java | 2 +- .../core/entity/InactiveCustomEntity.java | 4 +- .../core/entity/furniture/Furniture.java | 51 ++++++++++++++++--- .../furniture/FurnitureDataAccessor.java | 2 +- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java index 2ea1439a4..424174dd5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java @@ -13,7 +13,7 @@ public abstract class CustomEntity implements Cullable { protected WorldPosition position; protected boolean valid = true; - protected CustomEntity(CustomEntityType type, WorldPosition position, UUID uuid) { + protected CustomEntity(CustomEntityType type, UUID uuid, WorldPosition position) { this.position = position; this.type = type; this.uuid = uuid; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java index 1c870110a..9a4633d37 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java @@ -15,12 +15,12 @@ public class InactiveCustomEntity extends CustomEntity { private final CompoundTag data; public InactiveCustomEntity(UUID uuid, WorldPosition position) { - super(CustomEntityTypes.INACTIVE, position, uuid); + super(CustomEntityTypes.INACTIVE, uuid, position); this.data = INVALID_TAG; } public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { - super(CustomEntityTypes.INACTIVE, position, uuid); + super(CustomEntityTypes.INACTIVE, uuid, position); this.data = data; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 1b4661ba1..236a014b8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -2,18 +2,51 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; -public abstract class Furniture extends CustomEntity { - protected final FurnitureConfig config; - protected final FurnitureDataAccessor dataAccessor; +import java.util.Optional; +import java.util.UUID; - public Furniture(CustomEntityType type, WorldPosition position, FurnitureConfig config, CompoundTag data) { - super(type, position); - this.dataAccessor = new FurnitureDataAccessor(data); - this.config = config; +public abstract class Furniture extends CustomEntity { + protected FurnitureConfig config; + protected FurnitureDataAccessor dataAccessor; + + protected Key furnitureId; + protected CompoundTag inactiveFurnitureData; + + protected Furniture(CustomEntityType type, UUID uuid, WorldPosition position) { + super(type, uuid, position); + } + + @Override + protected void saveCustomData(CompoundTag tag) { + tag.putString("id", this.config.id().asMinimalString()); + + } + + @Override + public void loadCustomData(CompoundTag tag) { + this.furnitureId = readFurnitureId(tag); + this.dataAccessor = new FurnitureDataAccessor(tag.getCompound("data")); + Optional furnitureConfig = CraftEngine.instance().furnitureManager().furnitureById(this.furnitureId); + if (furnitureConfig.isPresent()) { + this.config = furnitureConfig.get(); + } else { + this.inactiveFurnitureData = tag; + } + } + + @Override + public CompoundTag saveAsTag() { + // 如果家具数据只是不活跃,则返回不活跃家具数据 + if (this.inactiveFurnitureData != null) { + return this.inactiveFurnitureData; + } + return super.saveAsTag(); } @NotNull @@ -25,4 +58,8 @@ public abstract class Furniture extends CustomEntity { public FurnitureDataAccessor dataAccessor() { return this.dataAccessor; } + + protected final Key readFurnitureId(CompoundTag tag) { + return Key.of(tag.getString("id")); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 07a6ccbb5..34f862f4f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -24,7 +24,7 @@ public class FurnitureDataAccessor { private final CompoundTag data; public FurnitureDataAccessor(CompoundTag data) { - this.data = data; + this.data = data == null ? new CompoundTag() : data; } public static FurnitureDataAccessor of(CompoundTag data) { From 0a6bd37c3638e856d00c68c2507024483ae4f3be Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 21:43:12 +0800 Subject: [PATCH 20/46] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84part2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BetterModelBlockEntityElementConfig.java | 8 +- .../ModelEngineBlockEntityElementConfig.java | 8 +- .../skript/event/EvtCustomClick.java | 2 +- .../skript/event/EvtCustomFurniture.java | 4 +- .../bukkit/api/CraftEngineFurniture.java | 4 +- .../api/event/FurnitureInteractEvent.java | 12 +- .../element/ItemBlockEntityElementConfig.java | 8 +- .../ItemDisplayBlockEntityElementConfig.java | 8 +- .../TextDisplayBlockEntityElementConfig.java | 8 +- .../bukkit/entity/BukkitEntity.java | 12 + .../bukkit/entity/data/BukkitEntityData.java | 2 +- .../entity/furniture/BukkitFurniture.java | 175 ++------ .../furniture/BukkitFurnitureManager.java | 417 +++++++++--------- .../furniture/FurnitureEventListener.java | 170 +++---- .../ItemDisplayFurnitureElementConfig.java | 12 +- .../hitbox/AbstractFurnitureHitBox.java | 57 +++ .../hitbox/BukkitFurnitureHitboxTypes.java | 15 + .../furniture/hitbox/BukkitHitBoxTypes.java | 15 - .../furniture/hitbox/CustomHitBoxConfig.java | 63 --- .../hitbox/HappyGhastHitBoxConfig.java | 139 ------ .../hitbox/InteractionFurnitureHitbox.java | 83 ++++ .../InteractionFurnitureHitboxConfig.java | 71 +++ .../hitbox/InteractionHitBoxConfig.java | 124 ------ .../furniture/hitbox/ShulkerHitBoxConfig.java | 361 --------------- .../bukkit/plugin/BukkitCraftEngine.java | 9 +- .../feature/DebugSpawnFurnitureCommand.java | 33 +- .../plugin/network/BukkitNetworkManager.java | 57 +-- .../bukkit/world/BukkitWorldManager.java | 8 + .../BlockEntityElementConfigFactory.java | 4 +- .../craftengine/core/entity/CustomEntity.java | 96 ---- .../core/entity/CustomEntityType.java | 14 - .../core/entity/CustomEntityTypeKeys.java | 10 - .../core/entity/CustomEntityTypes.java | 18 - .../craftengine/core/entity/Entity.java | 2 + .../core/entity/InactiveCustomEntity.java | 54 --- .../core/entity/data/EntityData.java | 4 +- .../furniture/AbstractFurnitureManager.java | 12 +- .../core/entity/furniture/AnchorType.java | 2 +- .../core/entity/furniture/Furniture.java | 198 +++++++-- .../entity/furniture/FurnitureConfig.java | 27 ++ .../entity/furniture/FurnitureVariant.java | 6 +- .../element/FurnitureElementConfig.java | 4 +- .../FurnitureElementConfigFactory.java | 4 +- ...ava => AbstractFurnitureHitBoxConfig.java} | 4 +- .../furniture/hitbox/AbstractHitBox.java | 56 --- .../furniture/hitbox/FurnitureHitBox.java | 29 ++ ...Config.java => FurnitureHitBoxConfig.java} | 6 +- .../hitbox/FurnitureHitBoxConfigFactory.java | 8 + ...oxTypes.java => FurnitureHitBoxTypes.java} | 14 +- .../core/entity/furniture/hitbox/HitBox.java | 19 - .../furniture/hitbox/HitBoxConfigFactory.java | 8 - .../entity/furniture/hitbox/HitBoxPart.java | 7 - .../core/entity/player/Player.java | 5 + .../core/pack/AbstractPackManager.java | 4 +- .../plugin/config/blockbench/BBModel.java | 2 +- .../function/RemoveFurnitureFunction.java | 4 +- .../function/ReplaceFurnitureFunction.java | 3 +- .../parameter/DirectContextParameters.java | 3 +- .../parameter/FurnitureParameterProvider.java | 4 +- .../core/registry/BuiltInRegistries.java | 6 +- .../craftengine/core/registry/Registries.java | 6 +- .../core/util/BlockEntityTickersList.java | 4 +- .../craftengine/core/world/WorldPosition.java | 38 +- .../craftengine/core/world/chunk/CEChunk.java | 51 +-- .../serialization/DefaultChunkSerializer.java | 11 +- .../DefaultEntitySerializer.java | 55 --- 66 files changed, 1011 insertions(+), 1676 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{AbstractHitBoxConfig.java => AbstractFurnitureHitBoxConfig.java} (79%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{HitBoxConfig.java => FurnitureHitBoxConfig.java} (64%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{HitBoxTypes.java => FurnitureHitBoxTypes.java} (65%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java index a6c936826..0ec8d06c9 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel; -import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -59,13 +58,12 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo return BetterModelBlockEntityElement.class; } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public BetterModelBlockEntityElementConfig create(Map arguments) { String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model"); - return (BlockEntityElementConfig) new BetterModelBlockEntityElementConfig( + return new BetterModelBlockEntityElementConfig( model, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java index 0a4c5c41c..c34ef8d12 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.compatibility.model.modelengine; -import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -49,13 +48,12 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo return ModelEngineBlockEntityElement.class; } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ModelEngineBlockEntityElementConfig create(Map arguments) { String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model"); - return (BlockEntityElementConfig) new ModelEngineBlockEntityElementConfig( + return new ModelEngineBlockEntityElementConfig( model, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java index 86b66d401..05ac9f064 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java @@ -49,7 +49,7 @@ public class EvtCustomClick extends SkriptEvent { EventValues.registerEventValue(FurnitureInteractEvent.class, Location.class, FurnitureInteractEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureInteractEvent.class, Player.class, FurnitureInteractEvent::player, EventValues.TIME_NOW); EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, event -> null, EventValues.TIME_NOW); - EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java index 2afbea401..41128d050 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java @@ -30,14 +30,14 @@ public class EvtCustomFurniture extends SkriptEvent { .description("Called when a furniture is broken by a player."); EventValues.registerEventValue(FurnitureBreakEvent.class, Location.class, FurnitureBreakEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureBreakEvent.class, Player.class, FurnitureBreakEvent::player, EventValues.TIME_NOW); - EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]") .description("Called when a player places a furniture."); EventValues.registerEventValue(FurniturePlaceEvent.class, Location.class, FurniturePlaceEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurniturePlaceEvent.class, Player.class, FurniturePlaceEvent::player, EventValues.TIME_NOW); - EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurniturePlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 8f61765d3..39ae861f4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -301,14 +301,14 @@ public final class CraftEngineFurniture { if (!furniture.isValid()) return; Location location = ((BukkitFurniture) furniture).getDropLocation(); furniture.destroy(); - LootTable lootTable = (LootTable) furniture.config().lootTable(); + LootTable lootTable = (LootTable) furniture.config.lootTable(); World world = new BukkitWorld(location.getWorld()); WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ()); if (dropLoot && lootTable != null) { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor.item().orElse(null)); if (player != null) { Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); builder.withParameter(DirectContextParameters.PLAYER, player) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java index ebe8850fb..2fd4cc6bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -16,23 +16,23 @@ public final class FurnitureInteractEvent extends PlayerEvent implements Cancell private final BukkitFurniture furniture; private final InteractionHand hand; private final Location interactionPoint; - private final HitBox hitBox; + private final FurnitureHitBox furnitureHitBox; public FurnitureInteractEvent(@NotNull Player player, @NotNull BukkitFurniture furniture, @NotNull InteractionHand hand, @NotNull Location interactionPoint, - @NotNull HitBox hitBox) { + @NotNull FurnitureHitBox furnitureHitBox) { super(player); this.furniture = furniture; this.hand = hand; this.interactionPoint = interactionPoint; - this.hitBox = hitBox; + this.furnitureHitBox = furnitureHitBox; } @NotNull - public HitBox hitBox() { - return hitBox; + public FurnitureHitBox hitBox() { + return furnitureHitBox; } @NotNull diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index 01151fab5..fc6ca3df7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; @@ -76,13 +75,12 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ItemBlockEntityElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); - return (BlockEntityElementConfig) new ItemBlockEntityElementConfig( + return new ItemBlockEntityElementConfig( player -> BukkitItemManager.instance().createWrappedItem(itemId, player), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position") ); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java index dbda0e1e0..a8b02c0b0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.entity.display.Billboard; @@ -158,13 +157,12 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ItemDisplayBlockEntityElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); - return (BlockEntityElementConfig) new ItemDisplayBlockEntityElementConfig( + return new ItemDisplayBlockEntityElementConfig( player -> BukkitItemManager.instance().createWrappedItem(itemId, player), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java index dc450d1fe..5b96a9611 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java @@ -4,7 +4,6 @@ import com.google.common.base.Objects; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.util.ComponentUtils; -import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.entity.display.Billboard; @@ -135,13 +134,12 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public TextDisplayBlockEntityElementConfig create(Map arguments) { String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text"); - return (BlockEntityElementConfig) new TextDisplayBlockEntityElementConfig( + return new TextDisplayBlockEntityElementConfig( text, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java index 76dfed5d0..3d4780635 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java @@ -2,12 +2,14 @@ package net.momirealms.craftengine.bukkit.entity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.data.EntityData; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import java.lang.ref.WeakReference; import java.util.UUID; @@ -38,6 +40,11 @@ public class BukkitEntity extends AbstractEntity { public void tick() { } + @Override + public WorldPosition position() { + return LocationUtils.toWorldPosition(platformEntity().getLocation()); + } + @Override public int entityID() { return platformEntity().getEntityId(); @@ -78,6 +85,11 @@ public class BukkitEntity extends AbstractEntity { return EntityUtils.getEntityType(platformEntity()); } + @Override + public boolean isValid() { + return platformEntity().isValid(); + } + @Override public String name() { return platformEntity().getName(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java index 2e3ee3eae..814eacd5b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java @@ -39,7 +39,7 @@ public class BukkitEntityData implements EntityData { } @Override - public Object create(Object entityDataAccessor, Object value) { + public Object create(Object entityDataAccessor, T value) { return EntityDataValue.create(entityDataAccessor, value); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index fe9bec331..7236d1ba0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,171 +1,68 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; -import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.QuaternionUtils; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; import org.bukkit.entity.Entity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.persistence.PersistentDataType; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; import java.util.Optional; -import java.util.UUID; -public class BukkitFurniture implements Furniture { - private static final UUID INVALID_UUID = new UUID(0, 0); - @NotNull - private final FurnitureConfig config; - @NotNull - private final FurnitureDataAccessor dataAccessor; - - private WeakReference baseEntity; - private FurnitureVariant currentVariant; +public class BukkitFurniture extends Furniture { + private final WeakReference metaEntity; private Location location; - private boolean valid; - private UUID uuid; - private int entityId; - private FurnitureElement[] elements; - - public BukkitFurniture(@NotNull FurnitureConfig config, - @NotNull CompoundTag data) { - this.dataAccessor = FurnitureDataAccessor.of(data); - this.currentVariant = Optional.ofNullable(getVariant()).orElseGet(config::anyVariant); - this.config = config; - this.baseEntity = new WeakReference<>(null); - this.valid = false; - this.entityId = -1; - this.uuid = INVALID_UUID; + public BukkitFurniture(ItemDisplay metaEntity, FurnitureConfig config, FurnitureDataAccessor data) { + super(new BukkitEntity(metaEntity), data, config); + this.metaEntity = new WeakReference<>(metaEntity); + this.location = metaEntity.getLocation(); + this.setVariant(super.config().getVariant(data)); } - private void sync() { - WorldPosition position = position(); - FurnitureElementConfig[] elementConfigs = this.currentVariant.elements(); - FurnitureElement[] elements = new FurnitureElement[elementConfigs.length]; - for (int i = 0; i < elementConfigs.length; i++) { - FurnitureElement o = elementConfigs[i].create(position); - elements[i] = o; + @Override + public void addCollidersToWorld() { + Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); + for (Collider entity : super.colliders) { + FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); + bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); + bukkitEntity.setPersistent(false); } - this.elements = elements; - } - - public void addToWorld(Location location) { - this.setLocation(location); - this.valid = true; - Entity baseEntity = null; // fixme 处理生成 - this.baseEntity = new WeakReference<>(baseEntity); - this.uuid = baseEntity.getUniqueId(); - this.entityId = baseEntity.getEntityId(); - this.sync(); - } - - @Override - public void show(Player player) { - for (FurnitureElement element : this.elements) { - element.show(player); - } - } - - @Override - public void hide(Player player) { - for (FurnitureElement element : this.elements) { - element.hide(player); - } - } - - @Nullable - @Override - public CullingData cullingData() { - return this.config.cullingData(); - } - - @NotNull - @Override - public FurnitureConfig config() { - return this.config; - } - - @NotNull - @Override - public FurnitureDataAccessor dataAccessor() { - return this.dataAccessor; - } - - @Override - public WorldPosition position() { - return LocationUtils.toWorldPosition(this.location); - } - - @Override - public boolean isValid() { - return this.valid; } @Override public void destroy() { - this.valid = false; - Optional.ofNullable(this.baseEntity.get()).ifPresent(Entity::remove); - } - - @Override - public UUID uuid() { - return this.uuid; - } - - @Override - public int entityId() { - return this.entityId; - } - - public void teleport(WorldPosition position) { - Location newLocation = LocationUtils.toLocation(position); - if (newLocation.equals(this.location)) { - return; + Optional.ofNullable(this.metaEntity.get()).ifPresent(Entity::remove); + for (Collider entity : super.colliders) { + entity.destroy(); } - this.setLocation(newLocation); - this.sync(); + } + + // 获取掉落物的位置,受到家具变种的影响 + public Location getDropLocation() { + Optional dropOffset = this.getCurrentVariant().dropOffset(); + if (dropOffset.isEmpty()) { + return this.location; + } + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); + return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); } public Location location() { return location; } - private void setLocation(Location location) { - this.location = location; - } - - public FurnitureVariant getVariant() { - return this.config.getVariant(this.dataAccessor.variant().orElseGet(this.config::anyVariantName)); - } - - public void setVariant(String variant) { - FurnitureVariant newVariant = this.config.getVariant(variant); - if (newVariant != this.currentVariant) { - this.currentVariant = newVariant; - this.sync(); - } - } - - // 获取掉落物的位置,受到家具变种的影响 - public Location getDropLocation() { - Optional dropOffset = this.getVariant().dropOffset(); - if (dropOffset.isEmpty()) { - return location(); - } - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); - Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); - return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); + public Entity getBukkitEntity() { + return this.metaEntity.get(); } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 969727e89..f2edfe2df 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -1,35 +1,52 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig; +import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionFurnitureHitboxConfig; +import net.momirealms.craftengine.bukkit.nms.CollisionEntity; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.*; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; import org.bukkit.entity.Interaction; +import org.bukkit.entity.ItemDisplay; import org.bukkit.event.HandlerList; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY); public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION); + private static BukkitFurnitureManager instance; + public static Class COLLISION_ENTITY_CLASS = Interaction.class; public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION; public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION; - private static BukkitFurnitureManager instance; + private final BukkitCraftEngine plugin; - private final Map furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f); - private final Map furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f); + + private final Map byMetaEntityId = new ConcurrentHashMap<>(256, 0.5f); + private final Map byEntityId = new ConcurrentHashMap<>(512, 0.5f); // Event listeners private final FurnitureEventListener furnitureEventListener; @@ -41,7 +58,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { super(plugin); instance = this; this.plugin = plugin; - this.furnitureEventListener = new FurnitureEventListener(this); + this.furnitureEventListener = new FurnitureEventListener(this, plugin.worldManager()); } @Override @@ -75,38 +92,43 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public void delayedInit() { + // 确定碰撞箱实体类型 + COLLISION_ENTITY_TYPE = Config.colliderType(); COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class; NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT; - COLLISION_ENTITY_TYPE = Config.colliderType(); + + // 注册事件 Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin()); -// if (VersionHelper.isFolia()) { -// BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); -// for (World world : Bukkit.getWorlds()) { -// List entities = world.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay display) { -// taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); -// } else if (entity instanceof Interaction interaction) { -// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); -// } else if (entity instanceof Boat boat) { -// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); -// } -// } -// } -// } else { -// for (World world : Bukkit.getWorlds()) { -// List entities = world.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay display) { -// handleBaseEntityLoadEarly(display); -// } else if (entity instanceof Interaction interaction) { -// handleCollisionEntityLoadOnEntitiesLoad(interaction); -// } else if (entity instanceof Boat boat) { -// handleCollisionEntityLoadOnEntitiesLoad(boat); -// } -// } -// } -// } + + // 对世界上已有实体的记录 + if (VersionHelper.isFolia()) { + BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); + for (World world : Bukkit.getWorlds()) { + List entities = world.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay display) { + taskExecutor.accept(entity, () -> handleMetaEntityDuringChunkLoad(display)); + } else if (entity instanceof Interaction interaction) { + taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(interaction)); + } else if (entity instanceof Boat boat) { + taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(boat)); + } + } + } + } else { + for (World world : Bukkit.getWorlds()) { + List entities = world.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay display) { + handleMetaEntityDuringChunkLoad(display); + } else if (entity instanceof Interaction interaction) { + handleCollisionEntityDuringChunkLoad(interaction); + } else if (entity instanceof Boat boat) { + handleCollisionEntityDuringChunkLoad(boat); + } + } + } + } } @Override @@ -117,183 +139,180 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public boolean isFurnitureRealEntity(int entityId) { - return this.furnitureByRealEntityId.containsKey(entityId); + return this.byMetaEntityId.containsKey(entityId); } @Nullable @Override public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) { - return this.furnitureByRealEntityId.get(entityId); + return this.byMetaEntityId.get(entityId); } @Override @Nullable public BukkitFurniture loadedFurnitureByEntityId(int entityId) { - return this.furnitureByEntityId.get(entityId); + return this.byEntityId.get(entityId); } -// -// protected void handleBaseEntityUnload(Entity entity) { -// int id = entity.getEntityId(); -// BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); -// if (furniture != null) { -// Location location = entity.getLocation(); -// boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// if (!isPreventing) { -// furniture.destroySeats(); -// } -// for (int sub : furniture.entityIds()) { -// this.furnitureByEntityId.remove(sub); -// } -// } -// } -// -// protected void handleCollisionEntityUnload(Entity entity) { -// int id = entity.getEntityId(); -// this.furnitureByRealEntityId.remove(id); -// } -// -// @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` -// protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { -// // must be a furniture item -// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); -// if (id == null) return; -// -// Key key = Key.of(id); -// Optional optionalFurniture = furnitureById(key); -// if (optionalFurniture.isEmpty()) return; -// -// FurnitureConfig customFurniture = optionalFurniture.get(); -// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); -// if (previous != null) return; -// -// Location location = display.getLocation(); -// boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); -// boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// if (above1_20_1) { -// if (!preventChange) { -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// furniture.initializeColliders(); -// for (Player player : display.getTrackedPlayers()) { -// BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); -// this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); -// } -// } -// } else { -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// for (Player player : display.getTrackedPlayers()) { -// BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); -// serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); -// this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); -// } -// if (preventChange) { -// this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// } else { -// furniture.initializeColliders(); -// } -// } -// if (depth > 2) return; -// this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// } -// -// protected void handleCollisionEntityLoadLate(Entity entity, int depth) { -// // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit -// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { -// return; -// } -// // not a collision entity -// Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); -// if (flag == null || flag != 1) { -// return; -// } -// -// Location location = entity.getLocation(); -// World world = location.getWorld(); -// int chunkX = location.getBlockX() >> 4; -// int chunkZ = location.getBlockZ() >> 4; -// if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { -// entity.remove(); -// return; -// } -// -// if (depth > 2) return; -// plugin.scheduler().sync().runLater(() -> { -// handleCollisionEntityLoadLate(entity, depth + 1); -// }, 1, world, chunkX, chunkZ); -// } -// -// public void handleBaseEntityLoadEarly(ItemDisplay display) { -// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); -// if (id == null) return; -// // Remove the entity if it's not a valid furniture -// if (Config.handleInvalidFurniture()) { -// String mapped = Config.furnitureMappings().get(id); -// if (mapped != null) { -// if (mapped.isEmpty()) { -// display.remove(); -// return; -// } else { -// id = mapped; -// display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); -// } -// } -// } -// -// Key key = Key.of(id); -// Optional optionalFurniture = furnitureById(key); -// if (optionalFurniture.isPresent()) { -// FurnitureConfig customFurniture = optionalFurniture.get(); -// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); -// if (previous != null) return; -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// furniture.initializeColliders(); // safely do it here -// } -// } -// -// public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { -// // faster -// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { -// collisionEntity.remove(); -// return; -// } -// -// // not a collision entity -// Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); -// if (flag == null || flag != 1) { -// return; -// } -// -// collisionEntity.remove(); -// } -// -// private FurnitureDataAccessor getFurnitureExtraData(Entity baseEntity) throws IOException { -// byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); -// if (extraData == null) return FurnitureDataAccessor.builder().build(); -// return FurnitureDataAccessor.fromBytes(extraData); -// } -// -// private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { -// FurnitureDataAccessor extraData; -// try { -// extraData = getFurnitureExtraData(display); -// } catch (IOException e) { -// extraData = FurnitureDataAccessor.builder().build(); -// plugin.logger().warn("Furniture extra data could not be loaded", e); -// } -// BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); -// this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); -// for (int entityId : bukkitFurniture.entityIds()) { -// this.furnitureByEntityId.put(entityId, bukkitFurniture); -// } -// for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { -// int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); -// this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); -// } -// return bukkitFurniture; -// } + protected void handleMetaEntityUnload(Entity entity) { + int id = entity.getEntityId(); + BukkitFurniture furniture = this.byMetaEntityId.remove(id); + if (furniture != null) { + Location location = entity.getLocation(); + // 区块还在加载的时候,就重复卸载了 + boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (!isPreventing) { + furniture.destroySeats(); + } + for (int sub : furniture.entityIds()) { + this.byEntityId.remove(sub); + } + } + } + + protected void handleCollisionEntityUnload(Entity entity) { + int id = entity.getEntityId(); + this.byMetaEntityId.remove(id); + } + + private boolean isEntitiesLoaded(Location location) { + CEWorld ceWorld = this.plugin.worldManager().getWorld(location.getWorld()); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (ceChunk == null) return false; + return ceChunk.isEntitiesLoaded(); + } + + protected void handleMetaEntityDuringChunkLoad(ItemDisplay entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + + // 获取家具pdc + String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); + if (id == null) return; + + // 处理无效的家具 + if (Config.handleInvalidFurniture()) { + String mapped = Config.furnitureMappings().get(id); + if (mapped != null) { + if (mapped.isEmpty()) { + entity.remove(); + return; + } else { + id = mapped; + entity.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); + } + } + } + + // 获取家具配置 + Key key = Key.of(id); + Optional optionalFurniture = furnitureById(key); + if (optionalFurniture.isEmpty()) return; + + // 已经在其他事件里加载过了 + FurnitureConfig customFurniture = optionalFurniture.get(); + BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); + if (previous != null) return; + + BukkitFurniture furniture = addNewFurniture(entity, customFurniture); + furniture.addCollidersToWorld(); + } + + protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + + // 获取家具pdc + String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); + if (id == null) return; + + // 这个区块还处于加载实体中,这个时候不处理 + if (!isEntitiesLoaded(entity.getLocation())) { + return; + } + + // 获取家具配置 + Key key = Key.of(id); + Optional optionalFurniture = furnitureById(key); + if (optionalFurniture.isEmpty()) return; + + // 已经在其他事件里加载过了 + FurnitureConfig customFurniture = optionalFurniture.get(); + BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); + if (previous != null) return; + + BukkitFurniture furniture = addNewFurniture(entity, customFurniture); + furniture.addCollidersToWorld(); + } + + protected void handleCollisionEntityAfterChunkLoad(Entity entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + // 如果是碰撞实体,那么就忽略 + if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { + return; + } + // 看看有没有碰撞实体的pdc + Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); + if (flag == null || flag != 1) { + return; + } + // 实体未加载 + if (!isEntitiesLoaded(entity.getLocation())) { + return; + } + // 移除被WorldEdit错误复制的碰撞实体 + entity.remove(); + } + + public void handleCollisionEntityDuringChunkLoad(Entity collisionEntity) { + // faster + if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { + collisionEntity.remove(); + return; + } + + // not a collision entity + Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); + if (flag == null || flag != 1) { + return; + } + + collisionEntity.remove(); + } + + private FurnitureDataAccessor getFurnitureDataAccessor(Entity baseEntity) { + byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); + if (extraData == null) return new FurnitureDataAccessor(null); + try { + return FurnitureDataAccessor.fromBytes(extraData); + } catch (IOException e) { + // 损坏了?一般不会 + return new FurnitureDataAccessor(null); + } + } + + private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { + BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); + this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); + for (int entityId : bukkitFurniture.entityIds()) { + this.byEntityId.put(entityId, bukkitFurniture); + } + for (Collider collisionEntity : bukkitFurniture.colliders()) { + int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); + this.byEntityId.put(collisionEntityId, bukkitFurniture); + } + return bukkitFurniture; + } @Override - protected HitBoxConfig defaultHitBox() { - return InteractionHitBoxConfig.DEFAULT; + protected FurnitureHitBoxConfig defaultHitBox() { + return InteractionFurnitureHitboxConfig.DEFAULT; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index 5fa382018..a8b0e8da5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -1,85 +1,107 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; +import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +import java.util.List; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; + private final BukkitWorldManager worldManager; - public FurnitureEventListener(final BukkitFurnitureManager manager) { + public FurnitureEventListener(final BukkitFurnitureManager manager, final BukkitWorldManager worldManager) { this.manager = manager; + this.worldManager = worldManager; } -// /* -// * Load Entities -// */ -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onEntitiesLoadEarly(EntitiesLoadEvent event) { -// List entities = event.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadEarly(itemDisplay); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onWorldLoad(WorldLoadEvent event) { -// List entities = event.getWorld().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadEarly(itemDisplay); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onEntityLoad(EntityAddToWorldEvent event) { -// Entity entity = event.getEntity(); -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadLate(itemDisplay, 0); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadLate(entity, 0); -// } -// } -// -// /* -// * Unload Entities -// */ -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onChunkUnload(ChunkUnloadEvent event) { -// Entity[] entities = event.getChunk().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onWorldUnload(WorldUnloadEvent event) { -// List entities = event.getWorld().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onEntityUnload(EntityRemoveFromWorldEvent event) { -// Entity entity = event.getEntity(); -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } + /* + * Load Entities + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onEntitiesLoadEarly(EntitiesLoadEvent event) { + List entities = event.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityDuringChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityDuringChunkLoad(entity); + } + } + CEWorld world = this.worldManager.getWorld(event.getWorld()); + CEChunk ceChunk = world.getChunkAtIfLoaded(event.getChunk().getChunkKey()); + if (ceChunk != null) { + ceChunk.setEntitiesLoaded(true); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onWorldLoad(WorldLoadEvent event) { + List entities = event.getWorld().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityDuringChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityDuringChunkLoad(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onEntityLoad(EntityAddToWorldEvent event) { + Entity entity = event.getEntity(); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityAfterChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityAfterChunkLoad(entity); + } + } + + /* + * Unload Entities + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onChunkUnload(ChunkUnloadEvent event) { + Entity[] entities = event.getChunk().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onWorldUnload(WorldUnloadEvent event) { + List entities = event.getWorld().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEntityUnload(EntityRemoveFromWorldEvent event) { + Entity entity = event.getEntity(); + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 8edc5d489..fe52c4c83 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; @@ -15,7 +15,6 @@ import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.joml.Quaternionf; @@ -126,18 +125,17 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig } @Override - public ItemDisplayFurnitureElement create(@NotNull WorldPosition position) { + public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { return new ItemDisplayFurnitureElement(this); } - public static class Factory implements FurnitureElementConfigFactory { + public static class Factory implements FurnitureElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public FurnitureElementConfig create(Map arguments) { + public ItemDisplayFurnitureElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); - return (FurnitureElementConfig) new ItemDisplayFurnitureElementConfig( + return new ItemDisplayFurnitureElementConfig( (player, colorSource) -> { Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); if (applyDyedColor && colorSource != null && wrappedItem != null) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java new file mode 100644 index 000000000..5ae471042 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; +import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.sparrow.nbt.CompoundTag; + +public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { + protected final Furniture furniture; + protected Seat[] seats; + + public AbstractFurnitureHitBox(Furniture furniture, FurnitureHitBoxConfig config) { + this.furniture = furniture; + this.seats = createSeats(config); + } + + @SuppressWarnings("unchecked") + private Seat[] createSeats(FurnitureHitBoxConfig config) { + SeatConfig[] seatConfigs = config.seats(); + Seat[] seats = new Seat[seatConfigs.length]; + for (int i = 0; i < seatConfigs.length; i++) { + seats[i] = new BukkitSeat<>(this, seatConfigs[i]); + } + return seats; + } + + @Override + public void saveCustomData(CompoundTag data) { + data.putString("type", "furniture"); + // 用于通过座椅找到原始家具 + data.putInt("entity_id", this.furniture.entityId()); + } + + @Override + public Seat[] seats() { + return this.seats; + } + + protected Collider createCollider(World world, Vec3d position, AABB ceAABB, boolean canCollide, boolean blocksBuilding, boolean canBeHitByProjectile) { + Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); + return new BukkitCollider(world.serverWorld(), nmsAABB, position.x, position.y, position.z, canBeHitByProjectile, canCollide, blocksBuilding); + } + + protected Object createDespawnPacket(int[] entityIds) { + return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java new file mode 100644 index 000000000..c1ef83ddb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; + +public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { + + public static void init() {} + + static { + register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); +// register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); +// register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); +// register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java deleted file mode 100644 index 017824a2a..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; - -public class BukkitHitBoxTypes extends HitBoxTypes { - - public static void init() {} - - static { - register(INTERACTION, InteractionHitBoxConfig.FACTORY); - register(SHULKER, ShulkerHitBoxConfig.FACTORY); - register(HAPPY_GHAST, HappyGhastHitBoxConfig.FACTORY); - register(CUSTOM, CustomHitBoxConfig.FACTORY); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java deleted file mode 100644 index 2b5abc32e..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractHitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.entity.EntityType; -import org.joml.Vector3f; - -import java.util.Map; - -public class CustomHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - private final float scale; - private final EntityType entityType; - - public CustomHitBoxConfig(SeatConfig[] seats, - Vector3f position, - EntityType type, - float scale, - boolean blocksBuilding, - boolean canBeHitByProjectile) { - super(seats, position, false, blocksBuilding, canBeHitByProjectile); - this.scale = scale; - this.entityType = type; - } - - public EntityType entityType() { - return this.entityType; - } - - public float scale() { - return this.scale; - } - - @Override - public Key type() { - return HitBoxTypes.CUSTOM; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); - String type = (String) arguments.getOrDefault("entity-id", "slime"); - EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); - if (entityType == null) { - throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); - } - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new CustomHitBoxConfig(SeatConfig.fromObj(arguments.get("seats")), position, entityType, scale, blocksBuilding, canBeHitByProjectile); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java deleted file mode 100644 index df7d249fe..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java +++ /dev/null @@ -1,139 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class HappyGhastHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - private final double scale; - private final boolean hardCollision; - private final List cachedValues = new ArrayList<>(); - - public HappyGhastHitBoxConfig(SeatConfig[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.scale = scale; - this.hardCollision = hardCollision; - HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues); - HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI - HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible - } - - @Override - public Key type() { - return HitBoxTypes.HAPPY_GHAST; - } - - public double scale() { - return this.scale; - } - - public boolean hardCollision() { - return this.hardCollision; - } - - @Override - public void initPacketsAndColliders(int[] entityIds, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[0], List.copyOf(this.cachedValues)), true); - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[0], Collections.singletonList(attributeInstance)), false); - } - if (this.hardCollision) { - collider.accept(this.createCollider(position.world(), offset, x, y, z, entityIds[0], aabb)); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct custom hitbox spawn packet", e); - } - } - - public Collider createCollider(World world, Vector3f offset, double x, double y, double z, int entityId, Consumer aabb) { - AABB ceAABB = createAABB(offset, x, y, z); - Object level = world.serverWorld(); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z))); - return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); - } - - public AABB createAABB(Vector3f offset, double x, double y, double z) { - double baseSize = 4.0 * this.scale; - double halfSize = baseSize * 0.5; - double minX = x - halfSize + offset.x(); - double maxX = x + halfSize + offset.x(); - double minY = y + offset.y(); - double maxY = y + baseSize + offset.y(); - double minZ = z - halfSize - offset.z(); - double maxZ = z + halfSize - offset.z(); - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - if (!this.hardCollision) return; - Vector3f offset = conjugated.transform(new Vector3f(position())); - AABB aabb = createAABB(offset, x, y, z); - aabbs.accept(aabb); - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - if (!VersionHelper.isOrAbove1_21_6()) { - throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+"); - } - double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale"); - boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision"); - boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new HappyGhastHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"), - scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision - ); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java new file mode 100644 index 000000000..9f8bc0366 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { + private final InteractionFurnitureHitboxConfig config; + private final Vec3d position; + private final AABB aabb; + private final Collider collider; + private final int[] entityIds; + private final Object spawnPacket; + private final Object despawnPacket; + + public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + this.position = Furniture.getRelativePosition(position, config.position()); + this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); + this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this.entityIds = new int[] {interactionId}; + List values = new ArrayList<>(3); + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + ), + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + )); + this.despawnPacket = createDespawnPacket(this.entityIds); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public AABB aabb() { + return this.aabb; + } + + @Override + public Vec3d position() { + return this.position; + } + + @Override + public Collider collider() { + return this.collider; + } + + @Override + public int[] virtualEntityIds() { + return this.entityIds; + } + + public InteractionFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java new file mode 100644 index 000000000..2a1efd59f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -0,0 +1,71 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.joml.Vector3f; + +import java.util.Map; + +public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, new Vector3f(1,1,1), true); + + public final Vector3f size; + public final boolean responsive; + + public InteractionFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + Vector3f size, + boolean responsive) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.size = size; + this.responsive = responsive; + } + + public Vector3f size() { + return size; + } + + public boolean responsive() { + return responsive; + } + + @Override + public InteractionFurnitureHitbox create(Furniture furniture) { + return new InteractionFurnitureHitbox(furniture, this); + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public InteractionFurnitureHitboxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float width; + float height; + if (arguments.containsKey("scale")) { + String[] split = arguments.get("scale").toString().split(","); + width = Float.parseFloat(split[0]); + height = Float.parseFloat(split[1]); + } else { + width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width"); + height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height"); + } + boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); + boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new InteractionFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, canUseOn, blocksBuilding, canBeHitByProjectile, + new Vector3f(width, height, width), + interactive + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java deleted file mode 100644 index 0d29c0ad2..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java +++ /dev/null @@ -1,124 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class InteractionHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - public static final InteractionHitBoxConfig DEFAULT = new InteractionHitBoxConfig(new SeatConfig[0], new Vector3f(), new Vector3f(1,1,1), true, false, false, false); - - private final Vector3f size; - private final boolean responsive; - private final List cachedValues = new ArrayList<>(); - - public InteractionHitBoxConfig(SeatConfig[] seats, Vector3f position, Vector3f size, boolean responsive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.size = size; - this.responsive = responsive; - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); - } - - public boolean responsive() { - return this.responsive; - } - - public Vector3f size() { - return this.size; - } - - @Override - public Key type() { - return HitBoxTypes.INTERACTION; - } - - @Override - public void initPacketsAndColliders(int[] entityId, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId[0], UUID.randomUUID(), vec3d.x, vec3d.y, vec3d.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); - aabb.accept(new HitBoxPart(entityId[0], AABB.fromInteraction(vec3d, this.size.x, this.size.y), vec3d)); - if (blocksBuilding() || this.canBeHitByProjectile()) { - AABB ceAABB = AABB.fromInteraction(vec3d, this.size.x, this.size.y); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding())); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - if (blocksBuilding()) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y); - aabbs.accept(ceAABB); - } - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float width; - float height; - if (arguments.containsKey("scale")) { - String[] split = arguments.get("scale").toString().split(","); - width = Float.parseFloat(split[0]); - height = Float.parseFloat(split[1]); - } else { - width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width"); - height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height"); - } - boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); - boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new InteractionHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - position, - new Vector3f(width, height, width), - interactive, canUseOn, blocksBuilding, canBeHitByProjectile - ); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java deleted file mode 100644 index f24f79b10..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java +++ /dev/null @@ -1,361 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; -import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class ShulkerHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - // 1.20.6+ - private final float scale; - private final byte peek; - private final boolean interactive; - private final boolean interactionEntity; - private final Direction direction; - private final List cachedShulkerValues = new ArrayList<>(); - private final DirectionalShulkerSpawner spawner; - private final AABBCreator aabbCreator; - - public ShulkerHitBoxConfig(SeatConfig[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.direction = direction; - this.scale = scale; - this.peek = peek; - this.interactive = interactive; - this.interactionEntity = interactionEntity; - - ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues); - ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues); - ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); - ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); - ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI - ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible - - float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; - List cachedInteractionValues = new ArrayList<>(); - if (this.direction == Direction.UP) { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - collider.accept(this.createCollider(Direction.UP, world, offset, x, y, z, entityIds[1], aabb)); - if (interactionEntity) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); - } else if (this.direction == Direction.DOWN) { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb)); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP))), false); - if (interactionEntity) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); - } else { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(scale + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); - Direction shulkerDirection = shulkerAnchor.opposite(); - collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb)); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor)))), false); - if (interactionEntity) { - // first interaction - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - // second interaction - double distance = shulkerHeight - scale; - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); - aabb.accept(new HitBoxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> { - Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); - Direction shulkerDirection = shulkerAnchor.opposite(); - return createAABB(shulkerDirection, offset, x, y, z); - }; - } - } - - public Collider createCollider(Direction direction, World world, Vector3f offset, double x, double y, double z, int entityId, Consumer aabb) { - AABB ceAABB = createAABB(direction, offset, x, y, z); - Object level = world.serverWorld(); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z))); - return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); - } - - public AABB createAABB(Direction direction, Vector3f offset, double x, double y, double z) { - float peek = getPhysicalPeek(this.peek() * 0.01F); - double x1 = -this.scale * 0.5; - double y1 = 0.0; - double z1 = -this.scale * 0.5; - double x2 = this.scale * 0.5; - double y2 = this.scale; - double z2 = this.scale * 0.5; - - double dx = (double) direction.stepX() * peek * (double) this.scale; - if (dx > 0) { - x2 += dx; - } else if (dx < 0) { - x1 += dx; - } - double dy = (double) direction.stepY() * peek * (double) this.scale; - if (dy > 0) { - y2 += dy; - } else if (dy < 0) { - y1 += dy; - } - double dz = (double) direction.stepZ() * peek * (double) this.scale; - if (dz > 0) { - z2 += dz; - } else if (dz < 0) { - z1 += dz; - } - double minX = x + x1 + offset.x(); - double maxX = x + x2 + offset.x(); - double minY = y + y1 + offset.y(); - double maxY = y + y2 + offset.y(); - double minZ = z + z1 - offset.z(); - double maxZ = z + z2 - offset.z(); - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - private static float getPhysicalPeek(float peek) { - return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; - } - - public boolean interactionEntity() { - return interactionEntity; - } - - public boolean interactive() { - return interactive; - } - - public Direction direction() { - return direction; - } - - public byte peek() { - return peek; - } - - public float scale() { - return scale; - } - - @Override - public Key type() { - return HitBoxTypes.SHULKER; - } - - @Override - public void initPacketsAndColliders(int[] entityIds, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - double originalY = y + offset.y; - double integerPart = Math.floor(originalY); - double fractionalPart = originalY - integerPart; - double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY; - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw, - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 - ), false); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, - MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 - ), false); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false); - // add passengers - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]), false); - // fix some special occasions - if (originalY != processedY) { - double deltaY = originalY - processedY; - short ya = (short) (deltaY * 8192); - packets.accept(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance( - entityIds[1], (short) 0, ya, (short) 0, true - ), false); - } - // set shulker scale - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false); - } - this.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets, collider, aabb); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - aabbs.accept(this.aabbCreator.create(x, y, z, yaw, offset)); - } - - @FunctionalInterface - interface DirectionalShulkerSpawner { - - void accept(int[] entityIds, - World world, - double x, - double y, - double z, - float yaw, - Vector3f offset, - BiConsumer packets, - Consumer collider, - Consumer aabb); - } - - @FunctionalInterface - interface AABBCreator { - - AABB create(double x, double y, double z, float yaw, Vector3f offset); - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - if (this.interactionEntity) { - if (this.direction.stepY() != 0) { - // 展示实体 // 潜影贝 // 交互实体 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; - } else { - // 展示实体 // 潜影贝 // 交互实体1 // 交互实体2 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; - } - } else { - // 展示实体 // 潜影贝 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; - } - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); - byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); - Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP); - boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); - boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity"); - boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new ShulkerHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - position, directionEnum, - scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile - ); - } - } - - public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) { - switch (newDirection) { - case NORTH -> { - return switch (oldDirection) { - case NORTH -> Direction.NORTH; - case SOUTH -> Direction.SOUTH; - case WEST -> Direction.EAST; - case EAST -> Direction.WEST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case SOUTH -> { - return switch (oldDirection) { - case SOUTH -> Direction.NORTH; - case WEST -> Direction.WEST; - case EAST -> Direction.EAST; - case NORTH -> Direction.SOUTH; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case WEST -> { - return switch (oldDirection) { - case SOUTH -> Direction.EAST; - case WEST -> Direction.NORTH; - case EAST -> Direction.SOUTH; - case NORTH -> Direction.WEST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case EAST -> { - return switch (oldDirection) { - case SOUTH -> Direction.WEST; - case WEST -> Direction.SOUTH; - case EAST -> Direction.NORTH; - case NORTH -> Direction.EAST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - default -> throw new IllegalStateException("Unexpected value: " + newDirection); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index d312a801d..e141aa000 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.element.BukkitFurnitureElementConfigs; -import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; +import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitFurnitureHitboxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; import net.momirealms.craftengine.bukkit.font.BukkitFontManager; @@ -161,7 +161,7 @@ public class BukkitCraftEngine extends CraftEngine { super.onPluginLoad(); BukkitBlockBehaviors.init(); BukkitItemBehaviors.init(); - BukkitHitBoxTypes.init(); + BukkitFurnitureHitboxTypes.init(); BukkitBlockEntityElementConfigs.init(); BukkitFurnitureElementConfigs.init(); // 初始化 onload 阶段的兼容性 @@ -371,6 +371,11 @@ public class BukkitCraftEngine extends CraftEngine { return (BukkitFontManager) fontManager; } + @Override + public BukkitWorldManager worldManager() { + return (BukkitWorldManager) worldManager; + } + @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void saveResource(String resourcePath) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index e1d29f243..c07060832 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -1,17 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; @@ -23,7 +16,6 @@ import org.incendo.cloud.parser.standard.EnumParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; -import java.util.Optional; import java.util.concurrent.CompletableFuture; public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { @@ -45,18 +37,19 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { - NamespacedKey namespacedKey = context.get("id"); - Key id = KeyUtils.namespacedKey2Key(namespacedKey); - BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); - Optional optionalCustomFurniture = furnitureManager.furnitureById(id); - if (optionalCustomFurniture.isEmpty()) { - return; - } - Location location = context.get("location"); - FurnitureConfig customFurniture = optionalCustomFurniture.get(); - AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); - boolean playSound = context.flags().hasFlag("silent"); - CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); + // fixme 指令 +// NamespacedKey namespacedKey = context.get("id"); +// Key id = KeyUtils.namespacedKey2Key(namespacedKey); +// BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); +// Optional optionalCustomFurniture = furnitureManager.furnitureById(id); +// if (optionalCustomFurniture.isEmpty()) { +// return; +// } +// Location location = context.get("location"); +// FurnitureConfig customFurniture = optionalCustomFurniture.get(); +// AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); +// boolean playSound = context.flags().hasFlag("silent"); +// CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 92686686d..9ab146c37 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -59,8 +59,7 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxPart; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -1274,7 +1273,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Throwable { Key itemId = furniture.config().settings().itemId(); if (itemId == null) return; - pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity())); + pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.getBukkitEntity())); } } @@ -3705,7 +3704,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; if (serverPlayer.isSpectatorMode()) return; Player platformPlayer = serverPlayer.platformPlayer(); - Location location = furniture.baseEntity().getLocation(); + Location location = furniture.location(); Runnable mainThreadTask; if (actionType == 1) { @@ -3722,11 +3721,15 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes mainThreadTask = () -> { // todo 冒险模式破坏工具白名单 - if (serverPlayer.isAdventureMode() || - !furniture.isValid()) return; + if (serverPlayer.isAdventureMode() || !furniture.isValid()) return; - // todo 重构家具时候注意,需要准备加载好的hitbox类,以获取hitbox坐标 - if (!serverPlayer.canInteractPoint(new Vec3d(location.getX(), location.getY(), location.getZ()), 16d)) { + // 先检查碰撞箱部分是否存在 + FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); + if (hitBox == null) return; + Vec3d pos = hitBox.position(); + + // 检查玩家是否能破坏此点 + if (!serverPlayer.canInteractPoint(pos, 16d)) { return; } @@ -3782,34 +3785,31 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } // 先检查碰撞箱部分是否存在 - HitBoxPart hitBoxPart = furniture.hitBoxPartByEntityId(entityId); - if (hitBoxPart == null) return; - Vec3d pos = hitBoxPart.pos(); + FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); + if (hitBox == null) return; + Vec3d pos = hitBox.position(); + // 检测距离 if (!serverPlayer.canInteractPoint(pos, 16d)) { return; } - // 检测 + + // 检测能否交互碰撞箱 Location eyeLocation = platformPlayer.getEyeLocation(); Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBoxPart.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = hitBox.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } EntityHitResult hitResult = result.get(); Vec3d hitLocation = hitResult.hitLocation(); + // 获取正确的交互点 Location interactionPoint = new Location(platformPlayer.getWorld(), hitLocation.x, hitLocation.y, hitLocation.z); - - HitBox hitbox = furniture.hitBoxByEntityId(entityId); - if (hitbox == null) { - return; - } - // 触发事件 - FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint, hitbox); + FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint, hitBox); if (EventUtils.fireAndCheckCancel(interactEvent)) { return; } @@ -3831,7 +3831,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } // 必须从网络包层面处理,否则无法获取交互的具体实体 - if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitbox.config().canUseItemOn()) { + if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitBox.config().canUseItemOn()) { Optional> optionalCustomItem = itemInHand.getCustomItem(); if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) { for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) { @@ -3851,7 +3851,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes ); } else { if (!serverPlayer.isSecondaryUseActive()) { - for (Seat seat : hitbox.seats()) { + for (Seat seat : hitBox.seats()) { if (!seat.isOccupied()) { if (seat.spawnSeat(serverPlayer, furniture.position())) { break; @@ -3978,11 +3978,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes int id = buf.readVarInt(); BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); if (furniture != null) { - user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); - user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); - if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { - event.setCancelled(true); - } + furniture.show((BukkitServerPlayer) user); + event.setCancelled(true); + // fixme 外部模型 +// user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); +// user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); +// if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { +// event.setCancelled(true); +// } } else { user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index c54cb0d55..98ef141a8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -101,6 +101,10 @@ public class BukkitWorldManager implements WorldManager, Listener { injectChunkGenerator(ceWorld); for (Chunk chunk : world.getLoadedChunks()) { handleChunkLoad(ceWorld, chunk, false); + CEChunk loadedChunk = ceWorld.getChunkAtIfLoaded(chunk.getChunkKey()); + if (loadedChunk != null) { + loadedChunk.setEntitiesLoaded(true); + } } ceWorld.setTicking(true); } catch (Exception e) { @@ -154,6 +158,10 @@ public class BukkitWorldManager implements WorldManager, Listener { CEWorld ceWorld = this.worlds.get(uuid); for (Chunk chunk : world.getLoadedChunks()) { handleChunkLoad(ceWorld, chunk, true); + CEChunk loadedChunk = ceWorld.getChunkAtIfLoaded(chunk.getChunkKey()); + if (loadedChunk != null) { + loadedChunk.setEntitiesLoaded(true); + } } ceWorld.setTicking(true); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java index 2aba16476..c4959f0aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.block.entity.render.element; import java.util.Map; @FunctionalInterface -public interface BlockEntityElementConfigFactory { +public interface BlockEntityElementConfigFactory { - BlockEntityElementConfig create(Map args); + BlockEntityElementConfig create(Map args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java deleted file mode 100644 index 424174dd5..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java +++ /dev/null @@ -1,96 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.Cullable; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; - -import java.util.UUID; - -public abstract class CustomEntity implements Cullable { - protected final CustomEntityType type; - protected final UUID uuid; - protected WorldPosition position; - protected boolean valid = true; - - protected CustomEntity(CustomEntityType type, UUID uuid, WorldPosition position) { - this.position = position; - this.type = type; - this.uuid = uuid; - } - - public CompoundTag saveAsTag() { - CompoundTag tag = new CompoundTag(); - this.saveId(tag); - this.savePos(tag); - this.saveCustomData(tag); - return tag; - } - - private void savePos(CompoundTag tag) { - tag.putDouble("x", this.position.x()); - tag.putDouble("y", this.position.y()); - tag.putDouble("z", this.position.z()); - tag.putFloat("x_rot", this.position.xRot()); - tag.putFloat("y_rot", this.position.yRot()); - } - - public boolean isValid() { - return this.valid; - } - - public UUID uuid() { - return uuid; - } - - public double x() { - return this.position.x(); - } - - public double y() { - return this.position.y(); - } - - public double z() { - return this.position.z(); - } - - public float yRot() { - return this.position.yRot(); - } - - public float xRot() { - return this.position.xRot(); - } - - public CustomEntityType entityType() { - return this.type; - } - - protected void saveCustomData(CompoundTag tag) { - } - - public void loadCustomData(CompoundTag tag) { - } - - private void saveId(CompoundTag tag) { - tag.putString("id", this.type.id().asString()); - } - - public void destroy() { - this.valid = false; - } - - public static UUID readUUID(CompoundTag tag) { - return tag.getUUID("uuid"); - } - - public static WorldPosition readPos(CEWorld world, CompoundTag tag) { - double x = tag.getDouble("x"); - double y = tag.getDouble("y"); - double z = tag.getDouble("z"); - float xRot = tag.getFloat("x_rot"); - float yRot = tag.getFloat("y_rot"); - return new WorldPosition(world.world, x, y, z, xRot, yRot); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java deleted file mode 100644 index a7cd7cb33..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; - -import java.util.UUID; - -public record CustomEntityType(Key id, Factory factory) { - - public interface Factory { - - T create(UUID uuid, WorldPosition position); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java deleted file mode 100644 index 57e36fadc..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.util.Key; - -public final class CustomEntityTypeKeys { - private CustomEntityTypeKeys() {} - - public static final Key FURNITURE = Key.of("craftengine:furniture"); - public static final Key INACTIVE = Key.of("craftengine:inactive"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java deleted file mode 100644 index c5697d9c2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.registry.Registries; -import net.momirealms.craftengine.core.registry.WritableRegistry; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceKey; - -public class CustomEntityTypes { - public static final CustomEntityType INACTIVE = register(CustomEntityTypeKeys.INACTIVE, InactiveCustomEntity::new); - - public static CustomEntityType register(Key id, CustomEntityType.Factory factory) { - CustomEntityType type = new CustomEntityType<>(id, factory); - ((WritableRegistry>) BuiltInRegistries.ENTITY_TYPE) - .register(ResourceKey.create(Registries.ENTITY_TYPE.location(), id), type); - return type; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java index 5c52db4e2..81ab02cf7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java @@ -11,6 +11,8 @@ import java.util.UUID; public interface Entity { Key type(); + boolean isValid(); + double x(); double y(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java deleted file mode 100644 index 9a4633d37..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.NBT; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.UUID; - -public class InactiveCustomEntity extends CustomEntity { - public static final CompoundTag INVALID_TAG = new CompoundTag(Map.of("type", NBT.createString(CustomEntityTypes.INACTIVE.id().asMinimalString()))); - private final CompoundTag data; - - public InactiveCustomEntity(UUID uuid, WorldPosition position) { - super(CustomEntityTypes.INACTIVE, uuid, position); - this.data = INVALID_TAG; - } - - public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { - super(CustomEntityTypes.INACTIVE, uuid, position); - this.data = data; - } - - @Override - public CompoundTag saveAsTag() { - return this.data; - } - - @Override - public boolean isValid() { - // 不正常的数据不要存储 - return this.data != INVALID_TAG; - } - - @Override - public void destroy() { - } - - @Override - public void show(Player player) { - } - - @Override - public void hide(Player player) { - } - - @Override - public @Nullable CullingData cullingData() { - return null; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java index 1351831e6..896473f0f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java @@ -12,14 +12,14 @@ public interface EntityData { Object entityDataAccessor(); - Object create(Object entityDataAccessor, Object value); + Object create(Object entityDataAccessor, T value); default Object createEntityDataIfNotDefaultValue(T value) { if (defaultValue().equals(value)) return null; return create(entityDataAccessor(), value); } - default Object createEntityData(Object value) { + default Object createEntityData(T value) { return create(entityDataAccessor(), value); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index dd67036f8..092e70ea7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; @@ -75,7 +75,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { this.byId.clear(); } - protected abstract HitBoxConfig defaultHitBox(); + protected abstract FurnitureHitBoxConfig defaultHitBox(); public class FurnitureParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; @@ -124,7 +124,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elementConfigs"), FurnitureElementConfigs::fromMap); // fixme 外部模型不应该在这 Optional externalModel; @@ -136,14 +136,14 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { externalModel = Optional.empty(); } - List hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), HitBoxTypes::fromMap); + List> hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), FurnitureHitBoxTypes::fromMap); if (hitboxes.isEmpty() && externalModel.isEmpty()) { hitboxes = List.of(defaultHitBox()); } variants.put(variantName, new FurnitureVariant( elements.toArray(new FurnitureElementConfig[0]), - hitboxes.toArray(new HitBoxConfig[0]), + hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, optionalLootSpawnOffset )); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index cefebe26b..b389f52f7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; -@Deprecated(since = "0.0.66", forRemoval = true) +@Deprecated(since = "0.0.66") public enum AnchorType { GROUND(0), WALL(1), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 236a014b8..d50a23ea0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,65 +1,187 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.CustomEntity; -import net.momirealms.craftengine.core.entity.CustomEntityType; -import net.momirealms.craftengine.core.plugin.CraftEngine; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.world.Cullable; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; -import java.util.Optional; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; -public abstract class Furniture extends CustomEntity { - protected FurnitureConfig config; - protected FurnitureDataAccessor dataAccessor; +public abstract class Furniture implements Cullable { + public final FurnitureConfig config; + public final FurnitureDataAccessor dataAccessor; + public final WeakReference metaDataEntity; - protected Key furnitureId; - protected CompoundTag inactiveFurnitureData; + protected FurnitureVariant currentVariant; + protected FurnitureElement[] elements; + protected Collider[] colliders; + protected FurnitureHitBox[] hitboxes; - protected Furniture(CustomEntityType type, UUID uuid, WorldPosition position) { - super(type, uuid, position); + protected Int2ObjectMap hitboxMap; + protected int[] entityIds; + + protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { + this.config = config; + this.dataAccessor = data; + this.metaDataEntity = new WeakReference<>(metaDataEntity); + } + + public WeakReference metaDataEntity() { + return this.metaDataEntity; + } + + public FurnitureVariant getCurrentVariant() { + return currentVariant; + } + + public String getCurrentVariantName() { + return null; + } + + public void setVariant(FurnitureVariant variant) { + this.currentVariant = variant; + WorldPosition position = this.position(); + // 初始化家具元素 + FurnitureElementConfig[] elementConfigs = variant.elementConfigs(); + this.elements = new FurnitureElement[elementConfigs.length]; + for (int i = 0; i < elementConfigs.length; i++) { + this.elements[i] = elementConfigs[i].create(this); + } + // 初始化碰撞箱 + FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.furnitureHitBoxConfigs(); + List colliders = new ArrayList<>(furnitureHitBoxConfigs.length); + this.hitboxes = new FurnitureHitBox[furnitureHitBoxConfigs.length]; + for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { + FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); + this.hitboxes[i] = hitbox; + for (int hitboxEntityId : hitbox.virtualEntityIds()) { + this.hitboxMap.put(hitboxEntityId, hitbox); + } + Collider collider = hitbox.collider(); + if (collider != null) { + colliders.add(collider); + } + } + this.colliders = colliders.toArray(new Collider[0]); + } + + @Nullable + public FurnitureHitBox hitboxByEntityId(int entityId) { + return this.hitboxMap.get(entityId); + } + + @Nullable + @Override + public CullingData cullingData() { + return this.config.cullingData(); + } + + public Key id() { + return this.config.id(); + } + + public int[] entityIds() { + return this.entityIds; + } + + public UUID uuid() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.uuid(); } @Override - protected void saveCustomData(CompoundTag tag) { - tag.putString("id", this.config.id().asMinimalString()); - - } - - @Override - public void loadCustomData(CompoundTag tag) { - this.furnitureId = readFurnitureId(tag); - this.dataAccessor = new FurnitureDataAccessor(tag.getCompound("data")); - Optional furnitureConfig = CraftEngine.instance().furnitureManager().furnitureById(this.furnitureId); - if (furnitureConfig.isPresent()) { - this.config = furnitureConfig.get(); - } else { - this.inactiveFurnitureData = tag; + public void show(Player player) { + for (FurnitureElement element : this.elements) { + element.show(player); + } + for (FurnitureHitBox hitbox : this.hitboxes) { + hitbox.show(player); } } @Override - public CompoundTag saveAsTag() { - // 如果家具数据只是不活跃,则返回不活跃家具数据 - if (this.inactiveFurnitureData != null) { - return this.inactiveFurnitureData; + public void hide(Player player) { + for (FurnitureElement element : this.elements) { + element.hide(player); + } + for (FurnitureHitBox hitbox : this.hitboxes) { + hitbox.hide(player); } - return super.saveAsTag(); } - @NotNull + public abstract void addCollidersToWorld(); + + public void destroySeats() { + for (FurnitureHitBox hitbox : this.hitboxes) { + for (Seat seat : hitbox.seats()) { + seat.destroy(); + } + } + } + + public boolean isValid() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return false; + return entity.isValid(); + } + + public abstract void destroy(); + public FurnitureConfig config() { - return this.config; + return config; } - @NotNull public FurnitureDataAccessor dataAccessor() { - return this.dataAccessor; + return dataAccessor; } - protected final Key readFurnitureId(CompoundTag tag) { - return Key.of(tag.getString("id")); + public Collider[] colliders() { + return this.colliders; + } + + public WorldPosition position() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.position(); + } + + public int entityId() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return -1; + return entity.entityID(); + } + + public Vec3d getRelativePosition(Vector3f position) { + return getRelativePosition(this.position(), position); + } + + public static Vec3d getRelativePosition(WorldPosition location, Vector3f position) { + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - location.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(position)); + return new Vec3d(location.x + offset.x, location.y + offset.y, location.z - offset.z); + } + + public World world() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.world(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index c6f075e60..6e70376e6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -11,7 +11,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; public interface FurnitureConfig { @@ -40,6 +42,31 @@ public interface FurnitureConfig { @NotNull FurnitureBehavior behavior(); + @NotNull + default FurnitureVariant getVariant(FurnitureDataAccessor accessor) { + Optional optionalVariant = accessor.variant(); + String variantName = null; + if (optionalVariant.isPresent()) { + variantName = optionalVariant.get(); + } else { + Optional optionalAnchorType = accessor.anchorType(); + if (optionalAnchorType.isPresent()) { + variantName = optionalAnchorType.get().name().toLowerCase(Locale.ROOT); + accessor.setVariant(variantName); + accessor.removeCustomData(FurnitureDataAccessor.ANCHOR_TYPE); + } + } + if (variantName == null) { + return anyVariant(); + } + FurnitureVariant variant = getVariant(variantName); + if (variant == null) { + return anyVariant(); + } + return variant; + + } + CullingData cullingData(); static Builder builder() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java index 946f201cd..056a8f47f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -1,13 +1,13 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import org.joml.Vector3f; import java.util.Optional; -public record FurnitureVariant(FurnitureElementConfig[] elements, - HitBoxConfig[] hitBoxConfigs, +public record FurnitureVariant(FurnitureElementConfig[] elementConfigs, + FurnitureHitBoxConfig[] furnitureHitBoxConfigs, Optional externalModel, Optional dropOffset) { } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java index c291a43be..31ab640f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java @@ -1,9 +1,9 @@ package net.momirealms.craftengine.core.entity.furniture.element; -import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import org.jetbrains.annotations.NotNull; public interface FurnitureElementConfig { - E create(@NotNull WorldPosition position); + E create(@NotNull Furniture furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java index 438124560..5172948f3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.entity.furniture.element; import java.util.Map; -public interface FurnitureElementConfigFactory { +public interface FurnitureElementConfigFactory { - FurnitureElementConfig create(Map args); + FurnitureElementConfig create(Map args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java similarity index 79% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java index d9a5755f0..f977145e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java @@ -3,14 +3,14 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import org.joml.Vector3f; -public abstract class AbstractHitBoxConfig implements HitBoxConfig { +public abstract class AbstractFurnitureHitBoxConfig implements FurnitureHitBoxConfig { protected final SeatConfig[] seats; protected final Vector3f position; protected final boolean canUseItemOn; protected final boolean blocksBuilding; protected final boolean canBeHitByProjectile; - public AbstractHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { + public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { this.seats = seats; this.position = position; this.canUseItemOn = canUseItemOn; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java deleted file mode 100644 index 185fec9de..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.sparrow.nbt.CompoundTag; - -import java.util.Optional; - -public abstract class AbstractHitBox implements HitBox { - private final Furniture furniture; - private final HitBoxConfig config; - private final HitBoxPart[] parts; - private Seat[] seats; - - public AbstractHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { - this.parts = parts; - this.config = config; - this.furniture = furniture; - } - - protected abstract void createSeats(HitBoxConfig config); - - @Override - public HitBoxPart[] parts() { - return this.parts; - } - - @Override - public HitBoxConfig config() { - return this.config; - } - - @Override - public Seat[] seats() { - return this.seats; - } - - @Override - public Optional clip(Vec3d min, Vec3d max) { - for (HitBoxPart hbe : this.parts) { - Optional result = hbe.aabb().clip(min, max); - if (result.isPresent()) { - return result; - } - } - return Optional.empty(); - } - - @Override - public void saveCustomData(CompoundTag data) { - data.putString("type", "furniture"); - data.putInt("entity_id", this.furniture.entityId()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java new file mode 100644 index 000000000..7fbb4ca70 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -0,0 +1,29 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.entity.seat.SeatOwner; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.jetbrains.annotations.Nullable; + +public interface FurnitureHitBox extends SeatOwner { + + Seat[] seats(); + + AABB aabb(); + + Vec3d position(); + + @Nullable + Collider collider(); + + int[] virtualEntityIds(); + + void show(Player player); + + void hide(Player player); + + FurnitureHitBoxConfig config(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java similarity index 64% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index f14f7a064..e1730c7f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -1,12 +1,12 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; import org.joml.Vector3f; -public interface HitBoxConfig { +public interface FurnitureHitBoxConfig { - Key type(); + H create(Furniture furniture); SeatConfig[] seats(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java new file mode 100644 index 000000000..4d867d1ec --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import java.util.Map; + +public interface FurnitureHitBoxConfigFactory { + + FurnitureHitBoxConfig create(Map arguments); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java index 853fa8318..d4f002a9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java @@ -10,21 +10,21 @@ import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; import java.util.Optional; -public class HitBoxTypes { +public class FurnitureHitBoxTypes { public static final Key INTERACTION = Key.of("minecraft:interaction"); public static final Key SHULKER = Key.of("minecraft:shulker"); public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast"); public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); - public static void register(Key key, HitBoxConfigFactory factory) { - ((WritableRegistry) BuiltInRegistries.HITBOX_FACTORY) - .register(ResourceKey.create(Registries.HITBOX_FACTORY.location(), key), factory); + public static void register(Key key, FurnitureHitBoxConfigFactory factory) { + ((WritableRegistry) BuiltInRegistries.FURNITURE_HITBOX_TYPE) + .register(ResourceKey.create(Registries.FURNITURE_HITBOX_TYPE.location(), key), factory); } - public static HitBoxConfig fromMap(Map arguments) { - Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(HitBoxTypes.INTERACTION); - HitBoxConfigFactory factory = BuiltInRegistries.HITBOX_FACTORY.getValue(type); + public static FurnitureHitBoxConfig fromMap(Map arguments) { + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(FurnitureHitBoxTypes.INTERACTION); + FurnitureHitBoxConfigFactory factory = BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java deleted file mode 100644 index 32d31c3e1..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.entity.seat.SeatOwner; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.Vec3d; - -import java.util.Optional; - -public interface HitBox extends SeatOwner { - - Seat[] seats(); - - Optional clip(Vec3d min, Vec3d max); - - HitBoxPart[] parts(); - - HitBoxConfig config(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java deleted file mode 100644 index df9a2b101..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import java.util.Map; - -public interface HitBoxConfigFactory { - - HitBoxConfig create(Map arguments); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java deleted file mode 100644 index fe5619281..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; - -public record HitBoxPart(int entityId, AABB aabb, Vec3d pos) { -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 689374c51..ae13804e6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -224,4 +224,9 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { } public abstract WorldPosition eyePosition(); + + @Override + public boolean isValid() { + return this.isOnline(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 1aa740527..1c9f0bd85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -90,7 +90,7 @@ public abstract class AbstractPackManager implements PackManager { public static final String NEW_TRIM_MATERIAL = "custom"; public static final Set ALLOWED_VANILLA_EQUIPMENT = Set.of("chainmail", "diamond", "gold", "iron", "netherite"); - public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elements", "gui_light", "overrides"); + public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elementConfigs", "gui_light", "overrides"); private static final byte[] EMPTY_1X1_IMAGE; private static final byte[] EMPTY_EQUIPMENT_IMAGE; @@ -2136,7 +2136,7 @@ public abstract class AbstractPackManager implements PackManager { .resolve("empty.png"); try { Files.createDirectories(modelPath.getParent()); - Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elements\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); + Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elementConfigs\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); } catch (IOException e) { this.plugin.logger().severe("Error writing empty block model", e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java index 8c4b0e176..942a704a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java @@ -11,7 +11,7 @@ public class BBModel { private String model_identifier; @SerializedName("visible_box") private int[] visible_box; - @SerializedName("elements") + @SerializedName("elementConfigs") private Element[] elements; @SerializedName("outliner") private OutLiner[] outliner; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java index 796358ef3..518b061cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java @@ -41,12 +41,12 @@ public class RemoveFurnitureFunction extends AbstractCondit WorldPosition position = furniture.position(); World world = position.world(); furniture.destroy(); - LootTable lootTable = furniture.config().lootTable(); + LootTable lootTable = furniture.config.lootTable(); if (dropLoot && lootTable != null) { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor.item().orElse(null)); Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); Player player = optionalPlayer.orElse(null); if (player != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 154c66687..20dee5084 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -71,7 +71,8 @@ public class ReplaceFurnitureFunction extends AbstractCondi RemoveFurnitureFunction.removeFurniture(ctx, oldFurniture, dropLoot, playSound); // Place the new furniture - SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); + // fixme function +// SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java index 2191c4da9..376550f22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.Entity; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -55,7 +54,7 @@ public final class DirectContextParameters { public static final ContextKey ID = ContextKey.direct("id"); public static final ContextKey CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data"); public static final ContextKey FURNITURE = ContextKey.direct("furniture"); - public static final ContextKey ANCHOR_TYPE = ContextKey.direct("anchor_type"); + public static final ContextKey VARIANT = ContextKey.direct("variant"); public static final ContextKey HAND = ContextKey.direct("hand"); public static final ContextKey EVENT = ContextKey.direct("event"); public static final ContextKey IS_SNEAKING = ContextKey.direct("is_sneaking"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java index dc4774853..dcf69a5e4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -12,9 +12,9 @@ import java.util.function.Function; public class FurnitureParameterProvider implements ChainParameterProvider { private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, Furniture::id); + CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, f -> f.config().id()); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid); - CONTEXT_FUNCTIONS.put(DirectContextParameters.ANCHOR_TYPE, Furniture::anchorType); + CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, Furniture::getCurrentVariantName); CONTEXT_FUNCTIONS.put(DirectContextParameters.X, furniture -> furniture.position().x()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, furniture -> furniture.position().y()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, furniture -> furniture.position().z()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index 8cab5409b..62bf71645 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -5,9 +5,8 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; -import net.momirealms.craftengine.core.entity.CustomEntityType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -78,7 +77,6 @@ public class BuiltInRegistries { public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16); public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16); public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16); - public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16); public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16); public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128); public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128); @@ -93,8 +91,8 @@ public class BuiltInRegistries { public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); - public static final Registry> ENTITY_TYPE = createConstantBoundRegistry(Registries.ENTITY_TYPE, 32); public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); + public static final Registry FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 5258d2e4b..9b728d316 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -5,9 +5,8 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; -import net.momirealms.craftengine.core.entity.CustomEntityType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -80,7 +79,6 @@ public class Registries { public static final ResourceKey>> PATH_MATCHER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory")); public static final ResourceKey> RESOLUTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory")); public static final ResourceKey> SMITHING_RESULT_PROCESSOR_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory")); - public static final ResourceKey> HITBOX_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory")); public static final ResourceKey> RESOURCE_PACK_HOST_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory")); public static final ResourceKey>> EVENT_FUNCTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("event_function_factory")); public static final ResourceKey>> EVENT_CONDITION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("event_condition_factory")); @@ -95,6 +93,6 @@ public class Registries { public static final ResourceKey>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type")); public static final ResourceKey> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); - public static final ResourceKey>> ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("entity_type")); public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); + public static final ResourceKey> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java index 4ff57411a..639bd31b2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java @@ -54,7 +54,7 @@ public final class BlockEntityTickersList extends ObjectArrayList because we want to avoid autoboxing when using contains final int requiredMatches = c.size(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java index 868538ea6..fb0b9dd02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java @@ -1,12 +1,14 @@ package net.momirealms.craftengine.core.world; +import java.util.Objects; + public class WorldPosition implements Position { - private final World world; - private final double x; - private final double y; - private final double z; - private final float xRot; - private final float yRot; + public final World world; + public final double x; + public final double y; + public final double z; + public final float xRot; + public final float yRot; public WorldPosition(World world, Position position) { this.x = position.x(); @@ -70,4 +72,28 @@ public class WorldPosition implements Position { public float yRot() { return yRot; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WorldPosition that = (WorldPosition) o; + return Double.compare(that.x, this.x) == 0 && + Double.compare(that.y, this.y) == 0 && + Double.compare(that.z, this.z) == 0 && + Float.compare(that.xRot, this.xRot) == 0 && + Float.compare(that.yRot, this.yRot) == 0 && + Objects.equals(this.world, that.world); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(this.world); + result = 31 * result + Double.hashCode(this.x); + result = 31 * result + Double.hashCode(this.y); + result = 31 * result + Double.hashCode(this.z); + result = 31 * result + Float.floatToIntBits(this.xRot); + result = 31 * result + Float.floatToIntBits(this.yRot); + return result; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index 0aee4a4a1..d278ed0cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -10,7 +10,6 @@ import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRen import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.tick.*; -import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -19,7 +18,6 @@ import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; -import net.momirealms.craftengine.core.world.chunk.serialization.DefaultEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,11 +26,11 @@ import java.util.*; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CEChunk { + private static final int DEFAULT_MAP_SIZE = 8; public final CEWorld world; public final ChunkPos chunkPos; public final CESection[] sections; public final WorldHeight worldHeightAccessor; - private final Map entities; // 从区域线程上访问,安全 private final Map blockEntities; // 从区域线程上访问,安全 private final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 private final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 @@ -42,18 +40,18 @@ public class CEChunk { private volatile boolean dirty; private volatile boolean loaded; private volatile boolean activated; + private boolean isEntitiesLoaded; public CEChunk(CEWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); this.fillEmptySection(); } @@ -61,9 +59,9 @@ public class CEChunk { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); - this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; if (sections != null) { @@ -76,35 +74,23 @@ public class CEChunk { } this.fillEmptySection(); if (blockEntitiesTag != null) { - this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 10), 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), DEFAULT_MAP_SIZE), 0.5f); List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); for (BlockEntity blockEntity : blockEntities) { this.setBlockEntity(blockEntity); } } else { - this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); } if (blockEntityRenders != null) { - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), 10), 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), DEFAULT_MAP_SIZE), 0.5f); List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, blockEntityRenders); for (BlockPos pos : blockEntityRendererPoses) { this.addConstantBlockEntityRenderer(pos); } } else { - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); } - if (entities != null) { - this.entities = new Object2ObjectOpenHashMap<>(Math.max(entities.size(), 10), 0.5f); - for (CustomEntity entity : DefaultEntitySerializer.deserialize(world, entities)) { - this.entities.put(entity.uuid(), entity); - } - } else { - this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); - } - } - - public Map entities() { - return Collections.unmodifiableMap(this.entities); } public void spawnBlockEntities(Player player) { @@ -680,6 +666,14 @@ public class CEChunk { return this.sections; } + public boolean isEntitiesLoaded() { + return this.isEntitiesLoaded; + } + + public void setEntitiesLoaded(boolean entitiesLoaded) { + this.isEntitiesLoaded = entitiesLoaded; + } + public boolean isLoaded() { return this.loaded; } @@ -694,5 +688,6 @@ public class CEChunk { if (!this.loaded) return; this.world.removeLoadedChunk(this); this.loaded = false; + this.isEntitiesLoaded = false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index e98376e7e..030d8fc3e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.world.chunk.serialization; -import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -10,9 +9,6 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; -import java.util.UUID; - public final class DefaultChunkSerializer { private DefaultChunkSerializer() {} @@ -40,10 +36,6 @@ public final class DefaultChunkSerializer { if (!blockEntityRenders.isEmpty()) { chunkNbt.put("block_entity_renderers", blockEntityRenders); } - ListTag listTag = new ListTag(); - Map entities = chunk.entities(); - - return chunkNbt; } @@ -63,7 +55,6 @@ public final class DefaultChunkSerializer { } ListTag blockEntities = chunkNbt.getList("block_entities"); ListTag blockEntityRenders = chunkNbt.getList("block_entity_renderers"); - ListTag entities = chunkNbt.getList("entities"); - return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, entities); + return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, null); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java deleted file mode 100644 index df24bb530..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java +++ /dev/null @@ -1,55 +0,0 @@ -package net.momirealms.craftengine.core.world.chunk.serialization; - -import net.momirealms.craftengine.core.entity.CustomEntity; -import net.momirealms.craftengine.core.entity.CustomEntityType; -import net.momirealms.craftengine.core.entity.InactiveCustomEntity; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.ListTag; -import net.momirealms.sparrow.nbt.Tag; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; - -public class DefaultEntitySerializer { - - public static ListTag serialize(@NotNull Collection entity) { - ListTag entities = new ListTag(); - for (CustomEntity customEntity : entity) { - if (customEntity.isValid()) { - entities.add(customEntity.saveAsTag()); - } - } - return entities; - } - - public static List deserialize(CEWorld world, ListTag entitiesTag) { - List entities = new ArrayList<>(entitiesTag.size()); - for (Tag tag : entitiesTag) { - if (tag instanceof CompoundTag entityTag) { - WorldPosition worldPosition = CustomEntity.readPos(world, entityTag); - UUID uuid = CustomEntity.readUUID(entityTag); - Key type = Key.of(entityTag.getString("type")); - CustomEntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(type); - if (entityType == null) { - InactiveCustomEntity entity = new InactiveCustomEntity(uuid, worldPosition, entityTag); - entities.add(entity); - } else { - CustomEntity entity = entityType.factory().create(uuid, worldPosition); - entity.loadCustomData(entityTag); - // 加载时无效则直接放弃 - if (entity.isValid()) { - entities.add(entity); - } - } - } - } - return entities; - } -} From c5bc8d8e3b014c809dfa70d0681705e81f282094 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 21:47:47 +0800 Subject: [PATCH 21/46] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../recipe/CustomSmithingTransformRecipe.java | 45 ++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..b53305b6e 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -161,6 +161,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: "I warning.config.recipe.smithing_transform.post_processor.invalid_type: "Issue found in file - The smithing transform recipe '' is using an invalid post processor type ''." warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Issue found in file - The smithing transform recipe '' is missing the required argument 'components' for post-processors 'keep_components'." warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Issue found in file - The smithing transform recipe '' is missing the required argument 'tags' for post-processors 'keep_tags'." +warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "Issue found in file - The smithing transform recipe '' is missing the required argument 'paths' for post-processors 'keep_custom_data'." warning.config.recipe.smithing_transform.missing_base: "Issue found in file - The smithing transform recipe '' is missing the required 'base' argument." warning.config.recipe.smithing_trim.missing_base: "Issue found in file - The smithing trim recipe '' is missing the required 'base' argument." warning.config.recipe.smithing_trim.missing_template_type: "Issue found in file - The smithing trim recipe '' is missing the required 'template-type' argument." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..bbada1743 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -159,6 +159,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: " warning.config.recipe.smithing_transform.post_processor.invalid_type: "在文件 发现问题 - 锻造升级配方 '' 使用了无效的后处理器类型 ''" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_components' 后处理器缺少必需的 'components' 参数" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_tags' 后处理器缺少必需的 'tags' 参数" +warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_custom_data' 后处理器缺少必需的 'paths' 参数" warning.config.recipe.smithing_transform.missing_base: "在文件 发现问题 - 锻造升级配方 '' 缺少必需的 'base' 参数" warning.config.recipe.smithing_trim.missing_base: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'base' 参数" warning.config.recipe.smithing_trim.missing_template_type: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'template-type' 参数" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index cad232493..87730aa0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -155,7 +155,6 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip private T createSmithingResult(Item base, T result) { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; - Optional customId = finalResult.customId(); // 修复在保留custom_data组件的时候意外保留前物品的id if (this.mergeComponents) { finalResult = base.mergeCopy(wrappedResult); } @@ -164,9 +163,6 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip processor.accept(base, wrappedResult, finalResult); } } - if (customId.isPresent()) { - finalResult.customId(customId.get()); - } return finalResult.getItem(); } @@ -233,10 +229,12 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip public static final Key KEEP_COMPONENTS = Key.of("craftengine:keep_components"); public static final Key KEEP_TAGS = Key.of("craftengine:keep_tags"); public static final Key MERGE_ENCHANTMENTS = Key.of("craftengine:merge_enchantments"); + public static final Key KEEP_CUSTOM_DATA = Key.of("craftengine:keep_custom_data"); static { if (VersionHelper.isOrAbove1_20_5()) { register(KEEP_COMPONENTS, KeepComponents.FACTORY); + register(KEEP_CUSTOM_DATA, KeepCustomData.FACTORY); } else { register(KEEP_TAGS, KeepTags.FACTORY); } @@ -316,6 +314,42 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip } } + public static class KeepCustomData implements ItemDataProcessor { + public static final Factory FACTORY = new Factory(); + private final List paths; + + public KeepCustomData(List data) { + this.paths = data; + } + + @Override + public void accept(Item item1, Item item2, Item item3) { + for (String[] path : this.paths) { + Object dataObj = item1.getJavaTag((Object[]) path); + if (dataObj != null) { + item3.setTag(dataObj, (Object[]) path); + } + } + } + + @Override + public Key type() { + return ItemDataProcessors.KEEP_CUSTOM_DATA; + } + + public static class Factory implements ProcessorFactory { + + @Override + public ItemDataProcessor create(Map arguments) { + List paths = MiscUtils.getAsStringList(ResourceConfigUtils.requireNonNullOrThrow( + arguments.get("paths"), + "warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths") + ); + return new KeepCustomData(paths.stream().map(it -> it.split("\\.")).toList()); + } + } + } + public static class KeepComponents implements ItemDataProcessor { public static final Factory FACTORY = new Factory(); private final List components; @@ -340,6 +374,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip } public static class Factory implements ProcessorFactory { + private static final Key CUSTOM_DATA = Key.of("minecraft", "custom_data"); @Override public ItemDataProcessor create(Map arguments) { @@ -348,7 +383,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components"); } List components = MiscUtils.getAsStringList(componentsObj); - return new KeepComponents(components.stream().map(Key::of).toList()); + return new KeepComponents(components.stream().map(Key::of).filter(it -> !CUSTOM_DATA.equals(it)).toList()); } } } From 20e1a42f6256292fe50e09768e241cad441e64ca Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 23:19:33 +0800 Subject: [PATCH 22/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=AE=B9=E5=99=A8=E5=85=B3=E4=B8=8D=E4=B8=8A?= =?UTF-8?q?=E9=97=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/entity/SimpleStorageBlockEntity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java index 625818617..5649bccc4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java @@ -173,7 +173,9 @@ public class SimpleStorageBlockEntity extends BlockEntity { public void updateOpenBlockState(boolean open) { ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos); if (state == null || state.behavior() != this.behavior) return; - Property property = this.behavior.openProperty(); + SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null); + if (behavior == null) return; + Property property = behavior.openProperty(); if (property == null) return; super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags()); } From d2bb9103bd0583eb311641552aff8cf74550c4b6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 01:30:29 +0800 Subject: [PATCH 23/46] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84part3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 12 +- .../api/event/FurnitureAttemptPlaceEvent.java | 19 +- .../entity/furniture/BukkitFurniture.java | 1 - .../furniture/BukkitFurnitureManager.java | 109 ++++---- .../entity/furniture/ItemColorSource.java | 6 - .../element/ItemDisplayFurnitureElement.java | 43 +++- .../ItemDisplayFurnitureElementConfig.java | 114 +++++---- .../hitbox/AbstractFurnitureHitBox.java | 6 + .../hitbox/InteractionFurnitureHitbox.java | 38 +-- .../InteractionFurnitureHitboxConfig.java | 26 +- .../item/behavior/FurnitureItemBehavior.java | 239 +++++++++--------- .../plugin/network/BukkitNetworkManager.java | 41 +-- .../handler/FurniturePacketHandler.java | 23 +- .../plugin/user/BukkitServerPlayer.java | 96 ++++--- .../furniture/AbstractFurnitureManager.java | 11 +- .../core/entity/furniture/Furniture.java | 64 +++-- .../furniture/FurnitureColorSource.java | 6 + .../entity/furniture/FurnitureConfig.java | 4 - .../entity/furniture/FurnitureConfigImpl.java | 17 +- .../furniture/FurnitureDataAccessor.java | 14 +- .../entity/furniture/FurnitureManager.java | 11 +- .../entity/furniture/FurnitureVariant.java | 8 +- .../furniture/element/FurnitureElement.java | 6 + .../hitbox/AbstractFurnitureHitBoxConfig.java | 6 +- .../furniture/hitbox/FurnitureHitBox.java | 29 ++- .../hitbox/FurnitureHitBoxConfig.java | 6 + .../core/entity/player/Player.java | 7 + .../parameter/FurnitureParameterProvider.java | 2 +- .../plugin/network/EntityPacketHandler.java | 2 +- .../core/util/ResourceConfigUtils.java | 2 + 30 files changed, 580 insertions(+), 388 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 39ae861f4..5d5c4532f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -185,7 +185,7 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { - return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId()); + return BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(baseEntity.getEntityId()); } /** @@ -199,7 +199,7 @@ public final class CraftEngineFurniture { if (isSeat(seat)) { CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat); int entityId = seatExtraData.getInt("entity_id"); - BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId); + BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entityId); } return null; } @@ -212,7 +212,7 @@ public final class CraftEngineFurniture { */ public static boolean remove(@NotNull Entity entity) { if (!isFurniture(entity)) return false; - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; furniture.destroy(); return true; @@ -230,7 +230,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!isFurniture(entity)) return false; - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound); return true; @@ -250,7 +250,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!isFurniture(entity)) return false; - Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; remove(furniture, player, dropLoot, playSound); return true; @@ -320,7 +320,7 @@ public final class CraftEngineFurniture { } } if (playSound) { - world.playBlockSound(position, furniture.config().settings().sounds().breakSound()); + world.playBlockSound(position, furniture.config.settings().sounds().breakSound()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index dd1920ca0..d7d053413 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; @@ -17,23 +18,20 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can private boolean cancelled; private final FurnitureConfig furniture; private final Location location; - private final AnchorType anchorType; - private final BlockFace clickedFace; + private final FurnitureVariant variant; private final Block clickedBlock; private final InteractionHand hand; public FurnitureAttemptPlaceEvent(@NotNull Player player, @NotNull FurnitureConfig furniture, - @NotNull AnchorType anchorType, + @NotNull FurnitureVariant variant, @NotNull Location location, - @NotNull BlockFace clickedFace, @NotNull InteractionHand hand, @NotNull Block clickedBlock) { super(player); this.furniture = furniture; this.location = location; - this.anchorType = anchorType; - this.clickedFace = clickedFace; + this.variant = variant; this.clickedBlock = clickedBlock; this.hand = hand; } @@ -48,19 +46,14 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can return hand; } - @NotNull - public BlockFace clickedFace() { - return clickedFace; - } - @NotNull public Player player() { return getPlayer(); } @NotNull - public AnchorType anchorType() { - return anchorType; + public FurnitureVariant variant() { + return variant; } @NotNull diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 7236d1ba0..4455884dc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -25,7 +25,6 @@ public class BukkitFurniture extends Furniture { super(new BukkitEntity(metaEntity), data, config); this.metaEntity = new WeakReference<>(metaEntity); this.location = metaEntity.getLocation(); - this.setVariant(super.config().getVariant(data)); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index f2edfe2df..a1dee6c88 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -5,23 +5,20 @@ import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.chunk.CEChunk; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.entity.Boat; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Interaction; -import org.bukkit.entity.ItemDisplay; +import org.bukkit.*; +import org.bukkit.entity.*; import org.bukkit.event.HandlerList; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; @@ -46,7 +43,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { private final BukkitCraftEngine plugin; private final Map byMetaEntityId = new ConcurrentHashMap<>(256, 0.5f); - private final Map byEntityId = new ConcurrentHashMap<>(512, 0.5f); + private final Map byVirtualEntityId = new ConcurrentHashMap<>(512, 0.5f); + private final Map byColliderEntityId = new ConcurrentHashMap<>(512, 0.5f); // Event listeners private final FurnitureEventListener furnitureEventListener; @@ -63,31 +61,25 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { -// return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); - return null; + return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); } - public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound) { -// Optional optionalAnchorType = extraData.anchorType(); -// if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { -// extraData.anchorType(furniture.getAnyAnchorType()); -// } -// Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { -// ItemDisplay display = (ItemDisplay) entity; -// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); -// try { -// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); -// } catch (IOException e) { -// this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); -// } -// handleBaseEntityLoadEarly(display); -// }); -// if (playSound) { -// SoundData data = furniture.settings().sounds().placeSound(); -// location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); -// } -// return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); - return null; + public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor data, boolean playSound) { + Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { + ItemDisplay display = (ItemDisplay) entity; + display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); + try { + display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, data.toBytes()); + } catch (IOException e) { + this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); + } + handleMetaEntityAfterChunkLoad(display); + }); + if (playSound) { + SoundData sound = furniture.settings().sounds().placeSound(); + location.getWorld().playSound(location, sound.id().toString(), SoundCategory.BLOCKS, sound.volume().get(), sound.pitch().get()); + } + return loadedFurnitureByMetaEntityId(furnitureEntity.getEntityId()); } @Override @@ -138,43 +130,59 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public boolean isFurnitureRealEntity(int entityId) { + public boolean isFurnitureMetaEntity(int entityId) { return this.byMetaEntityId.containsKey(entityId); } @Nullable @Override - public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) { + public BukkitFurniture loadedFurnitureByMetaEntityId(int entityId) { return this.byMetaEntityId.get(entityId); } - @Override @Nullable - public BukkitFurniture loadedFurnitureByEntityId(int entityId) { - return this.byEntityId.get(entityId); + @Override + public BukkitFurniture loadedFurnitureByVirtualEntityId(int entityId) { + return this.byVirtualEntityId.get(entityId); } + @Nullable + @Override + public BukkitFurniture loadedFurnitureByColliderEntityId(int entityId) { + return this.byColliderEntityId.get(entityId); + } + + // 当元数据实体被卸载了 protected void handleMetaEntityUnload(Entity entity) { + // 不是持久化的 + if (!entity.isPersistent()) { + return; + } int id = entity.getEntityId(); BukkitFurniture furniture = this.byMetaEntityId.remove(id); if (furniture != null) { Location location = entity.getLocation(); - // 区块还在加载的时候,就重复卸载了 + // 区块还在加载的时候,就重复卸载了。为极其特殊情况 boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); if (!isPreventing) { furniture.destroySeats(); } - for (int sub : furniture.entityIds()) { - this.byEntityId.remove(sub); + for (int sub : furniture.virtualEntityIds()) { + this.byVirtualEntityId.remove(sub); + } + for (int sub : furniture.colliderEntityIds()) { + this.byColliderEntityId.remove(sub); } } } + // 保险起见,collision实体卸载也移除一下 protected void handleCollisionEntityUnload(Entity entity) { int id = entity.getEntityId(); - this.byMetaEntityId.remove(id); + this.byColliderEntityId.remove(id); } + // 检查这个区块的实体是否已经被加载了 private boolean isEntitiesLoaded(Location location) { CEWorld ceWorld = this.plugin.worldManager().getWorld(location.getWorld()); CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4); @@ -216,8 +224,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(entity, customFurniture); - furniture.addCollidersToWorld(); + // 创建新的家具 + createFurnitureInstance(entity, customFurniture); } protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { @@ -245,8 +253,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(entity, customFurniture); - furniture.addCollidersToWorld(); + createFurnitureInstance(entity, customFurniture); } protected void handleCollisionEntityAfterChunkLoad(Entity entity) { @@ -298,17 +305,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } } - private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { + // 创建家具实例,并初始化碰撞实体 + private void createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.entityIds()) { - this.byEntityId.put(entityId, bukkitFurniture); + for (int entityId : bukkitFurniture.virtualEntityIds()) { + this.byVirtualEntityId.put(entityId, bukkitFurniture); } for (Collider collisionEntity : bukkitFurniture.colliders()) { - int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); - this.byEntityId.put(collisionEntityId, bukkitFurniture); + this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); } - return bukkitFurniture; + bukkitFurniture.addCollidersToWorld(); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java deleted file mode 100644 index 339dad09d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import net.momirealms.craftengine.core.util.Color; - -public record ItemColorSource(Color dyedColor, int[] fireworkColors) { -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index 5c92715cf..ae884d0c7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -1,22 +1,61 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; public class ItemDisplayFurnitureElement implements FurnitureElement { private final ItemDisplayFurnitureElementConfig config; + private final WorldPosition position; + private final int entityId; + private final Object despawnPacket; + private final FurnitureColorSource colorSource; - public ItemDisplayFurnitureElement(ItemDisplayFurnitureElementConfig config) { + public ItemDisplayFurnitureElement(Furniture furniture, ItemDisplayFurnitureElementConfig config) { this.config = config; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + WorldPosition furniturePos = furniture.position(); + Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); + this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.colorSource = furniture.dataAccessor.getColorSource(); } @Override public void show(Player player) { - + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + this.entityId, UUID.randomUUID(), + this.position.x, this.position.y, this.position.z, 0, this.position.yRot, + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ), + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) + )), false); } @Override public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + @Override + public int[] virtualEntityIds() { + return new int[] {this.entityId}; + } + + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index fe52c4c83..cf58886a5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; @@ -28,21 +28,21 @@ import java.util.function.BiFunction; public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { public static final Factory FACTORY = new Factory(); - private final BiFunction> lazyMetadataPacket; - private final BiFunction> item; - private final Vector3f scale; - private final Vector3f position; - private final Vector3f translation; - private final float xRot; - private final float yRot; - private final Quaternionf rotation; - private final ItemDisplayContext displayContext; - private final Billboard billboard; - private final float shadowRadius; - private final float shadowStrength; - private final boolean applyDyedColor; + public final BiFunction> metadata; + public final Key itemId; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final ItemDisplayContext displayContext; + public final Billboard billboard; + public final float shadowRadius; + public final float shadowStrength; + public final boolean applyDyedColor; - public ItemDisplayFurnitureElementConfig(BiFunction> item, + public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, Vector3f position, Vector3f translation, @@ -65,10 +65,24 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig this.shadowRadius = shadowRadius; this.shadowStrength = shadowStrength; this.applyDyedColor = applyDyedColor; - this.item = item; - this.lazyMetadataPacket = (player, source) -> { + this.itemId = itemId; + BiFunction> itemFunction = (player, colorSource) -> { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + }; + this.metadata = (player, source) -> { List dataValues = new ArrayList<>(); - ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player, source).getLiteralObject(), dataValues); + ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); @@ -81,52 +95,56 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig } public Vector3f scale() { - return scale; + return this.scale; } public Vector3f position() { - return position; + return this.position; } public Vector3f translation() { - return translation; + return this.translation; } public float xRot() { - return xRot; + return this.xRot; } public float yRot() { - return yRot; + return this.yRot; } public Quaternionf rotation() { - return rotation; + return this.rotation; } public ItemDisplayContext displayContext() { - return displayContext; + return this.displayContext; } public Billboard billboard() { - return billboard; + return this.billboard; } public float shadowRadius() { - return shadowRadius; + return this.shadowRadius; } public float shadowStrength() { - return shadowStrength; + return this.shadowStrength; } public boolean applyDyedColor() { - return applyDyedColor; + return this.applyDyedColor; + } + + public Key itemId() { + return this.itemId; } @Override public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { - return new ItemDisplayFurnitureElement(this); + return new ItemDisplayFurnitureElement(furniture, this); } public static class Factory implements FurnitureElementConfigFactory { @@ -136,22 +154,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); return new ItemDisplayFurnitureElementConfig( - (player, colorSource) -> { - Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); - if (applyDyedColor && colorSource != null && wrappedItem != null) { - Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); - Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( - FireworkExplosion.Shape.SMALL_BALL, - new IntArrayList(colors), - new IntArrayList(), - false, - false - ))); - } - return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); - }, + itemId, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), - ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), @@ -164,4 +169,23 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ); } } + + @Override + public String toString() { + return "ItemDisplayFurnitureElementConfig{" + + "metadata=" + metadata + + ", itemId=" + itemId + + ", scale=" + scale + + ", position=" + position + + ", translation=" + translation + + ", xRot=" + xRot + + ", yRot=" + yRot + + ", rotation=" + rotation + + ", displayContext=" + displayContext + + ", billboard=" + billboard + + ", shadowRadius=" + shadowRadius + + ", shadowStrength=" + shadowStrength + + ", applyDyedColor=" + applyDyedColor + + '}'; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java index 5ae471042..2789d4347 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.Vec3d; @@ -54,4 +55,9 @@ public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { protected Object createDespawnPacket(int[] entityIds) { return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); } + + @Override + public void hide(Player player) { + player.sendPacket(createDespawnPacket(this.virtualEntityIds()), false); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 9f8bc0366..65836a4b5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -14,15 +15,15 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; private final Vec3d position; private final AABB aabb; private final Collider collider; - private final int[] entityIds; + private final int interactionId; private final Object spawnPacket; - private final Object despawnPacket; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); @@ -31,20 +32,21 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.position = Furniture.getRelativePosition(position, config.position()); this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); - int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - this.entityIds = new int[] {interactionId}; - List values = new ArrayList<>(3); + this.interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List values = new ArrayList<>(4); InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); + if (config.invisible) { + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, values); + } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + this.interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.interactionId, values) )); - this.despawnPacket = createDespawnPacket(this.entityIds); } @Override @@ -53,13 +55,8 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public void hide(Player player) { - player.sendPacket(this.despawnPacket, false); - } - - @Override - public AABB aabb() { - return this.aabb; + public AABB[] aabb() { + return new AABB[] { this.aabb }; } @Override @@ -68,13 +65,18 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public Collider collider() { - return this.collider; + public List colliders() { + return List.of(this.collider); } @Override public int[] virtualEntityIds() { - return this.entityIds; + return new int[] { this.interactionId }; + } + + @Override + public void collectVirtualEntityIds(Consumer collector) { + collector.accept(this.interactionId); } public InteractionFurnitureHitboxConfig config() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 2a1efd59f..7711218c2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -5,27 +5,34 @@ import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurniture import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; import java.util.Map; +import java.util.function.Consumer; public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, new Vector3f(1,1,1), true); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, false, new Vector3f(1,1,1), true); public final Vector3f size; public final boolean responsive; + public final boolean invisible; public InteractionFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile, + boolean invisible, Vector3f size, boolean responsive) { super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); this.size = size; this.responsive = responsive; + this.invisible = invisible; } public Vector3f size() { @@ -36,6 +43,18 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon return responsive; } + public boolean invisible() { + return invisible; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, size.x, size.y)); + } + } + @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); @@ -45,7 +64,7 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon @Override public InteractionFurnitureHitboxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position"); float width; float height; if (arguments.containsKey("scale")) { @@ -60,9 +79,10 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + boolean invisible = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("invisible", false), "invisible"); return new InteractionFurnitureHitboxConfig( SeatConfig.fromObj(arguments.get("seats")), - position, canUseOn, blocksBuilding, canBeHitByProjectile, + position, canUseOn, blocksBuilding, canBeHitByProjectile, invisible, new Vector3f(width, height, width), interactive ); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index b92b4440e..3b85c0ef7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -1,18 +1,46 @@ package net.momirealms.craftengine.bukkit.item.behavior; +import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptPlaceEvent; +import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.EventUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.Location; +import org.bukkit.World; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Optional; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); @@ -23,7 +51,7 @@ public class FurnitureItemBehavior extends ItemBehavior { } public Key furnitureId() { - return id; + return this.id; } @Override @@ -32,121 +60,96 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { -// Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); -// if (optionalCustomFurniture.isEmpty()) { -// CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); -// return InteractionResult.FAIL; -// } -// FurnitureConfig customFurniture = optionalCustomFurniture.get(); -// -// Direction clickedFace = context.getClickedFace(); -// AnchorType anchorType = switch (clickedFace) { -// case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; -// case UP -> AnchorType.GROUND; -// case DOWN -> AnchorType.CEILING; -// }; -// -// FurnitureConfig.Variant placement = customFurniture.getPlacement(anchorType); -// if (placement == null) { -// return InteractionResult.FAIL; -// } -// -// Player player = context.getPlayer(); -// // todo adventure check -// if (player != null && player.isAdventureMode()) { -// return InteractionResult.FAIL; -// } -// -// Vec3d clickedPosition = context.getClickLocation(); -// -// // trigger event -// org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; -// World world = (World) context.getLevel().platformWorld(); -// -// // get position and rotation for placement -// Vec3d finalPlacePosition; -// double furnitureYaw; -// if (anchorType == AnchorType.WALL) { -// furnitureYaw = Direction.getYaw(clickedFace); -// if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); -// finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); -// } else { -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); -// finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); -// } -// } else { -// furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); -// finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); -// } -// -// Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); -// -// List aabbs = new ArrayList<>(); -// for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { -// hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); -// } -// if (!aabbs.isEmpty()) { -// if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { -// return InteractionResult.FAIL; -// } -// } -// -// if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { -// return InteractionResult.FAIL; -// } -// -// if (player != null) { -// FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), -// DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); -// if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { -// return InteractionResult.FAIL; -// } -// } -// -// Item item = context.getItem(); -// // 不可能 -// if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; -// -// BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( -// furnitureLocation.clone(), customFurniture, -// FurnitureDataAccessor.builder() -// .item(item.copyWithCount(1)) -// .anchorType(anchorType) -// .dyedColor(item.dyedColor().orElse(null)) -// .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) -// .build(), false); -// -// if (player != null) { -// FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); -// if (EventUtils.fireAndCheckCancel(placeEvent)) { -// bukkitFurniture.destroy(); -// return InteractionResult.FAIL; -// } -// } -// -// Cancellable dummy = Cancellable.dummy(); -// PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() -// .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) -// .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) -// .withParameter(DirectContextParameters.EVENT, dummy) -// .withParameter(DirectContextParameters.HAND, context.getHand()) -// .withParameter(DirectContextParameters.ITEM_IN_HAND, item) -// ); -// customFurniture.execute(functionContext, EventTrigger.PLACE); -// if (dummy.isCancelled()) { -// return InteractionResult.SUCCESS_AND_CANCEL; -// } -// -// if (player != null) { -// if (!player.canInstabuild()) { -// item.count(item.count() - 1); -// } -// player.swingHand(context.getHand()); -// } -// -// context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); + Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); + if (optionalCustomFurniture.isEmpty()) { + CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); + return InteractionResult.FAIL; + } + + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + FurnitureVariant variant = customFurniture.anyVariant(); + if (variant == null) { + return InteractionResult.FAIL; + } + + Player player = context.getPlayer(); + if (player != null && player.isAdventureMode()) { + return InteractionResult.FAIL; + } + + Vec3d clickedPosition = context.getClickLocation(); + + // trigger event + org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; + World world = (World) context.getLevel().platformWorld(); + + // get position and rotation for placement + Vec3d finalPlacePosition = clickedPosition; + double furnitureYaw = 180 + (player != null ? player.yRot() : 0); + + Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); + WorldPosition furniturePos = LocationUtils.toWorldPosition(furnitureLocation); + List aabbs = new ArrayList<>(); + // 收集阻挡的碰撞箱 + for (FurnitureHitBoxConfig hitBoxConfig : variant.hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(furniturePos, aabbs::add); + } + // 检查方块、实体阻挡 + if (!aabbs.isEmpty()) { + if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + return InteractionResult.FAIL; + } + } + // 检查其他插件兼容性 + if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { + return InteractionResult.FAIL; + } + // 触发尝试放置的事件 + if (player != null) { + FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, variant, furnitureLocation.clone(), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); + if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { + return InteractionResult.FAIL; + } + } + Item item = context.getItem(); + if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; + // 获取家具物品的一些属性 + FurnitureDataAccessor dataAccessor = FurnitureDataAccessor.of(new CompoundTag()); + dataAccessor.setVariant(variant.name()); + dataAccessor.setItem(item.copyWithCount(1)); + dataAccessor.setDyedColor(item.dyedColor().orElse(null)); + dataAccessor.setFireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)); + // 放置家具 + BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place(furnitureLocation.clone(), customFurniture, dataAccessor, false); + // 触发放置事件 + if (player != null) { + FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + bukkitFurniture.destroy(); + return InteractionResult.FAIL; + } + } + // 触发ce事件 + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.HAND, context.getHand()) + .withParameter(DirectContextParameters.ITEM_IN_HAND, item) + ); + customFurniture.execute(functionContext, EventTrigger.PLACE); + if (dummy.isCancelled()) { + return InteractionResult.SUCCESS_AND_CANCEL; + } + // 后续处理 + if (player != null) { + if (!player.canInstabuild()) { + item.count(item.count() - 1); + } + player.swingHand(context.getHand()); + } + context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 9ab146c37..57a5f59cc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1247,7 +1247,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entityId from ServerboundPickItemFromEntityPacket", e); return; } - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByVirtualEntityId(entityId); if (furniture == null) return; Player player = (Player) user.platformPlayer(); if (player == null) return; @@ -1469,6 +1469,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes player.setClientSideWorld(BukkitAdaptors.adapt(world)); player.clearTrackedChunks(); player.clearTrackedBlockEntities(); + player.clearTrackedFurniture(); } else { CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); } @@ -1745,9 +1746,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes @Override public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); if (handler != null) { handler.handleMoveAndRotate(user, event, packet); @@ -1778,7 +1776,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundRotateHeadPacket", t); return; } - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { + System.out.println("RotateHeadListener"); event.setCancelled(true); } } @@ -1796,7 +1795,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundSetEntityMotionPacket", t); return; } - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { + System.out.println("SetEntityMotionListener"); event.setCancelled(true); } } @@ -3349,7 +3349,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes for (int i = 0, size = intList.size(); i < size; i++) { int entityId = intList.getInt(i); EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); - if (handler != null && handler.handleEntitiesRemove(intList)) { + if (handler != null && handler.handleEntitiesRemove(user, intList)) { changed = true; } } @@ -3698,7 +3698,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int entityId = hasModelEngine() ? plugin.compatibilityManager().interactionToBaseEntity(buf.readVarInt()) : buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByVirtualEntityId(entityId); if (furniture == null) return; int actionType = buf.readVarInt(); BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; @@ -3799,7 +3799,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBox.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } @@ -3976,16 +3976,19 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.handlers[MEntityTypes.ITEM_DISPLAY$registryId] = (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(id); if (furniture != null) { - furniture.show((BukkitServerPlayer) user); - event.setCancelled(true); + serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); + if (Config.enableEntityCulling()) { + serverPlayer.addTrackedFurniture(id, furniture); + } else { + furniture.show(serverPlayer); + } // fixme 外部模型 -// user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); -// user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); -// if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { -// event.setCancelled(true); -// } + if (Config.hideBaseEntity() && true) { + event.setCancelled(true); + } } else { user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); } @@ -3995,7 +3998,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(id); if (furniture != null) { event.setCancelled(true); user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); @@ -4006,7 +4009,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(id); if (furniture != null) { event.setCancelled(true); user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java index 0a60cfc3a..3e7277429 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -1,22 +1,26 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; -import java.util.List; - public class FurniturePacketHandler implements EntityPacketHandler { - private final List fakeEntities; + private final int metaEntityId; + private final int[] virtualHitboxEntities; - public FurniturePacketHandler(List fakeEntities) { - this.fakeEntities = fakeEntities; + public FurniturePacketHandler(int metaEntityId, int[] virtualHitboxEntities) { + this.virtualHitboxEntities = virtualHitboxEntities; + this.metaEntityId = metaEntityId; } @Override - public boolean handleEntitiesRemove(IntList entityIds) { - entityIds.addAll(this.fakeEntities); + public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { + ((Player) user).removeTrackedFurniture(this.metaEntityId); + for (int entityId : this.virtualHitboxEntities) { + entityIds.add(entityId); + } return true; } @@ -29,4 +33,9 @@ public class FurniturePacketHandler implements EntityPacketHandler { public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { event.setCancelled(true); } + + @Override + public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { + event.setCancelled(true); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index e7bdc3bea..a67b52a51 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -25,6 +25,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.data.EntityData; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; @@ -144,6 +145,7 @@ public class BukkitServerPlayer extends Player { private int lastStopMiningTick; // 跟踪到的方块实体渲染器 private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); + private final Map trackedFurniture = new ConcurrentHashMap<>(); private final EntityCulling culling; private Vec3d firstPersonCameraVec3; private Vec3d thirdPersonCameraVec3; @@ -603,44 +605,54 @@ public class BukkitServerPlayer extends Player { boolean useRayTracing = Config.entityCullingRayTracing(); if (this.enableEntityCulling) { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - CullingData cullingData = cullableObject.cullable.cullingData(); - if (cullingData != null) { - boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); - // 之前可见 - if (cullableObject.isShown) { - boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); - if (!firstPersonVisible && !thirdPersonVisible) { - cullableObject.setShown(this, false); - } - } - // 之前不可见 - else { - // 但是第一人称可见了 - if (firstPersonVisible) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; - } - cullableObject.setShown(this, true); - continue; - } - if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; - } - cullableObject.setShown(this, true); - } - // 仍然不可见 - } - } else { - cullableObject.setShown(this, true); - } + cullEntity(useRayTracing, cullableObject); + } + for (VirtualCullableObject cullableObject : this.trackedFurniture.values()) { + cullEntity(useRayTracing, cullableObject); } } else { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { cullableObject.setShown(this, true); } + for (VirtualCullableObject cullableObject : this.trackedFurniture.values()) { + cullableObject.setShown(this, true); + } + } + } + + private void cullEntity(boolean useRayTracing, VirtualCullableObject cullableObject) { + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); + // 之前可见 + if (cullableObject.isShown) { + boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); + if (!firstPersonVisible && !thirdPersonVisible) { + cullableObject.setShown(this, false); + } + } + // 之前不可见 + else { + // 但是第一人称可见了 + if (firstPersonVisible) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + return; + } + cullableObject.setShown(this, true); + return; + } + if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + return; + } + cullableObject.setShown(this, true); + } + // 仍然不可见 + } + } else { + cullableObject.setShown(this, true); } } @@ -1427,6 +1439,24 @@ public class BukkitServerPlayer extends Player { this.trackedBlockEntityRenderers.clear(); } + @Override + public void addTrackedFurniture(int entityId, Furniture furniture) { + this.trackedFurniture.put(entityId, new VirtualCullableObject(furniture)); + } + + @Override + public void removeTrackedFurniture(int entityId) { + VirtualCullableObject remove = this.trackedFurniture.remove(entityId); + if (remove != null && remove.isShown()) { + remove.cullable().hide(this); + } + } + + @Override + public void clearTrackedFurniture() { + this.trackedFurniture.clear(); + } + @Override public WorldPosition eyePosition() { return LocationUtils.toWorldPosition(this.getEyeLocation()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 092e70ea7..c0569fd87 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -124,7 +124,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elementConfigs"), FurnitureElementConfigs::fromMap); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); // fixme 外部模型不应该在这 Optional externalModel; @@ -141,7 +141,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } + // fixme 动态计算aabb,因为家具具有朝向 + AABB maxAABB = new AABB(0,0,0,1,1,1); + variants.put(variantName, new FurnitureVariant( + variantName, + parseCullingData(section.get("entity-culling"), maxAABB), elements.toArray(new FurnitureElementConfig[0]), hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, @@ -149,15 +154,13 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { )); } - AABB maxAABB = null; - FurnitureConfig furniture = FurnitureConfig.builder() .id(id) .settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true))) .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .cullingData(parseCullingData(section.get("entity-culling"), maxAABB)) + .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index d50a23ea0..32f0b7c4a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,6 +1,10 @@ package net.momirealms.craftengine.core.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; @@ -15,13 +19,12 @@ import net.momirealms.craftengine.core.world.Cullable; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; public abstract class Furniture implements Cullable { @@ -29,18 +32,20 @@ public abstract class Furniture implements Cullable { public final FurnitureDataAccessor dataAccessor; public final WeakReference metaDataEntity; + protected CullingData cullingData; protected FurnitureVariant currentVariant; protected FurnitureElement[] elements; protected Collider[] colliders; protected FurnitureHitBox[] hitboxes; - protected Int2ObjectMap hitboxMap; - protected int[] entityIds; + protected int[] virtualEntityIds; + protected int[] colliderEntityIds; protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; this.metaDataEntity = new WeakReference<>(metaDataEntity); + this.setVariant(config.getVariant(data)); } public WeakReference metaDataEntity() { @@ -48,25 +53,24 @@ public abstract class Furniture implements Cullable { } public FurnitureVariant getCurrentVariant() { - return currentVariant; - } - - public String getCurrentVariantName() { - return null; + return this.currentVariant; } public void setVariant(FurnitureVariant variant) { this.currentVariant = variant; - WorldPosition position = this.position(); + this.hitboxMap = new Int2ObjectOpenHashMap<>(); // 初始化家具元素 + IntList virtualEntityIds = new IntArrayList(); FurnitureElementConfig[] elementConfigs = variant.elementConfigs(); this.elements = new FurnitureElement[elementConfigs.length]; for (int i = 0; i < elementConfigs.length; i++) { - this.elements[i] = elementConfigs[i].create(this); + FurnitureElement element = elementConfigs[i].create(this); + this.elements[i] = element; + element.collectVirtualEntityId(virtualEntityIds::addLast); } // 初始化碰撞箱 - FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.furnitureHitBoxConfigs(); - List colliders = new ArrayList<>(furnitureHitBoxConfigs.length); + FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.hitBoxConfigs(); + ObjectArrayList colliders = new ObjectArrayList<>(furnitureHitBoxConfigs.length); this.hitboxes = new FurnitureHitBox[furnitureHitBoxConfigs.length]; for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); @@ -74,12 +78,23 @@ public abstract class Furniture implements Cullable { for (int hitboxEntityId : hitbox.virtualEntityIds()) { this.hitboxMap.put(hitboxEntityId, hitbox); } - Collider collider = hitbox.collider(); - if (collider != null) { - colliders.add(collider); - } + colliders.addAll(hitbox.colliders()); + hitbox.collectVirtualEntityIds(virtualEntityIds::addLast); } + // 虚拟碰撞箱的实体id + this.virtualEntityIds = virtualEntityIds.toIntArray(); this.colliders = colliders.toArray(new Collider[0]); + this.colliderEntityIds = colliders.stream().mapToInt(Collider::entityId).toArray(); + this.cullingData = createCullingData(variant.cullingData()); + } + + private CullingData createCullingData(CullingData parent) { + if (parent == null) return null; + AABB aabb = parent.aabb; + WorldPosition position = position(); + Vec3d pos1 = getRelativePosition(position, new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ)); + Vec3d pos2 = getRelativePosition(position, new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ)); + return new CullingData(new AABB(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); } @Nullable @@ -90,15 +105,20 @@ public abstract class Furniture implements Cullable { @Nullable @Override public CullingData cullingData() { - return this.config.cullingData(); + return this.cullingData; } public Key id() { return this.config.id(); } - public int[] entityIds() { - return this.entityIds; + // 会发给玩家的包 + public int[] virtualEntityIds() { + return this.virtualEntityIds; + } + + public int[] colliderEntityIds() { + return colliderEntityIds; } public UUID uuid() { @@ -146,11 +166,11 @@ public abstract class Furniture implements Cullable { public abstract void destroy(); public FurnitureConfig config() { - return config; + return this.config; } public FurnitureDataAccessor dataAccessor() { - return dataAccessor; + return this.dataAccessor; } public Collider[] colliders() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java new file mode 100644 index 000000000..2a6427080 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.util.Color; + +public record FurnitureColorSource(Color dyedColor, int[] fireworkColors) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index 6e70376e6..7ef9ffd4c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -67,8 +67,6 @@ public interface FurnitureConfig { } - CullingData cullingData(); - static Builder builder() { return new FurnitureConfigImpl.BuilderImpl(); } @@ -87,8 +85,6 @@ public interface FurnitureConfig { Builder behavior(FurnitureBehavior behavior); - Builder cullingData(CullingData cullingData); - FurnitureConfig build(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index 8d6fe94b8..632e4fe80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -23,7 +23,6 @@ class FurnitureConfigImpl implements FurnitureConfig { private final Map variants; private final Map>> events; private final FurnitureBehavior behavior; - private final CullingData cullingData; @Nullable private final LootTable lootTable; @@ -32,14 +31,12 @@ class FurnitureConfigImpl implements FurnitureConfig { @NotNull Map variants, @NotNull Map>> events, @NotNull FurnitureBehavior behavior, - @Nullable CullingData cullingData, @Nullable LootTable lootTable) { this.id = id; this.settings = settings; this.variants = ImmutableMap.copyOf(variants); this.lootTable = lootTable; this.behavior = behavior; - this.cullingData = cullingData; this.events = events; } @@ -75,11 +72,6 @@ class FurnitureConfigImpl implements FurnitureConfig { return this.behavior; } - @Override - public CullingData cullingData() { - return this.cullingData; - } - @Nullable @Override public FurnitureVariant getVariant(String variantName) { @@ -93,11 +85,10 @@ class FurnitureConfigImpl implements FurnitureConfig { private Map>> events; private LootTable lootTable; private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE; - private CullingData cullingData; @Override public FurnitureConfig build() { - return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.cullingData, this.lootTable); + return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.lootTable); } @Override @@ -130,12 +121,6 @@ class FurnitureConfigImpl implements FurnitureConfig { return this; } - @Override - public Builder cullingData(CullingData cullingData) { - this.cullingData = cullingData; - return this; - } - @Override public Builder behavior(FurnitureBehavior behavior) { this.behavior = behavior; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 34f862f4f..e1bb1bd0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -68,12 +68,20 @@ public class FurnitureDataAccessor { this.data.putByteArray(ITEM, item.toByteArray()); } + public FurnitureColorSource getColorSource() { + return new FurnitureColorSource(dyedColor().orElse(null), fireworkExplosionColors().orElse(null)); + } + public Optional fireworkExplosionColors() { if (this.data.containsKey(FIREWORK_EXPLOSION_COLORS)) return Optional.of(this.data.getIntArray(FIREWORK_EXPLOSION_COLORS)); return Optional.empty(); } public void setFireworkExplosionColors(int[] colors) { + if (colors == null) { + this.data.remove(FIREWORK_EXPLOSION_COLORS); + return; + } this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); } @@ -82,7 +90,11 @@ public class FurnitureDataAccessor { return Optional.empty(); } - public void setDyedColor(Color color) { + public void setDyedColor(@Nullable Color color) { + if (color == null) { + this.data.remove(DYED_COLOR); + return; + } this.data.putInt(DYED_COLOR, color.color()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index 5f8ca181a..99d4ee3a0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; @@ -31,16 +30,14 @@ public interface FurnitureManager extends Manageable { Map loadedFurniture(); - boolean isFurnitureRealEntity(int entityId); + boolean isFurnitureMetaEntity(int entityId); @Nullable - Furniture loadedFurnitureByRealEntityId(int entityId); + Furniture loadedFurnitureByMetaEntityId(int entityId); @Nullable - default Furniture loadedFurnitureByRealEntity(AbstractEntity entity) { - return loadedFurnitureByRealEntityId(entity.entityID()); - } + Furniture loadedFurnitureByVirtualEntityId(int entityId); @Nullable - Furniture loadedFurnitureByEntityId(int entityId); + Furniture loadedFurnitureByColliderEntityId(int entityId); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java index 056a8f47f..206a5ddf7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -2,12 +2,16 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import java.util.Optional; -public record FurnitureVariant(FurnitureElementConfig[] elementConfigs, - FurnitureHitBoxConfig[] furnitureHitBoxConfigs, +public record FurnitureVariant(String name, + @Nullable CullingData cullingData, + FurnitureElementConfig[] elementConfigs, + FurnitureHitBoxConfig[] hitBoxConfigs, Optional externalModel, Optional dropOffset) { } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java index 118d5bd92..6a625f941 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java @@ -2,8 +2,14 @@ package net.momirealms.craftengine.core.entity.furniture.element; import net.momirealms.craftengine.core.entity.player.Player; +import java.util.function.Consumer; + public interface FurnitureElement { + int[] virtualEntityIds(); + + void collectVirtualEntityId(Consumer collector); + void show(Player player); void hide(Player player); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java index f977145e7..b121fbe02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java @@ -10,7 +10,11 @@ public abstract class AbstractFurnitureHitBoxConfig i protected final boolean blocksBuilding; protected final boolean canBeHitByProjectile; - public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { + public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile) { this.seats = seats; this.position = position; this.canUseItemOn = canUseItemOn; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 7fbb4ca70..7f3d894c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -4,26 +4,41 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; +import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; -import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { - Seat[] seats(); - - AABB aabb(); - Vec3d position(); - @Nullable - Collider collider(); + Seat[] seats(); + + AABB[] aabb(); + + List colliders(); int[] virtualEntityIds(); + void collectVirtualEntityIds(Consumer collector); + void show(Player player); void hide(Player player); FurnitureHitBoxConfig config(); + + default Optional clip(Vec3d min, Vec3d max) { + for (AABB value : aabb()) { + Optional clip = value.clip(min, max); + if (clip.isPresent()) { + return clip; + } + } + return Optional.empty(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index e1730c7f2..689cb130c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -2,8 +2,12 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; +import java.util.function.Consumer; + public interface FurnitureHitBoxConfig { H create(Furniture furniture); @@ -17,4 +21,6 @@ public interface FurnitureHitBoxConfig { boolean canBeHitByProjectile(); boolean canUseItemOn(); + + void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index ae13804e6..2f89bf90b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.advancement.AdvancementType; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.AbstractEntity; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; @@ -217,12 +218,18 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void removeTrackedBlockEntities(Collection renders); + public abstract void addTrackedFurniture(int entityId, Furniture furniture); + public abstract void clearTrackedBlockEntities(); @Override public void remove() { } + public abstract void removeTrackedFurniture(int entityId); + + public abstract void clearTrackedFurniture(); + public abstract WorldPosition eyePosition(); @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java index dcf69a5e4..c7dd3f082 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -14,7 +14,7 @@ public class FurnitureParameterProvider implements ChainParameterProvider f.config().id()); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid); - CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, Furniture::getCurrentVariantName); + CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, f -> f.getCurrentVariant().name()); CONTEXT_FUNCTIONS.put(DirectContextParameters.X, furniture -> furniture.position().x()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, furniture -> furniture.position().y()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, furniture -> furniture.position().z()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java index 57fb85de9..233b09cf5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player; public interface EntityPacketHandler { - default boolean handleEntitiesRemove(IntList entityIds) { + default boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { return false; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index 4461e5d93..08308a8fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -266,6 +266,8 @@ public final class ResourceConfigUtils { if (o == null) return new Vector3f(); if (o instanceof List list && list.size() == 3) { return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); + } else if (o instanceof Number number) { + return new Vector3f(number.floatValue()); } else { String stringFormat = o.toString(); String[] split = stringFormat.split(","); From 34cb3936b084fd76d13bd8bbf47237b1a8e2304b Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 04:45:29 +0800 Subject: [PATCH 24/46] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84part4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/event/FurnitureAttemptPlaceEvent.java | 2 - .../furniture/BukkitFurnitureManager.java | 2 +- .../furniture/FurnitureEventListener.java | 12 +- .../ItemDisplayFurnitureElementConfig.java | 2 +- .../hitbox/AbstractFurnitureHitBox.java | 11 - .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 65 ++-- .../hitbox/ShulkerFurnitureHitbox.java | 131 ++++++++ .../hitbox/ShulkerFurnitureHitboxConfig.java | 317 ++++++++++++++++++ .../item/behavior/FurnitureItemBehavior.java | 7 +- .../plugin/network/BukkitNetworkManager.java | 70 ++-- .../handler/FurniturePacketHandler.java | 5 - .../core/entity/furniture/Furniture.java | 34 +- .../entity/furniture/FurnitureConfig.java | 1 - .../entity/furniture/FurnitureConfigImpl.java | 1 - .../furniture/hitbox/FurnitureHitBox.java | 14 +- .../furniture/hitbox/FurnitureHitboxPart.java | 7 + 17 files changed, 533 insertions(+), 150 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index d7d053413..7e492239e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -1,12 +1,10 @@ package net.momirealms.craftengine.bukkit.api.event; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index a1dee6c88..21693ed18 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -153,7 +153,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 当元数据实体被卸载了 - protected void handleMetaEntityUnload(Entity entity) { + protected void handleMetaEntityUnload(ItemDisplay entity) { // 不是持久化的 if (!entity.isPersistent()) { return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index a8b0e8da5..534a3b841 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -75,8 +75,8 @@ public class FurnitureEventListener implements Listener { public void onChunkUnload(ChunkUnloadEvent event) { Entity[] entities = event.getChunk().getEntities(); for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } @@ -87,8 +87,8 @@ public class FurnitureEventListener implements Listener { public void onWorldUnload(WorldUnloadEvent event) { List entities = event.getWorld().getEntities(); for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } @@ -98,8 +98,8 @@ public class FurnitureEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onEntityUnload(EntityRemoveFromWorldEvent event) { Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index cf58886a5..caa972e9b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -2,11 +2,11 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java index 2789d4347..f40ead283 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -8,7 +7,6 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; -import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.Vec3d; @@ -51,13 +49,4 @@ public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); return new BukkitCollider(world.serverWorld(), nmsAABB, position.x, position.y, position.z, canBeHitByProjectile, canCollide, blocksBuilding); } - - protected Object createDespawnPacket(int[] entityIds) { - return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); - } - - @Override - public void hide(Player player) { - player.sendPacket(createDespawnPacket(this.virtualEntityIds()), false); - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index c1ef83ddb..b930275bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -8,7 +8,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); -// register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); + register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); // register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 65836a4b5..836434808 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -7,6 +8,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -15,24 +17,22 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; - private final Vec3d position; - private final AABB aabb; private final Collider collider; - private final int interactionId; private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); this.config = config; WorldPosition position = furniture.position(); - this.position = Furniture.getRelativePosition(position, config.position()); - this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); - this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); - this.interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.fromInteraction(pos, config.size.x, config.size.y); + this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); List values = new ArrayList<>(4); InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); @@ -42,11 +42,28 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - this.interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) )); + this.part = new FurnitureHitboxPart(interactionId, aabb, pos); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public InteractionFurnitureHitboxConfig config() { + return this.config; } @Override @@ -55,31 +72,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public AABB[] aabb() { - return new AABB[] { this.aabb }; - } - - @Override - public Vec3d position() { - return this.position; - } - - @Override - public List colliders() { - return List.of(this.collider); - } - - @Override - public int[] virtualEntityIds() { - return new int[] { this.interactionId }; - } - - @Override - public void collectVirtualEntityIds(Consumer collector) { - collector.accept(this.interactionId); - } - - public InteractionFurnitureHitboxConfig config() { - return this.config; + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java new file mode 100644 index 000000000..69842ded6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -0,0 +1,131 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { + private final ShulkerFurnitureHitboxConfig config; + private final List parts; + private final List colliders; + private final Object spawnPacket; + private final Object despawnPacket; + + public ShulkerFurnitureHitbox(Furniture furniture, ShulkerFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + int[] entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); + WorldPosition position = furniture.position(); + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(config.position())); + double x = position.x(); + double y = position.y(); + double z = position.z(); + float yaw = position.yRot(); + double originalY = y + offset.y; + double integerPart = Math.floor(originalY); + double fractionalPart = originalY - integerPart; + double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY; + List packets = new ArrayList<>(); + List colliders = new ArrayList<>(); + List parts = new ArrayList<>(); + + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw, + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, + MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); + + // fix some special occasions + if (originalY != processedY) { + double deltaY = originalY - processedY; + short ya = (short) (deltaY * 8192); + try { + packets.add(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance( + entityIds[1], (short) 0, ya, (short) 0, true + )); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to construct ClientboundMoveEntityPacket$Pos", e); + } + } + if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + config.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); + this.parts = parts; + this.colliders = colliders; + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); + } + + @Override + public List colliders() { + return this.colliders; + } + + @Override + public List parts() { + return this.parts; + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public ShulkerFurnitureHitboxConfig config() { + return this.config; + } + + public int[] acquireEntityIds(Supplier entityIdSupplier) { + if (config.interactionEntity) { + if (config.direction.stepY() != 0) { + // 展示实体 // 潜影贝 // 交互实体 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; + } else { + // 展示实体 // 潜影贝 // 交互实体1 // 交互实体2 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; + } + } else { + // 展示实体 // 潜影贝 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java new file mode 100644 index 000000000..4a86ba359 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -0,0 +1,317 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; +import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + public final float scale; + public final byte peek; + public final boolean interactive; + public final boolean interactionEntity; + public final Direction direction; + public final DirectionalShulkerSpawner spawner; + public final List cachedShulkerValues = new ArrayList<>(); + public final AABBCreator aabbCreator; + + public ShulkerFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + float scale, + byte peek, + boolean interactive, + boolean interactionEntity, + Direction direction) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.peek = peek; + this.interactive = interactive; + this.interactionEntity = interactionEntity; + this.direction = direction; + + ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues); + ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues); + ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI + ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible + + List cachedInteractionValues = new ArrayList<>(); + float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; + if (direction == Direction.UP) { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + collider.accept(this.createCollider(Direction.UP, world, offset, x, y, z, entityIds[1], aabb)); + if (interactionEntity) { + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); + } else if (direction == Direction.DOWN) { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb)); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP)))); + if (interactionEntity) { + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); + } else { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(scale + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); + Direction shulkerDirection = shulkerAnchor.opposite(); + collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb)); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor))))); + if (interactionEntity) { + // first interaction + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + // second interaction + double distance = shulkerHeight - scale; + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> { + Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); + Direction shulkerDirection = shulkerAnchor.opposite(); + return createAABB(shulkerDirection, offset, x, y, z); + }; + } + } + + public static float getPhysicalPeek(float peek) { + return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - targetPos.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(position())); + aabbConsumer.accept(this.aabbCreator.create(targetPos.x, targetPos.y, targetPos.z, targetPos.yRot, offset)); + } + } + + public float scale() { + return scale; + } + + public byte peek() { + return peek; + } + + public boolean interactive() { + return interactive; + } + + public boolean interactionEntity() { + return interactionEntity; + } + + public Direction direction() { + return direction; + } + + @Override + public ShulkerFurnitureHitbox create(Furniture furniture) { + return new ShulkerFurnitureHitbox(furniture, this); + } + + @FunctionalInterface + public interface AABBCreator { + + AABB create(double x, double y, double z, float yaw, Vector3f offset); + } + + @FunctionalInterface + public interface DirectionalShulkerSpawner { + + void accept(int[] entityIds, + World world, + double x, + double y, + double z, + float yaw, + Vector3f offset, + Consumer packets, + Consumer collider, + Consumer aabb); + } + + public Collider createCollider(Direction direction, World world, + Vector3f offset, double x, double y, double z, + int entityId, + Consumer aabb) { + AABB ceAABB = createAABB(direction, offset, x, y, z); + Object level = world.serverWorld(); + Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); + aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z))); + return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); + } + + public AABB createAABB(Direction direction, Vector3f relativePos, double x, double y, double z) { + float peek = getPhysicalPeek(this.peek * 0.01F); + double x1 = -this.scale * 0.5; + double y1 = 0.0; + double z1 = -this.scale * 0.5; + double x2 = this.scale * 0.5; + double y2 = this.scale; + double z2 = this.scale * 0.5; + + double dx = (double) direction.stepX() * peek * (double) this.scale; + if (dx > 0) { + x2 += dx; + } else if (dx < 0) { + x1 += dx; + } + double dy = (double) direction.stepY() * peek * (double) this.scale; + if (dy > 0) { + y2 += dy; + } else if (dy < 0) { + y1 += dy; + } + double dz = (double) direction.stepZ() * peek * (double) this.scale; + if (dz > 0) { + z2 += dz; + } else if (dz < 0) { + z1 += dz; + } + double minX = x + x1 + relativePos.x(); + double maxX = x + x2 + relativePos.x(); + double minY = y + y1 + relativePos.y(); + double maxY = y + y2 + relativePos.y(); + double minZ = z + z1 - relativePos.z(); + double maxZ = z + z2 - relativePos.z(); + return new AABB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) { + switch (newDirection) { + case NORTH -> { + return switch (oldDirection) { + case NORTH -> Direction.NORTH; + case SOUTH -> Direction.SOUTH; + case WEST -> Direction.EAST; + case EAST -> Direction.WEST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case SOUTH -> { + return switch (oldDirection) { + case SOUTH -> Direction.NORTH; + case WEST -> Direction.WEST; + case EAST -> Direction.EAST; + case NORTH -> Direction.SOUTH; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case WEST -> { + return switch (oldDirection) { + case SOUTH -> Direction.EAST; + case WEST -> Direction.NORTH; + case EAST -> Direction.SOUTH; + case NORTH -> Direction.WEST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case EAST -> { + return switch (oldDirection) { + case SOUTH -> Direction.WEST; + case WEST -> Direction.SOUTH; + case EAST -> Direction.NORTH; + case NORTH -> Direction.EAST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + default -> throw new IllegalStateException("Unexpected value: " + newDirection); + } + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public ShulkerFurnitureHitboxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); + byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); + Direction directionEnum = ResourceConfigUtils.getAsEnum(arguments.get("direction"), Direction.class, Direction.UP); + boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); + boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity"); + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new ShulkerFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, + canUseItemOn, blocksBuilding, canBeHitByProjectile, + scale, peek, interactive, interactionEntity, directionEnum + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 3b85c0ef7..19a9aea16 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -6,13 +6,11 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; -import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -28,7 +26,10 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.Cancellable; +import net.momirealms.craftengine.core.util.ItemUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 57a5f59cc..578925913 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -60,6 +60,7 @@ import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -358,8 +359,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(new EntityEventListener(), NetworkReflections.clazz$ClientboundEntityEventPacket); registerNMSPacketConsumer(new MovePosAndRotateEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); registerNMSPacketConsumer(new MovePosEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); - registerNMSPacketConsumer(new RotateHeadListener(), NetworkReflections.clazz$ClientboundRotateHeadPacket); - registerNMSPacketConsumer(new SetEntityMotionListener(), NetworkReflections.clazz$ClientboundSetEntityMotionPacket); registerNMSPacketConsumer(new FinishConfigurationListener(), NetworkReflections.clazz$ClientboundFinishConfigurationPacket); registerNMSPacketConsumer(new LoginFinishedListener(), NetworkReflections.clazz$ClientboundLoginFinishedPacket); registerNMSPacketConsumer(new UpdateTagsListener(), NetworkReflections.clazz$ClientboundUpdateTagsPacket); @@ -1765,43 +1764,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } - public static class RotateHeadListener implements NMSPacketListener { - - @Override - public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { - int entityId; - try { - entityId = (int) NetworkReflections.methodHandle$ClientboundRotateHeadPacket$entityIdGetter.invokeExact(packet); - } catch (Throwable t) { - CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundRotateHeadPacket", t); - return; - } - if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { - System.out.println("RotateHeadListener"); - event.setCancelled(true); - } - } - } - - public static class SetEntityMotionListener implements NMSPacketListener { - - @Override - public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { - if (!VersionHelper.isOrAbove1_21_6()) return; - int entityId; - try { - entityId = (int) NetworkReflections.methodHandle$ClientboundSetEntityMotionPacket$idGetter.invokeExact(packet); - } catch (Throwable t) { - CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundSetEntityMotionPacket", t); - return; - } - if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { - System.out.println("SetEntityMotionListener"); - event.setCancelled(true); - } - } - } - public static class FinishConfigurationListener implements NMSPacketListener { @SuppressWarnings("unchecked") @@ -3726,11 +3688,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 先检查碰撞箱部分是否存在 FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); if (hitBox == null) return; - Vec3d pos = hitBox.position(); - - // 检查玩家是否能破坏此点 - if (!serverPlayer.canInteractPoint(pos, 16d)) { - return; + for (FurnitureHitboxPart part : hitBox.parts()) { + if (part.entityId() == entityId) { + // 检查玩家是否能破坏此点 + if (!serverPlayer.canInteractPoint(part.pos(), 16d)) { + return; + } + } } FurnitureAttemptBreakEvent preBreakEvent = new FurnitureAttemptBreakEvent(serverPlayer.platformPlayer(), furniture); @@ -3787,10 +3751,18 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 先检查碰撞箱部分是否存在 FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); if (hitBox == null) return; - Vec3d pos = hitBox.position(); - - // 检测距离 - if (!serverPlayer.canInteractPoint(pos, 16d)) { + FurnitureHitboxPart part = null; + for (FurnitureHitboxPart p : hitBox.parts()) { + if (p.entityId() == entityId) { + Vec3d pos = p.pos(); + // 检测距离 + if (!serverPlayer.canInteractPoint(pos, 16d)) { + return; + } + part = p; + } + } + if (part == null) { return; } @@ -3799,7 +3771,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = part.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java index 3e7277429..a73ebc3e2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -33,9 +33,4 @@ public class FurniturePacketHandler implements EntityPacketHandler { public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { event.setCancelled(true); } - - @Override - public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { - event.setCancelled(true); - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 32f0b7c4a..a69ccd26f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -24,13 +25,12 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.lang.ref.WeakReference; import java.util.UUID; public abstract class Furniture implements Cullable { public final FurnitureConfig config; public final FurnitureDataAccessor dataAccessor; - public final WeakReference metaDataEntity; + public final Entity metaDataEntity; protected CullingData cullingData; protected FurnitureVariant currentVariant; @@ -44,11 +44,11 @@ public abstract class Furniture implements Cullable { protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; - this.metaDataEntity = new WeakReference<>(metaDataEntity); + this.metaDataEntity = metaDataEntity; this.setVariant(config.getVariant(data)); } - public WeakReference metaDataEntity() { + public Entity metaDataEntity() { return this.metaDataEntity; } @@ -75,11 +75,11 @@ public abstract class Furniture implements Cullable { for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); this.hitboxes[i] = hitbox; - for (int hitboxEntityId : hitbox.virtualEntityIds()) { - this.hitboxMap.put(hitboxEntityId, hitbox); + for (FurnitureHitboxPart part : hitbox.parts()) { + this.hitboxMap.put(part.entityId(), hitbox); + virtualEntityIds.add(part.entityId()); } colliders.addAll(hitbox.colliders()); - hitbox.collectVirtualEntityIds(virtualEntityIds::addLast); } // 虚拟碰撞箱的实体id this.virtualEntityIds = virtualEntityIds.toIntArray(); @@ -122,9 +122,7 @@ public abstract class Furniture implements Cullable { } public UUID uuid() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.uuid(); + return this.metaDataEntity.uuid(); } @Override @@ -158,9 +156,7 @@ public abstract class Furniture implements Cullable { } public boolean isValid() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return false; - return entity.isValid(); + return this.metaDataEntity.isValid(); } public abstract void destroy(); @@ -178,15 +174,11 @@ public abstract class Furniture implements Cullable { } public WorldPosition position() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.position(); + return this.metaDataEntity.position(); } public int entityId() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return -1; - return entity.entityID(); + return this.metaDataEntity.entityID(); } public Vec3d getRelativePosition(Vector3f position) { @@ -200,8 +192,6 @@ public abstract class Furniture implements Cullable { } public World world() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.world(); + return this.metaDataEntity.world(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index 7ef9ffd4c..b9f34c864 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index 632e4fe80..a68613fef 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 7f3d894c4..0a249acd4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -6,25 +6,17 @@ import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { - Vec3d position(); - Seat[] seats(); - AABB[] aabb(); - List colliders(); - int[] virtualEntityIds(); - - void collectVirtualEntityIds(Consumer collector); + List parts(); void show(Player player); @@ -33,8 +25,8 @@ public interface FurnitureHitBox extends SeatOwner { FurnitureHitBoxConfig config(); default Optional clip(Vec3d min, Vec3d max) { - for (AABB value : aabb()) { - Optional clip = value.clip(min, max); + for (FurnitureHitboxPart value : parts()) { + Optional clip = value.aabb().clip(min, max); if (clip.isPresent()) { return clip; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java new file mode 100644 index 000000000..e69a747f5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.collision.AABB; + +public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos) { +} From 7082020220fd4c1e98f598649c7b41cd8abc7cce Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Wed, 3 Dec 2025 07:38:36 +0800 Subject: [PATCH 25/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BF=AB=E4=B9=90?= =?UTF-8?q?=E6=81=B6=E9=AD=82=E7=A2=B0=E6=92=9E=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 8 +- .../event/AsyncResourcePackCacheEvent.java | 2 +- .../behavior/NearLiquidBlockBehavior.java | 2 +- .../block/behavior/OnLiquidBlockBehavior.java | 2 +- .../entity/data/ItemDisplayEntityData.java | 2 +- .../hitbox/BukkitFurnitureHitboxTypes.java | 7 +- .../hitbox/HappyGhastFurnitureHitbox.java | 88 +++++++++++++++++++ .../HappyGhastFurnitureHitboxConfig.java | 83 +++++++++++++++++ .../hitbox/InteractionFurnitureHitbox.java | 14 +-- .../InteractionFurnitureHitboxConfig.java | 30 ++++++- .../hitbox/ShulkerFurnitureHitbox.java | 12 +-- .../hitbox/ShulkerFurnitureHitboxConfig.java | 28 +++--- .../bukkit/plugin/BukkitPlatform.java | 4 +- .../command/feature/DebugItemDataCommand.java | 2 +- .../feature/DebugSpawnFurnitureCommand.java | 5 +- .../handler/ProjectilePacketHandler.java | 2 +- .../plugin/network/payload/PayloadHelper.java | 4 +- .../craftengine/core/block/BlockBehavior.java | 2 +- .../behavior/IsPathFindableBlockBehavior.java | 2 +- .../element/BlockEntityElementConfigs.java | 7 +- .../entity/furniture/FurnitureConfig.java | 1 + .../furniture/FurnitureDataAccessor.java | 3 +- .../element/FurnitureElementConfigs.java | 7 +- .../hitbox/FurnitureHitBoxTypes.java | 7 +- .../craftengine/core/item/Item.java | 2 +- .../craftengine/core/item/ItemSettings.java | 4 +- .../recipe/CustomSmithingTransformRecipe.java | 2 +- .../item/recipe/CustomSmithingTrimRecipe.java | 2 +- .../function/ApplyBonusCountFunction.java | 4 +- .../core/pack/model/ItemModels.java | 2 +- .../model/condition/ConditionProperties.java | 2 +- .../RangeDispatchProperties.java | 2 +- .../pack/model/select/SelectProperties.java | 2 +- .../pack/model/special/SignSpecialModel.java | 2 +- .../pack/model/special/SpecialModels.java | 2 +- .../core/pack/model/tint/Tints.java | 2 +- .../core/pack/obfuscation/ObfA.java | 2 +- .../plugin/command/sender/AbstractSender.java | 2 +- .../core/plugin/config/Config.java | 4 +- .../plugin/config/StringKeyConstructor.java | 10 +-- .../argument/ExpressionTemplateArgument.java | 2 +- .../template/argument/TemplateArguments.java | 2 +- .../context/function/DamageFunction.java | 2 +- .../context/function/OpenWindowFunction.java | 2 +- .../function/ReplaceFurnitureFunction.java | 3 +- .../function/SpawnFurnitureFunction.java | 2 +- .../context/function/ToastFunction.java | 2 +- .../core/plugin/locale/LangData.java | 2 +- .../core/registry/BuiltInRegistries.java | 6 +- .../craftengine/core/registry/Registries.java | 6 +- .../craftengine/core/sound/SoundData.java | 2 +- .../craftengine/core/util/Pair.java | 8 +- .../core/util/ReflectionUtils.java | 2 +- .../core/util/ResourceConfigUtils.java | 72 ++++++++------- .../craftengine/core/util/SNBTReader.java | 2 +- .../craftengine/core/util/Tuple.java | 12 +-- .../craftengine/core/util/TypeUtils.java | 14 +-- 57 files changed, 359 insertions(+), 152 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 5d5c4532f..7f2a1cabe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -80,7 +80,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @return the loaded furniture */ @Nullable @@ -98,7 +98,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @return the loaded furniture */ @NotNull @@ -114,7 +114,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @param playSound whether to play place sounds * @return the loaded furniture */ @@ -133,7 +133,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @param playSound whether to play place sounds * @return the loaded furniture */ diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java index 62099bba8..dfa064fa3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java @@ -49,7 +49,7 @@ public final class AsyncResourcePackCacheEvent extends Event { * Adds an external resource pack to the cache. *

* This method accepts either a .zip file or a directory path representing a resource pack. - * The resource pack will be added to the appropriate cache collection based on its id. + * The resource pack will be added to the appropriate cache collection based on its type. *

* * @param path the file system path to the resource pack. Must be either a .zip file or a directory. diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java index 060282f7d..9c9b02c65 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java @@ -45,7 +45,7 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); List positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index 3e8a3fed1..e71780c7c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -40,7 +40,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); return new OnLiquidBlockBehavior(block, delay, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java index 091f8654e..09d39ebdc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java @@ -6,7 +6,7 @@ public class ItemDisplayEntityData extends DisplayEntityData { // Item display only public static final ItemDisplayEntityData DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY); /** - * Display id: + * Display type: * 0 = NONE * 1 = THIRD_PERSON_LEFT_HAND * 2 = THIRD_PERSON_RIGHT_HAND diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index b930275bc..10c1a33d2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; +import net.momirealms.craftengine.core.util.VersionHelper; public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { @@ -9,7 +10,9 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); -// register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); -// register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + if (VersionHelper.isOrAbove1_21_6()) { + register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java new file mode 100644 index 000000000..8905b7c2f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -0,0 +1,88 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { + private final HappyGhastFurnitureHitboxConfig config; + private final Collider collider; + private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; + + public HappyGhastFurnitureHitbox(Furniture furniture, HappyGhastFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(config.position())); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.fromInteraction(pos, 3 * config.scale(), 3 * config.scale()); + int happyGhastId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(3); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + happyGhastId, UUID.randomUUID(), position.x + offset.x, position.y + offset.y, position.z + offset.z, 0, position.yRot, + MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(happyGhastId, config.cachedValues())); + if (config.scale() != 1) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(happyGhastId, Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + this.collider = createCollider(furniture.world(), pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); + this.part = new FurnitureHitboxPart(happyGhastId, aabb, pos); + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(happyGhastId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public HappyGhastFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java new file mode 100644 index 000000000..7e28b68da --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + private final double scale; + private final boolean hardCollision; + private final List cachedValues = new ArrayList<>(3); + + public HappyGhastFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + double scale, + boolean hardCollision) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.hardCollision = hardCollision; + HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues); + HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI + HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible + } + + public double scale() { + return scale; + } + + public boolean hardCollision() { + return hardCollision; + } + + public List cachedValues() { + return cachedValues; + } + + @Override + public HappyGhastFurnitureHitbox create(Furniture furniture) { + return new HappyGhastFurnitureHitbox(furniture, this); + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, 3 * this.scale, 3 * this.scale)); + } + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public FurnitureHitBoxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position"); + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); + double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale"); + boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision"); + return new HappyGhastFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, canUseItemOn, blocksBuilding, canBeHitByProjectile, + scale, hardCollision + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 836434808..44380525c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,8 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; @@ -14,7 +12,6 @@ import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; -import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -30,22 +27,15 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.config = config; WorldPosition position = furniture.position(); Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, config.size.x, config.size.y); + AABB aabb = AABB.fromInteraction(pos, config.size().x, config.size().y); this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List values = new ArrayList<>(4); - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); - if (config.invisible) { - BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, values); - } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) )); this.part = new FurnitureHitboxPart(interactionId, aabb, pos); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 7711218c2..a3e853d17 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; @@ -10,16 +12,19 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.function.Consumer; public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, false, new Vector3f(1,1,1), true); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(); - public final Vector3f size; - public final boolean responsive; - public final boolean invisible; + private final Vector3f size; + private final boolean responsive; + private final boolean invisible; + private final List cachedValues = new ArrayList<>(4); public InteractionFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, @@ -33,6 +38,19 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon this.size = size; this.responsive = responsive; this.invisible = invisible; + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); + if (invisible) { + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedValues); + } + } + + private InteractionFurnitureHitboxConfig() { + super(new SeatConfig[0], new Vector3f(), false, false, false); + this.size = new Vector3f(1); + this.responsive = true; + this.invisible = false; } public Vector3f size() { @@ -47,6 +65,10 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon return invisible; } + public List cachedValues() { + return cachedValues; + } + @Override public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index 69842ded6..9a6c39d43 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -58,7 +58,7 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues()))); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); // fix some special occasions @@ -73,16 +73,16 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { CraftEngine.instance().logger().warn("Failed to construct ClientboundMoveEntityPacket$Pos", e); } } - if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) { + if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) { try { Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); } } - config.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); + config.spawner().accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); this.parts = parts; this.colliders = colliders; this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); @@ -115,8 +115,8 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { } public int[] acquireEntityIds(Supplier entityIdSupplier) { - if (config.interactionEntity) { - if (config.direction.stepY() != 0) { + if (config.interactionEntity()) { + if (config.direction().stepY() != 0) { // 展示实体 // 潜影贝 // 交互实体 return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 4a86ba359..004b73d26 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -32,14 +32,14 @@ import java.util.function.Consumer; public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public final float scale; - public final byte peek; - public final boolean interactive; - public final boolean interactionEntity; - public final Direction direction; - public final DirectionalShulkerSpawner spawner; - public final List cachedShulkerValues = new ArrayList<>(); - public final AABBCreator aabbCreator; + private final float scale; + private final byte peek; + private final boolean interactive; + private final boolean interactionEntity; + private final Direction direction; + private final DirectionalShulkerSpawner spawner; + private final List cachedShulkerValues = new ArrayList<>(6); + private final AABBCreator aabbCreator; public ShulkerFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, @@ -178,6 +178,14 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< return direction; } + public DirectionalShulkerSpawner spawner() { + return spawner; + } + + public List cachedShulkerValues() { + return cachedShulkerValues; + } + @Override public ShulkerFurnitureHitbox create(Furniture furniture) { return new ShulkerFurnitureHitbox(furniture, this); @@ -297,8 +305,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< @Override public ShulkerFurnitureHitboxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position"); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); Direction directionEnum = ResourceConfigUtils.getAsEnum(arguments.get("direction"), Direction.class, Direction.UP); boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java index 352ffd619..9a6729b0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java @@ -39,7 +39,7 @@ public class BukkitPlatform implements Platform { Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); } } @@ -55,7 +55,7 @@ public class BukkitPlatform implements Platform { CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java index 1b4b1a942..861c01eb5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java @@ -115,7 +115,7 @@ public class DebugItemDataCommand extends BukkitCommandFeature { } else if (nbt instanceof short[]) { value = Arrays.toString((short[]) nbt); } else { - value = "Unknown array id"; + value = "Unknown array type"; } } else { value = nbt.toString(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index c07060832..7b32aeba1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -24,6 +24,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder @@ -34,7 +35,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { // fixme 指令 @@ -47,7 +48,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(data.type()); if (codec == null) { - CraftEngine.instance().logger().warn("Unknown data id class: " + data.getClass().getName()); + CraftEngine.instance().logger().warn("Unknown data type class: " + data.getClass().getName()); return; } FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); @@ -65,7 +65,7 @@ public class PayloadHelper { @SuppressWarnings("unchecked") NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); if (codec == null) { - Debugger.COMMON.debug(() -> "Unknown data id received: " + type); + Debugger.COMMON.debug(() -> "Unknown data type received: " + type); return; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index b32258402..6d794219f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -78,7 +78,7 @@ public abstract class BlockBehavior { return (boolean) superMethod.call(); } - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type // 1.20.5+ BlockState state, PathComputationType pathComputationType public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return (boolean) superMethod.call(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java index dca06a1bc..7fe7d8e06 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java @@ -4,7 +4,7 @@ import java.util.concurrent.Callable; public interface IsPathFindableBlockBehavior { - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type // 1.20.5+ BlockState state, PathComputationType pathComputationType boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java index 1dabfa3be..c355c4eb2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java @@ -15,14 +15,15 @@ public abstract class BlockEntityElementConfigs { public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); public static final Key ITEM = Key.of("craftengine:item"); - public static void register(Key key, BlockEntityElementConfigFactory type) { - ((WritableRegistry) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE) + public static void register(Key key, BlockEntityElementConfigFactory type) { + ((WritableRegistry>) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE) .register(ResourceKey.create(Registries.BLOCK_ENTITY_ELEMENT_TYPE.location(), key), type); } public static BlockEntityElementConfig fromMap(Map arguments) { Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); - BlockEntityElementConfigFactory factory = BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE.getValue(type); + @SuppressWarnings("unchecked") + BlockEntityElementConfigFactory factory = (BlockEntityElementConfigFactory) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.block.state.entity_renderer.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index b9f34c864..cd5634893 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -48,6 +48,7 @@ public interface FurnitureConfig { if (optionalVariant.isPresent()) { variantName = optionalVariant.get(); } else { + @SuppressWarnings("deprecation") Optional optionalAnchorType = accessor.anchorType(); if (optionalAnchorType.isPresent()) { variantName = optionalAnchorType.get().name().toLowerCase(Locale.ROOT); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index e1bb1bd0e..c6bc0793a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -106,6 +106,7 @@ public class FurnitureDataAccessor { this.data.putString(VARIANT, variant); } + @SuppressWarnings("deprecation") @ApiStatus.Obsolete public Optional anchorType() { if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); @@ -113,7 +114,7 @@ public class FurnitureDataAccessor { } @ApiStatus.Obsolete - public FurnitureDataAccessor anchorType(AnchorType type) { + public FurnitureDataAccessor anchorType(@SuppressWarnings("deprecation") AnchorType type) { this.data.putInt(ANCHOR_TYPE, type.getId()); return this; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java index 8e375a743..7247cdf0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java @@ -15,14 +15,15 @@ public class FurnitureElementConfigs { public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); public static final Key ITEM = Key.of("craftengine:item"); - public static void register(Key key, FurnitureElementConfigFactory type) { - ((WritableRegistry) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) + public static void register(Key key, FurnitureElementConfigFactory type) { + ((WritableRegistry>) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) .register(ResourceKey.create(Registries.FURNITURE_ELEMENT_TYPE.location(), key), type); } public static FurnitureElementConfig fromMap(Map arguments) { Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); - FurnitureElementConfigFactory factory = BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); + @SuppressWarnings("unchecked") + FurnitureElementConfigFactory factory = (FurnitureElementConfigFactory) BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.element.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java index d4f002a9c..61fc5b871 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java @@ -17,14 +17,15 @@ public class FurnitureHitBoxTypes { public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); - public static void register(Key key, FurnitureHitBoxConfigFactory factory) { - ((WritableRegistry) BuiltInRegistries.FURNITURE_HITBOX_TYPE) + public static void register(Key key, FurnitureHitBoxConfigFactory factory) { + ((WritableRegistry>) BuiltInRegistries.FURNITURE_HITBOX_TYPE) .register(ResourceKey.create(Registries.FURNITURE_HITBOX_TYPE.location(), key), factory); } public static FurnitureHitBoxConfig fromMap(Map arguments) { Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(FurnitureHitBoxTypes.INTERACTION); - FurnitureHitBoxConfigFactory factory = BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); + @SuppressWarnings("unchecked") + FurnitureHitBoxConfigFactory factory = (FurnitureHitBoxConfigFactory) BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index f48a6bf39..5b7c2f563 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -28,7 +28,7 @@ import java.util.Optional; * This interface provides methods for managing item properties such as custom model data, * damage, display name, lore, enchantments, and tags. * - * @param the id of the item implementation + * @param the type of the item implementation */ public interface Item { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 0c125097d..64a67c8dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -446,8 +446,8 @@ public class ItemSettings { Key customTridentItemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("item"), "warning.config.item.settings.projectile.missing_item")); ItemDisplayContext displayType = ItemDisplayContext.valueOf(args.getOrDefault("display-transform", "NONE").toString().toUpperCase(Locale.ENGLISH)); Billboard billboard = Billboard.valueOf(args.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH)); - Vector3f translation = ResourceConfigUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation"); - Vector3f scale = ResourceConfigUtils.getAsVector3f(args.getOrDefault("scale", "1"), "scale"); + Vector3f translation = ResourceConfigUtils.getAsVector3f(args.getOrDefault("translation", 0), "translation"); + Vector3f scale = ResourceConfigUtils.getAsVector3f(args.getOrDefault("scale", 1), "scale"); Quaternionf rotation = ResourceConfigUtils.getAsQuaternionf(ResourceConfigUtils.get(args, "rotation"), "rotation"); double range = ResourceConfigUtils.getAsDouble(args.getOrDefault("range", 1), "range"); return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, billboard, scale, translation, rotation, range)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 206655f05..190ee8004 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -190,7 +190,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Override public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-id")); + List template = MiscUtils.getAsStringList(arguments.get("template-type")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); boolean mergeEnchantments = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-enchantments", false), "merge-enchantments"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index c3a749435..7119a5f48 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -141,7 +141,7 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe @Override public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-id")); + List template = MiscUtils.getAsStringList(arguments.get("template-type")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index b5d118862..c0475500d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -87,12 +87,12 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< public static Formula fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("number id cannot be null"); + throw new NullPointerException("number type cannot be null"); } Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); FormulaFactory factory = BuiltInRegistries.FORMULA_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown formula id: " + type); + throw new IllegalArgumentException("Unknown formula type: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index c79c52a8a..e6d151250 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -75,7 +75,7 @@ public class ItemModels { Key key = Key.withDefaultNamespace(type, "minecraft"); ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid item model id: " + key); + throw new IllegalArgumentException("Invalid item model type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index ce19f4e75..f2d5022fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -80,7 +80,7 @@ public class ConditionProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid condition property id: " + key); + throw new IllegalArgumentException("Invalid condition property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index 0d98ca88b..a8f09bcc0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -71,7 +71,7 @@ public class RangeDispatchProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid range dispatch property id: " + key); + throw new IllegalArgumentException("Invalid range dispatch property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index 464c79fc3..e6f5b46e0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -71,7 +71,7 @@ public class SelectProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid select property id: " + key); + throw new IllegalArgumentException("Invalid select property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index cdfffd3bb..b6a83032c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -45,7 +45,7 @@ public class SignSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); - String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-id"), "warning.config.item.model.special.sign.missing_wood_type"); + String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture"); return new SignSpecialModel(type, woodType, texture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index 8b222da2b..c27c4bba3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -80,7 +80,7 @@ public class SpecialModels { Key key = Key.withDefaultNamespace(type, "minecraft"); SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid special model id: " + key); + throw new IllegalArgumentException("Invalid special model type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index 8148873b5..d44d3102d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -65,7 +65,7 @@ public class Tints { Key key = Key.withDefaultNamespace(type, "minecraft"); TintReader reader = BuiltInRegistries.TINT_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid tint id: " + type); + throw new IllegalArgumentException("Invalid tint type: " + type); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java index 7833ea8fc..9d545f67d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java @@ -36,7 +36,7 @@ public enum ObfA { return type; } } - throw new IllegalArgumentException("Unknown resource id: " + xclf); + throw new IllegalArgumentException("Unknown resource type: " + xclf); } public static final byte[] VALUES = new byte[] { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java index 96a87ede0..ff1e71fa1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java @@ -9,7 +9,7 @@ import java.util.UUID; /** * Simple implementation of {@link Sender} using a {@link SenderFactory} * - * @param the command sender id + * @param the command sender type */ public final class AbstractSender implements Sender { private final Plugin plugin; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 2ec08dbdf..df09a8b08 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -448,10 +448,10 @@ public class Config { // furniture furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true); - furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-id", "interaction").toUpperCase(Locale.ENGLISH)); + furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH)); // equipment - equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.id", "chainmail").toLowerCase(Locale.ENGLISH); + equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail").toLowerCase(Locale.ENGLISH); if (!AbstractPackManager.ALLOWED_VANILLA_EQUIPMENT.contains(equipment$sacrificed_vanilla_armor$type)) { TranslationManager.instance().log("warning.config.equipment.invalid_sacrificed_armor", equipment$sacrificed_vanilla_armor$type); equipment$sacrificed_vanilla_armor$type = "chainmail"; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index 4c3ea46fc..10f7f8bea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -259,7 +259,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.byteValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -271,7 +271,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.shortValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -283,7 +283,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.longValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -295,7 +295,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.floatValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -307,7 +307,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.doubleValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index 790641d5d..cf02eeb46 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -61,7 +61,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { public TemplateArgument create(Map arguments) { return new ExpressionTemplateArgument( arguments.getOrDefault("expression", "").toString(), - ValueType.valueOf(arguments.getOrDefault("value-id", "double").toString().toUpperCase(Locale.ROOT)) + ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT)) ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java index 4c0e9baa7..9b1a8ef91 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java @@ -58,7 +58,7 @@ public class TemplateArguments { Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE); TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown argument id: " + type); + throw new IllegalArgumentException("Unknown argument type: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java index fa33c8b7f..97e197b55 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -43,7 +43,7 @@ public class DamageFunction extends AbstractConditionalFunc @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-id", "generic"))); + Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic"))); NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java index 1d1b17e4e..bbe2fe3d0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java @@ -65,7 +65,7 @@ public class OpenWindowFunction extends AbstractConditional @Override public Function create(Map arguments) { String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null); - String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-id"), "warning.config.function.open_window.missing_gui_type"); + String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type"); try { GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH)); return new OpenWindowFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), type, title); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 20dee5084..61eec5c6c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +@SuppressWarnings("deprecation") public class ReplaceFurnitureFunction extends AbstractConditionalFunction { private final Key newFurnitureId; private final NumberProvider x; @@ -95,7 +96,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-id"), AnchorType.class, null); + AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java index 1db66e416..d29b7758e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java @@ -87,7 +87,7 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-id")); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java index ec63aa261..853cc7c28 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java @@ -63,7 +63,7 @@ public class ToastFunction extends AbstractConditionalFunct @Override public Function create(Map arguments) { AdvancementType advancementType; - String advancementName = arguments.getOrDefault("advancement-id", "goal").toString(); + String advancementName = arguments.getOrDefault("advancement-type", "goal").toString(); try { advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java index b0f6d19dd..b677d18a5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java @@ -53,7 +53,7 @@ public class LangData { temp.put(result, entry.getValue()); } }, - () -> CraftEngine.instance().logger().warn("Unknown lang id: " + key) + () -> CraftEngine.instance().logger().warn("Unknown lang type: " + key) ); } else { temp.put(key, entry.getValue()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index 62bf71645..287ca2752 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -89,10 +89,10 @@ public class BuiltInRegistries { public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16); public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); - public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); + public static final Registry> BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); - public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); - public static final Registry FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); + public static final Registry> FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); + public static final Registry> FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 9b728d316..4afecdcd7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -91,8 +91,8 @@ public class Registries { public static final ResourceKey>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type")); public static final ResourceKey>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type")); public static final ResourceKey>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type")); - public static final ResourceKey> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); + public static final ResourceKey>> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); - public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); - public static final ResourceKey> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); + public static final ResourceKey>> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); + public static final ResourceKey>> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index cc7919e9c..af0356c6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -22,7 +22,7 @@ public record SoundData(Key id, SoundValue volume, SoundValue pitch) { SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume); return new SoundData(id, volumeValue, pitchValue); } else { - throw new IllegalArgumentException("Illegal object id for sound data: " + obj.getClass()); + throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java index 7d5e95ce6..de9774f5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java @@ -6,8 +6,8 @@ import java.util.Objects; * A generic class representing a pair of values. * This class provides methods to create and access pairs of values. * - * @param the id of the left value - * @param the id of the right value + * @param the type of the left value + * @param the type of the right value */ public record Pair(L left, R right) { @@ -16,8 +16,8 @@ public record Pair(L left, R right) { * * @param left the left value * @param right the right value - * @param the id of the left value - * @param the id of the right value + * @param the type of the left value + * @param the type of the right value * @return a new {@link Pair} with the specified values */ public static Pair of(final L left, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java index 30472678c..703571af4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java @@ -439,7 +439,7 @@ public class ReflectionUtils { public static List getMethods(@NotNull Class clazz, @NotNull Class returnType, @NotNull Class... parameterTypes) { List list = new ArrayList<>(); for (Method method : clazz.getMethods()) { - if (!returnType.isAssignableFrom(method.getReturnType()) // check id + if (!returnType.isAssignableFrom(method.getReturnType()) // check type || method.getParameterCount() != parameterTypes.length // check length ) continue; Class[] types = method.getParameterTypes(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index 08308a8fa..43a35d7dc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -137,13 +137,13 @@ public final class ResourceConfigUtils { try { return Integer.parseInt(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.int", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option); } } case Boolean b -> { return b ? 1 : 0; } - default -> throw new LocalizedResourceConfigException("warning.config.id.int", o.toString(), option); + default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option); } } @@ -162,11 +162,11 @@ public final class ResourceConfigUtils { try { return Double.parseDouble(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.double", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.id.double", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option); } } } @@ -183,14 +183,14 @@ public final class ResourceConfigUtils { try { return Float.parseFloat(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.float", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option); } } case Number number -> { return number.floatValue(); } default -> { - throw new LocalizedResourceConfigException("warning.config.id.float", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option); } } } @@ -206,15 +206,15 @@ public final class ResourceConfigUtils { case Number n -> { if (n.byteValue() == 0) return false; if (n.byteValue() == 1) return true; - throw new LocalizedResourceConfigException("warning.config.id.boolean", String.valueOf(n), option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", String.valueOf(n), option); } case String s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; - throw new LocalizedResourceConfigException("warning.config.id.boolean", s, option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", s, option); } default -> { - throw new LocalizedResourceConfigException("warning.config.id.boolean", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", o.toString(), option); } } } @@ -234,11 +234,11 @@ public final class ResourceConfigUtils { try { return Long.parseLong(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.long", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.long", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.id.long", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.long", o.toString(), option); } } } @@ -248,7 +248,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); } @SuppressWarnings("unchecked") @@ -259,24 +259,30 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); } public static Vector3f getAsVector3f(Object o, String option) { - if (o == null) return new Vector3f(); - if (o instanceof List list && list.size() == 3) { - return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); - } else if (o instanceof Number number) { - return new Vector3f(number.floatValue()); - } else { - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 3) { - return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); - } else if (split.length == 1) { - return new Vector3f(Float.parseFloat(split[0])); - } else { - throw new LocalizedResourceConfigException("warning.config.id.vector3f", stringFormat, option); + switch (o) { + case null -> { + return new Vector3f(); + } + case List list when list.size() == 3 -> { + return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); + } + case Number number -> { + return new Vector3f(number.floatValue()); + } + default -> { + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 3) { + return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); + } else if (split.length == 1) { + return new Vector3f(Float.parseFloat(split[0])); + } else { + throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + } } } } @@ -295,7 +301,7 @@ public final class ResourceConfigUtils { } else if (split.length == 1) { return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0); } else { - throw new LocalizedResourceConfigException("warning.config.id.quaternionf", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); } } } @@ -313,7 +319,7 @@ public final class ResourceConfigUtils { double d = Double.parseDouble(split[0]); return new Vec3d(d, d, d); } else { - throw new LocalizedResourceConfigException("warning.config.id.vec3d", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option); } } } @@ -347,7 +353,7 @@ public final class ResourceConfigUtils { public static AABB getAsAABB(Object o, String option) { switch (o) { - case null -> throw new LocalizedResourceConfigException("warning.config.id.aabb", "null", option); + case null -> throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option); case AABB aabb -> { return aabb; } @@ -367,7 +373,7 @@ public final class ResourceConfigUtils { try { args[i] = Double.parseDouble(list.get(i).toString()); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } @@ -378,7 +384,7 @@ public final class ResourceConfigUtils { try { args[i] = Double.parseDouble(split[i]); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } @@ -391,7 +397,7 @@ public final class ResourceConfigUtils { } else if (args.length == 6) { return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); } else { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 27fd39740..0be81388d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -156,7 +156,7 @@ public final class SNBTReader extends DefaultStringReader { // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element id at pos " + getCursor()); + // throw new IllegalArgumentException("Error element type at pos " + getCursor()); if (!(element instanceof Number number)) throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java index d91a2afaf..e4cf6baf5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java @@ -6,9 +6,9 @@ import java.util.Objects; * A generic class representing a tuple with three values. * This class provides methods for creating and accessing tuples with three values. * - * @param the id of the left value - * @param the id of the middle value - * @param the id of the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value */ public record Tuple(L left, M mid, R right) { @@ -18,9 +18,9 @@ public record Tuple(L left, M mid, R right) { * @param left the left value * @param mid the middle value * @param right the right value - * @param the id of the left value - * @param the id of the middle value - * @param the id of the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value * @return a new {@link Tuple} with the specified values */ public static Tuple of(final L left, final M mid, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java index 02687838f..8e3b3dc1a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java @@ -8,18 +8,18 @@ public class TypeUtils { private TypeUtils() {} /** - * Checks if the provided object is of the specified id. + * Checks if the provided object is of the specified type. * If not, throws an IllegalArgumentException with a detailed message. * * @param object The object to check. - * @param expectedType The expected class id. - * @param The id parameter for expectedType. - * @return The object cast to the expected id if it matches. - * @throws IllegalArgumentException if the object's id does not match the expected id. + * @param expectedType The expected class type. + * @param The type parameter for expectedType. + * @return The object cast to the expected type if it matches. + * @throws IllegalArgumentException if the object's type does not match the expected type. */ public static T checkType(Object object, Class expectedType) { if (!expectedType.isInstance(object)) { - throw new IllegalArgumentException("Expected id: " + expectedType.getName() + + throw new IllegalArgumentException("Expected type: " + expectedType.getName() + ", but got: " + (object == null ? "null" : object.getClass().getName())); } return expectedType.cast(object); @@ -48,7 +48,7 @@ public class TypeUtils { } yield bytes; } - default -> throw new IllegalStateException("Unsupported id: " + type.toLowerCase(Locale.ENGLISH)); + default -> throw new IllegalStateException("Unsupported type: " + type.toLowerCase(Locale.ENGLISH)); }; } From 5e79d18cf8cb1c5f7657470d4a2f90765b3cb989 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 16:27:31 +0800 Subject: [PATCH 26/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/pack/AbstractPackManager.java | 4 ++-- .../craftengine/core/plugin/config/blockbench/BBModel.java | 2 +- .../craftengine/core/util/BlockEntityTickersList.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 1c9f0bd85..1aa740527 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -90,7 +90,7 @@ public abstract class AbstractPackManager implements PackManager { public static final String NEW_TRIM_MATERIAL = "custom"; public static final Set ALLOWED_VANILLA_EQUIPMENT = Set.of("chainmail", "diamond", "gold", "iron", "netherite"); - public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elementConfigs", "gui_light", "overrides"); + public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elements", "gui_light", "overrides"); private static final byte[] EMPTY_1X1_IMAGE; private static final byte[] EMPTY_EQUIPMENT_IMAGE; @@ -2136,7 +2136,7 @@ public abstract class AbstractPackManager implements PackManager { .resolve("empty.png"); try { Files.createDirectories(modelPath.getParent()); - Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elementConfigs\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); + Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elements\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); } catch (IOException e) { this.plugin.logger().severe("Error writing empty block model", e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java index 942a704a4..8c4b0e176 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java @@ -11,7 +11,7 @@ public class BBModel { private String model_identifier; @SerializedName("visible_box") private int[] visible_box; - @SerializedName("elementConfigs") + @SerializedName("elements") private Element[] elements; @SerializedName("outliner") private OutLiner[] outliner; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java index 639bd31b2..4ff57411a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java @@ -54,7 +54,7 @@ public final class BlockEntityTickersList extends ObjectArrayList because we want to avoid autoboxing when using contains final int requiredMatches = c.size(); From fb4ae9357bc4a6d7ef68536d08559be3559d8921 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 17:19:16 +0800 Subject: [PATCH 27/46] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BA=A7=E6=A4=85?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=B8=8E=E4=B9=90=E9=AD=82=E7=9A=84=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/HappyGhastFurnitureHitbox.java | 44 ++++++++++--------- .../HappyGhastFurnitureHitboxConfig.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../InteractionFurnitureHitboxConfig.java | 6 +-- .../hitbox/ShulkerFurnitureHitbox.java | 2 +- .../hitbox/ShulkerFurnitureHitboxConfig.java | 25 ++++++----- .../plugin/network/BukkitNetworkManager.java | 3 ++ .../configuration/furniture/wooden_chair.yml | 1 + .../furniture/hitbox/FurnitureHitboxPart.java | 2 +- 9 files changed, 47 insertions(+), 40 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index 8905b7c2f..fcf530b95 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -11,12 +11,9 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; import java.util.ArrayList; import java.util.Collections; @@ -27,38 +24,37 @@ import java.util.function.Consumer; public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { private final HappyGhastFurnitureHitboxConfig config; private final Collider collider; - private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final Vec3d pos; + private final List packets; + private final int entityId; + private final float yaw; public HappyGhastFurnitureHitbox(Furniture furniture, HappyGhastFurnitureHitboxConfig config) { super(furniture, config); this.config = config; WorldPosition position = furniture.position(); - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); - Vector3f offset = conjugated.transform(new Vector3f(config.position())); - Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, 3 * config.scale(), 3 * config.scale()); - int happyGhastId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List packets = new ArrayList<>(3); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - happyGhastId, UUID.randomUUID(), position.x + offset.x, position.y + offset.y, position.z + offset.z, 0, position.yRot, - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(happyGhastId, config.cachedValues())); + this.pos = Furniture.getRelativePosition(position, config.position()); + double bbSize = 4 * config.scale(); + AABB aabb = AABB.fromInteraction(this.pos, bbSize, bbSize); + this.yaw = position.yRot; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this.packets = new ArrayList<>(3); + this.packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, config.cachedValues())); if (config.scale() != 1) { try { Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); - packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(happyGhastId, Collections.singletonList(attributeInstance))); + this.packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(this.entityId, Collections.singletonList(attributeInstance))); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); } } - this.collider = createCollider(furniture.world(), pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); - this.part = new FurnitureHitboxPart(happyGhastId, aabb, pos); - this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(happyGhastId); }}); + this.packets.add(FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, this.pos.x, this.pos.y, this.pos.z, 0, position.yRot, false)); + this.collider = createCollider(furniture.world(), this.pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); + this.part = new FurnitureHitboxPart(this.entityId, aabb, this.pos, false); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); } @Override @@ -73,7 +69,13 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { @Override public void show(Player player) { - player.sendPacket(this.spawnPacket, false); + List packets = new ArrayList<>(); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + this.entityId, UUID.randomUUID(), this.pos.x, player.y() - (this.config.scale() * 4 + 16), this.pos.z, 0, this.yaw, + MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.addAll(this.packets); + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets), false); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 7e28b68da..1d2cffb8a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -59,7 +59,7 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, 3 * this.scale, 3 * this.scale)); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, 4 * this.scale, 4 * this.scale)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 44380525c..c44037500 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -37,7 +37,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) )); - this.part = new FurnitureHitboxPart(interactionId, aabb, pos); + this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive()); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index a3e853d17..68be109f9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -33,14 +33,14 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon boolean canBeHitByProjectile, boolean invisible, Vector3f size, - boolean responsive) { + boolean interactive) { super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); this.size = size; - this.responsive = responsive; + this.responsive = interactive; this.invisible = invisible; InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedValues); if (invisible) { BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedValues); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index 9a6c39d43..e4ef37a2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -58,7 +58,7 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues()))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], config.cachedShulkerValues())); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); // fix some special occasions diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 004b73d26..4ca6e465a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -66,6 +66,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible List cachedInteractionValues = new ArrayList<>(); + InteractionEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedInteractionValues); float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; if (direction == Direction.UP) { InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); @@ -81,7 +82,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -101,7 +102,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -132,8 +133,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< if (canUseItemOn) { Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2, interactive)); } } }; @@ -159,31 +160,31 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } public float scale() { - return scale; + return this.scale; } public byte peek() { - return peek; + return this.peek; } public boolean interactive() { - return interactive; + return this.interactive; } public boolean interactionEntity() { - return interactionEntity; + return this.interactionEntity; } public Direction direction() { - return direction; + return this.direction; } public DirectionalShulkerSpawner spawner() { - return spawner; + return this.spawner; } public List cachedShulkerValues() { - return cachedShulkerValues; + return this.cachedShulkerValues; } @Override @@ -219,7 +220,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< AABB ceAABB = createAABB(direction, offset, x, y, z); Object level = world.serverWorld(); Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z))); + aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z), false)); return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 578925913..afa959aec 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -3826,6 +3826,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes for (Seat seat : hitBox.seats()) { if (!seat.isOccupied()) { if (seat.spawnSeat(serverPlayer, furniture.position())) { + if (!part.interactive()) { + serverPlayer.swingHand(InteractionHand.MAIN_HAND); + } break; } } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml index 9ea08846d..354d39baf 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -29,6 +29,7 @@ items: - position: 0,0,0 type: interaction blocks-building: true + invisible: true width: 0.7 height: 1.2 interactive: true diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java index e69a747f5..acf13acd3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java @@ -3,5 +3,5 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; -public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos) { +public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos, boolean interactive) { } From 462cea8d0a99ffab30c4ffe4e0a11600ee59304e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 17:27:17 +0800 Subject: [PATCH 28/46] Update BukkitServerPlayer.java --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index a67b52a51..d887a07d7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -546,7 +546,7 @@ public class BukkitServerPlayer extends Player { Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer); unsureEyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + bukkitPlayer.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos)); } - if (Config.predictBreaking() && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { + if (Config.predictBreaking() && this.eyeLocation != null && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { // if it's not destroying blocks, we do predict if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { this.predictNextBlockToMine(); From 1d00df89c51839ec27c1c34a82a7f648a6eb6928 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 21:25:54 +0800 Subject: [PATCH 29/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0custom=E7=A2=B0?= =?UTF-8?q?=E6=92=9E=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/CustomFurnitureHitbox.java | 83 +++++++++++++ .../hitbox/CustomFurnitureHitboxConfig.java | 114 ++++++++++++++++++ .../hitbox/HappyGhastFurnitureHitbox.java | 2 +- .../HappyGhastFurnitureHitboxConfig.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../InteractionFurnitureHitboxConfig.java | 2 +- .../hitbox/ShulkerFurnitureHitboxConfig.java | 8 +- .../reflection/minecraft/CoreReflections.java | 23 ++++ .../configuration/furniture/wooden_chair.yml | 9 +- .../core/world/collision/AABB.java | 2 +- 11 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index 10c1a33d2..715cb7678 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -10,7 +10,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); - // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); if (VersionHelper.isOrAbove1_21_6()) { register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java new file mode 100644 index 000000000..461f4ea07 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { + private final CustomFurnitureHitboxConfig config; + private final Collider collider; + private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; + + public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.makeBoundingBox(pos, config.width(), config.height()); + this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(3); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + config.entityType(), 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, config.cachedValues())); + if (VersionHelper.isOrAbove1_20_5()) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId, Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public CustomFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java new file mode 100644 index 000000000..d7147708c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -0,0 +1,114 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + private final float scale; + private final Object entityType; + private final List cachedValues = new ArrayList<>(); + private final float width; + private final float height; + + public CustomFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + float width, + float height, + boolean fixed, + float scale, + Object type) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.entityType = type; + this.width = fixed ? width : width * scale; + this.height = fixed ? height : height * scale; + BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues); + BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues); + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); + } + + public float scale() { + return this.scale; + } + + public Object entityType() { + return this.entityType; + } + + public List cachedValues() { + return this.cachedValues; + } + + public float width() { + return width; + } + + public float height() { + return height; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, this.width, this.height)); + } + } + + @Override + public CustomFurnitureHitbox create(Furniture furniture) { + return new CustomFurnitureHitbox(furniture, this); + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public CustomFurnitureHitboxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); + String type = (String) arguments.getOrDefault("entity-type", "slime"); + Object nmsEntityType = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ENTITY_TYPE, KeyUtils.toResourceLocation(Key.of(type))); + if (nmsEntityType == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); + } + float width; + float height; + boolean fixed; + try { + Object dimensions = CoreReflections.field$EntityType$dimensions.get(nmsEntityType); + width = CoreReflections.field$EntityDimensions$width.getFloat(dimensions); + height = CoreReflections.field$EntityDimensions$height.getFloat(dimensions); + fixed = CoreReflections.field$EntityDimensions$fixed.getBoolean(dimensions); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to get dimensions for " + nmsEntityType, e); + } + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new CustomFurnitureHitboxConfig(SeatConfig.fromObj(arguments.get("seats")), position, canUseItemOn, blocksBuilding, canBeHitByProjectile, width, height, fixed, scale, nmsEntityType); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index fcf530b95..750ee7c1c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -37,7 +37,7 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { WorldPosition position = furniture.position(); this.pos = Furniture.getRelativePosition(position, config.position()); double bbSize = 4 * config.scale(); - AABB aabb = AABB.fromInteraction(this.pos, bbSize, bbSize); + AABB aabb = AABB.makeBoundingBox(this.pos, bbSize, bbSize); this.yaw = position.yRot; this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.packets = new ArrayList<>(3); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 1d2cffb8a..6264b3a5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -59,7 +59,7 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, 4 * this.scale, 4 * this.scale)); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, 4 * this.scale, 4 * this.scale)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index c44037500..1c4e45ca6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -27,7 +27,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.config = config; WorldPosition position = furniture.position(); Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, config.size().x, config.size().y); + AABB aabb = AABB.makeBoundingBox(pos, config.size().x, config.size().y); this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 68be109f9..909335b0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -73,7 +73,7 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, size.x, size.y)); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, size.x, size.y)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 4ca6e465a..169acd8f2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -82,7 +82,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -102,7 +102,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -133,8 +133,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< if (canUseItemOn) { Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1, interactive)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); } } }; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index f92d2c2cd..146162837 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -4594,4 +4594,27 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to init EmptyBlockGetter$INSTANCE", e); } } + + public static final Class clazz$EntityDimensions = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.EntitySize", + "world.entity.EntityDimensions" + ) + ); + + public static final Field field$EntityType$dimensions = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityType, clazz$EntityDimensions, 0) + ); + + public static final Field field$EntityDimensions$width = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 0) + ); + + public static final Field field$EntityDimensions$height = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 1) + ); + + public static final Field field$EntityDimensions$fixed = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, boolean.class, 0) + ); } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml index 354d39baf..fddace996 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -27,12 +27,11 @@ items: translation: 0,0.5,0 hitboxes: - position: 0,0,0 - type: interaction - blocks-building: true + type: custom + entity-type: slime invisible: true - width: 0.7 - height: 1.2 - interactive: true + can-use-item-on: true + blocks-building: true seats: - 0,0,-0.1 0 loot: diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index c5a725f59..dc328fc4d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -57,7 +57,7 @@ public class AABB { return x * x + y * y + z * z; } - public static AABB fromInteraction(Vec3d pos, double width, double height) { + public static AABB makeBoundingBox(Vec3d pos, double width, double height) { return new AABB( pos.x - width / 2, pos.y, From 1c95eb75bdbbac2afc0bf71118ba7a09959d46a3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 22:25:24 +0800 Subject: [PATCH 30/46] =?UTF-8?q?=E9=99=90=E5=88=B6=E6=91=86=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../item/behavior/FurnitureItemBehavior.java | 107 ++++++++++++++---- .../default/configuration/furniture/bench.yml | 8 +- .../configuration/furniture/flower_basket.yml | 32 +++--- .../configuration/furniture/wooden_chair.yml | 9 +- .../core/entity/furniture/AnchorType.java | 15 ++- .../core/util/ResourceConfigUtils.java | 2 +- 8 files changed, 126 insertions(+), 51 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index 715cb7678..f7fa5e2e1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -10,7 +10,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); - register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); if (VersionHelper.isOrAbove1_21_6()) { register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 1c4e45ca6..65b0ffd9e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -32,7 +32,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + interactionId, UUID.randomUUID(), pos.x, pos.y, pos.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 19a9aea16..b74926aa9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -8,9 +8,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; -import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; -import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -26,10 +24,8 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Cancellable; -import net.momirealms.craftengine.core.util.ItemUtils; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; @@ -38,17 +34,17 @@ import org.bukkit.Location; import org.bukkit.World; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); + private static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); private final Key id; + private final Map rules; - public FurnitureItemBehavior(Key id) { + public FurnitureItemBehavior(Key id, Map rules) { this.id = id; + this.rules = rules; } public Key furnitureId() { @@ -67,12 +63,24 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } + Direction clickedFace = context.getClickedFace(); + AnchorType anchorType = switch (clickedFace) { + case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; + case UP -> AnchorType.GROUND; + case DOWN -> AnchorType.CEILING; + }; + FurnitureConfig customFurniture = optionalCustomFurniture.get(); - FurnitureVariant variant = customFurniture.anyVariant(); + FurnitureVariant variant = customFurniture.getVariant(anchorType.variantName()); if (variant == null) { return InteractionResult.FAIL; } + Rule rule = this.rules.get(anchorType); + if (rule == null) { + rule = Rule.DEFAULT; + } + Player player = context.getPlayer(); if (player != null && player.isAdventureMode()) { return InteractionResult.FAIL; @@ -80,14 +88,27 @@ public class FurnitureItemBehavior extends ItemBehavior { Vec3d clickedPosition = context.getClickLocation(); + // get position and rotation for placement + Vec3d finalPlacePosition; + double furnitureYaw; + if (anchorType == AnchorType.WALL) { + furnitureYaw = Direction.getYaw(clickedFace); + if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); + finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); + } else { + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); + finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); + } + } else { + furnitureYaw = rule.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); + finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); + } + // trigger event org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; World world = (World) context.getLevel().platformWorld(); - - // get position and rotation for placement - Vec3d finalPlacePosition = clickedPosition; - double furnitureYaw = 180 + (player != null ? player.yRot() : 0); - Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); WorldPosition furniturePos = LocationUtils.toWorldPosition(furnitureLocation); List aabbs = new ArrayList<>(); @@ -162,17 +183,59 @@ public class FurnitureItemBehavior extends ItemBehavior { if (id == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); } + Map rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules"); + Key furnitureId; if (id instanceof Map map) { + Map furnitureSection; if (map.containsKey(key.toString())) { // 防呆 - BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false))); + furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); } else { - BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false))); + furnitureSection = MiscUtils.castToMap(map, false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } + furnitureId = key; + // 兼容老版本 + if (rulesMap == null) { + Map placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement"); + if (placementSection != null) { + rulesMap = new HashMap<>(); + for (Map.Entry entry : placementSection.entrySet()) { + if (entry.getValue() instanceof Map innerMap) { + if (innerMap.containsKey("rules")) { + Map rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules"); + if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) { + rulesMap.put(entry.getKey(), rules); + } + } + } + } + } } - return new FurnitureItemBehavior(key); } else { - return new FurnitureItemBehavior(Key.of(id.toString())); + furnitureId = Key.of(id.toString()); } + Map rules = new EnumMap<>(AnchorType.class); + if (rulesMap != null) { + for (Map.Entry entry : rulesMap.entrySet()) { + try { + AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT)); + Map ruleSection = MiscUtils.castToMap(entry.getValue(), true); + rules.put(type, new Rule( + ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY), + ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY) + )); + } catch (IllegalArgumentException ignored) { + Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey()); + } + } + } + return new FurnitureItemBehavior(furnitureId, rules); } } + + public record Rule(AlignmentRule alignmentRule, RotationRule rotationRule) { + public static final Rule DEFAULT = new Rule(AlignmentRule.ANY, RotationRule.ANY); + } } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml index 06523d269..66e36f644 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -6,6 +6,10 @@ items: model: minecraft:item/custom/bench behavior: type: furniture_item + rules: + ground: + rotation: four + alignment: center furniture: settings: item: default:bench @@ -17,8 +21,8 @@ items: loot-spawn-offset: 0.5,0.5,0 elements: - item: default:bench - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0.5,0,0 translation: 0,0.5,0 shadow-radius: 1 diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml index 15194da75..acfcedb15 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml @@ -7,6 +7,16 @@ items: behavior: type: furniture_item furniture: default:flower_basket + rules: + ground: + rotation: any + alignment: any + wall: + rotation: any + alignment: any + ceiling: + rotation: any + alignment: any default:flower_basket_ground: material: nether_brick model: minecraft:item/custom/flower_basket_ground @@ -27,15 +37,12 @@ furniture: template: default:loot_table/furniture arguments: item: default:flower_basket - placement: + variants: ground: - rules: - rotation: ANY - alignment: ANY elements: - item: default:flower_basket_ground - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,0,0 translation: 0,0.5,0 shadow-radius: 0.5 @@ -50,12 +57,10 @@ furniture: height: 0.5 interactive: true wall: - rules: - alignment: ANY elements: - item: default:flower_basket_wall - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,0,0.2 translation: 0,0,0 hitboxes: @@ -76,13 +81,10 @@ furniture: height: 0.75 interactive: true ceiling: - rules: - rotation: ANY - alignment: ANY elements: - item: default:flower_basket_ceiling - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,-0.46,0 translation: 0,0,0 hitboxes: diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml index fddace996..7ad0017fe 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -6,18 +6,19 @@ items: model: minecraft:item/custom/wooden_chair behavior: type: furniture_item + rules: + ground: + rotation: any + alignment: any furniture: settings: item: default:wooden_chair sounds: break: minecraft:block.bamboo_wood.break place: minecraft:block.bamboo_wood.place - placement: + variants: ground: loot-spawn-offset: 0,0.4,0 - rules: - rotation: ANY - alignment: ANY elements: - item: default:wooden_chair display-transform: NONE diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index b389f52f7..8ec7b6749 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,21 +1,26 @@ package net.momirealms.craftengine.core.entity.furniture; -@Deprecated(since = "0.0.66") public enum AnchorType { - GROUND(0), - WALL(1), - CEILING(2); + GROUND(0, "ground"), + WALL(1, "wall"), + CEILING(2, "ceiling"); private final int id; + private final String variantName; - AnchorType(int id) { + AnchorType(int id, String variantName) { this.id = id; + this.variantName = variantName; } public int getId() { return id; } + public String variantName() { + return variantName; + } + public static AnchorType byId(int id) { return values()[id]; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index 43a35d7dc..e3be821e6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -36,7 +36,7 @@ public final class ResourceConfigUtils { return defaultValue; } try { - return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ENGLISH)); + return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { return defaultValue; } From 1d74e2764f363a53c8433e2a9b4605ea86f75ba6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 00:28:45 +0800 Subject: [PATCH 31/46] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=96=B0=E5=AE=B6?= =?UTF-8?q?=E5=85=B7API=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skript/condition/CondIsFurniture.java | 2 +- .../skript/effect/EffRemoveFurniture.java | 2 +- .../expression/ExprEntityFurnitureID.java | 2 +- .../bukkit/api/CraftEngineFurniture.java | 167 ++++++++++++++++-- .../plugin/command/feature/TestCommand.java | 8 + .../configuration/furniture/flower_basket.yml | 3 + .../furniture/FurnitureDataAccessor.java | 6 + 7 files changed, 169 insertions(+), 21 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java index 96b57c2d5..5be023307 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java @@ -25,7 +25,7 @@ public class CondIsFurniture extends Condition { @Override public boolean check(Event event) { return entities.check(event, entity -> { - BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity); + BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity); return baseEntity != null; }, isNegated()); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java index 995f3364a..b65a9bc1b 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java @@ -22,7 +22,7 @@ public class EffRemoveFurniture extends Effect { @Override protected void execute(Event e) { for (Entity entity : entities.getArray(e)) { - Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity); + Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity); if (bukkitFurniture != null) { bukkitFurniture.destroy(); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java index 0e8826602..fdf2674be 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java @@ -16,7 +16,7 @@ public class ExprEntityFurnitureID extends SimplePropertyExpression it.id().toString()) .orElse(null); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 7f2a1cabe..c8ab62b47 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -6,10 +6,12 @@ import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; @@ -19,11 +21,13 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.RayTraceResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,6 +63,45 @@ public final class CraftEngineFurniture { return BukkitFurnitureManager.instance().furnitureById(id).orElse(null); } + /** + * Performs ray tracing to find the furniture entity that the player is currently targeting + * + * @param player The player performing the ray trace + * @param maxDistance Maximum ray trace distance (in blocks) + * @return The furniture being targeted by the player, or null if no furniture is found + */ + @Nullable + public static BukkitFurniture rayTrace(Player player, double maxDistance) { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Location eyeLocation = serverPlayer.getEyeLocation(); + RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), maxDistance, FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity); + if (result == null) + return null; + Entity hitEntity = result.getHitEntity(); + if (hitEntity == null) + return null; + return getLoadedFurnitureByCollider(hitEntity); + } + + /** + * Performs ray tracing to find the furniture entity that the player is currently targeting + * + * @param player The player performing the ray trace + * @return The furniture being targeted by the player, or null if no furniture is found + */ + @Nullable + public static BukkitFurniture rayTrace(Player player) { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Location eyeLocation = serverPlayer.getEyeLocation(); + RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), serverPlayer.getCachedInteractionRange(), FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity); + if (result == null) + return null; + Entity hitEntity = result.getHitEntity(); + if (hitEntity == null) + return null; + return getLoadedFurnitureByCollider(hitEntity); + } + /** * Places furniture at certain location * @@ -70,9 +113,7 @@ public final class CraftEngineFurniture { public static BukkitFurniture place(Location location, Key furnitureId) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return place(location, furnitureId, furniture.getAnyAnchorType()); - return null; + return place(location, furniture, furniture.anyVariantName(), false); } /** @@ -86,11 +127,22 @@ public final class CraftEngineFurniture { @Nullable @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) { + return place(location, furnitureId, anchorType.variantName()); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furnitureId furniture to place + * @param variant variant type + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture place(Location location, Key furnitureId, String variant) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); - return null; + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), true); } /** @@ -104,9 +156,7 @@ public final class CraftEngineFurniture { @NotNull @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType) { - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); - return null; + return place(location, furniture, anchorType.variantName(), true); } /** @@ -123,9 +173,23 @@ public final class CraftEngineFurniture { public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); - return null; + return place(location, furniture, anchorType.variantName(), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furnitureId furniture to place + * @param variant variant + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture place(Location location, Key furnitureId, String variant, boolean playSound) { + FurnitureConfig furniture = byId(furnitureId); + if (furniture == null) return null; + return place(location, furniture, variant, playSound); } /** @@ -140,9 +204,49 @@ public final class CraftEngineFurniture { @NotNull @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType, boolean playSound) { - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); - return null; + return place(location, furniture, anchorType.variantName(), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param variant variant + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, String variant, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param data furniture data + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, CompoundTag data, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.of(data), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param dataAccessor furniture data accessor + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, dataAccessor, playSound); } /** @@ -178,18 +282,30 @@ public final class CraftEngineFurniture { } /** - * Gets the base furniture by the base entity + * Gets the furniture by the meta entity * * @param baseEntity base entity * @return the loaded furniture */ @Nullable - public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { + public static BukkitFurniture getLoadedFurnitureByMetaEntity(@NotNull Entity baseEntity) { return BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(baseEntity.getEntityId()); } /** - * Gets the base furniture by the seat entity + * Gets the furniture by the meta entity + * + * @param baseEntity base entity + * @return the loaded furniture + */ + @Nullable + @Deprecated(since = "0.0.66") + public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { + return getLoadedFurnitureByMetaEntity(baseEntity); + } + + /** + * Gets the furniture by the seat entity * * @param seat seat entity * @return the loaded furniture @@ -204,6 +320,21 @@ public final class CraftEngineFurniture { return null; } + /** + * Gets the furniture by the collider entity + * + * @param collider collider entity + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture getLoadedFurnitureByCollider(@NotNull Entity collider) { + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(collider); + if (nmsEntity instanceof CollisionEntity collisionEntity) { + return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getId()); + } + return null; + } + /** * Removes furniture * diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index 437855e70..cc9bdb782 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,9 +1,12 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.incendo.cloud.Command; public class TestCommand extends BukkitCommandFeature { @@ -15,9 +18,14 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder + .senderType(Player.class) .handler(context -> { // DO NOT PUSH ANY CODE FOR TEST COMMAND // 禁止推送含有实现的Test指令 + BukkitFurniture furniture = CraftEngineFurniture.rayTrace(context.sender(), 4); + if (furniture != null) { + System.out.println(furniture.id()); + } }); } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml index acfcedb15..416b43aad 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml @@ -20,12 +20,15 @@ items: default:flower_basket_ground: material: nether_brick model: minecraft:item/custom/flower_basket_ground + item-name: default:flower_basket_wall: material: nether_brick model: minecraft:item/custom/flower_basket_wall + item-name: default:flower_basket_ceiling: material: nether_brick model: minecraft:item/custom/flower_basket_ceiling + item-name: furniture: default:flower_basket: settings: diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index c6bc0793a..fb4785432 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -31,6 +31,12 @@ public class FurnitureDataAccessor { return new FurnitureDataAccessor(data); } + public static FurnitureDataAccessor ofVariant(String variant) { + FurnitureDataAccessor accessor = new FurnitureDataAccessor(new CompoundTag()); + accessor.setVariant(variant); + return accessor; + } + public CompoundTag copyTag() { return this.data.copy(); } From 7d2496b06d663ddc435259ed4d4e263c727c985a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:12:48 +0800 Subject: [PATCH 32/46] Update DebugSpawnFurnitureCommand.java --- .../feature/DebugSpawnFurnitureCommand.java | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index 7b32aeba1..832a1c40e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -1,10 +1,16 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.util.Key; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; @@ -12,10 +18,12 @@ import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; import org.incendo.cloud.bukkit.parser.location.LocationParser; import org.incendo.cloud.context.CommandContext; import org.incendo.cloud.context.CommandInput; -import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; +import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { @@ -24,7 +32,6 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder @@ -35,22 +42,30 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + NamespacedKey namespacedKey = context.get("id"); + Key id = KeyUtils.namespacedKey2Key(namespacedKey); + BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + return optionalCustomFurniture.>>map(config -> CompletableFuture.completedFuture(config.variants().keySet().stream().map(Suggestion::suggestion).toList())).orElseGet(() -> CompletableFuture.completedFuture(List.of())); + } + })) .flag(FlagKeys.SILENT_FLAG) .handler(context -> { - // fixme 指令 -// NamespacedKey namespacedKey = context.get("id"); -// Key id = KeyUtils.namespacedKey2Key(namespacedKey); -// BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); -// Optional optionalCustomFurniture = furnitureManager.furnitureById(id); -// if (optionalCustomFurniture.isEmpty()) { -// return; -// } -// Location location = context.get("location"); -// FurnitureConfig customFurniture = optionalCustomFurniture.get(); -// AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType()); -// boolean playSound = context.flags().hasFlag("silent"); -// CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); + NamespacedKey namespacedKey = context.get("id"); + Key id = KeyUtils.namespacedKey2Key(namespacedKey); + BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + if (optionalCustomFurniture.isEmpty()) { + return; + } + Location location = context.get("location"); + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + String variant = (String) context.optional("variant").orElse(customFurniture.anyVariantName()); + boolean playSound = context.flags().hasFlag("silent"); + CraftEngineFurniture.place(location, customFurniture, variant, playSound); }); } From 1610ec97362b4f594c23950855e588953797be11 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:13:57 +0800 Subject: [PATCH 33/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dreplacement=E7=A9=BA?= =?UTF-8?q?=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/listener/ItemEventListener.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index 88f5173de..4d7e6e374 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -457,9 +457,11 @@ public class ItemEventListener implements Listener { } } else { // fixme 如何取消堆叠数量>1的物品的默认replacement - Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); - if (replacementItem != null) { - PlayerUtils.giveItem(serverPlayer, 1, replacementItem); + if (replacement != null) { + Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); + if (replacementItem != null) { + PlayerUtils.giveItem(serverPlayer, 1, replacementItem); + } } } } From a1569423443de371e6a872baae437d873769f0f7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:49:27 +0800 Subject: [PATCH 34/46] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=AE=B6=E5=85=B7=E6=9C=80=E5=A4=A7AABB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/CustomFurnitureHitboxConfig.java | 5 ++++ .../HappyGhastFurnitureHitboxConfig.java | 5 ++++ .../InteractionFurnitureHitboxConfig.java | 5 ++++ .../hitbox/ShulkerFurnitureHitboxConfig.java | 5 ++++ .../plugin/command/feature/TestCommand.java | 8 ------- .../furniture/AbstractFurnitureManager.java | 24 +++++++++++++++---- .../hitbox/FurnitureHitBoxConfig.java | 2 ++ .../function/ReplaceFurnitureFunction.java | 15 +++++------- .../function/SpawnFurnitureFunction.java | 15 +++++------- .../core/world/collision/AABB.java | 12 ++++++++++ 10 files changed, 66 insertions(+), 30 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java index d7147708c..58e2ed92f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -78,6 +78,11 @@ public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, this.width, this.height)); + } + @Override public CustomFurnitureHitbox create(Furniture furniture) { return new CustomFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 6264b3a5f..df7458e19 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -63,6 +63,11 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, 4 * this.scale, 4 * this.scale)); + } + public static class Factory implements FurnitureHitBoxConfigFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 909335b0a..3f823705a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -77,6 +77,11 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, size.x, size.y)); + } + @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 169acd8f2..ebc750c2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -159,6 +159,11 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(this.aabbCreator.create(position.x, position.y, position.z, 180, new Vector3f(0))); + } + public float scale() { return this.scale; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index cc9bdb782..437855e70 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,12 +1,9 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.incendo.cloud.Command; public class TestCommand extends BukkitCommandFeature { @@ -18,14 +15,9 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .senderType(Player.class) .handler(context -> { // DO NOT PUSH ANY CODE FOR TEST COMMAND // 禁止推送含有实现的Test指令 - BukkitFurniture furniture = CraftEngineFurniture.rayTrace(context.sender(), 4); - if (furniture != null) { - System.out.println(furniture.id()); - } }); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index c0569fd87..e056dfc73 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; @@ -141,9 +142,25 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } - // fixme 动态计算aabb,因为家具具有朝向 - AABB maxAABB = new AABB(0,0,0,1,1,1); - + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBox : hitboxes) { + hitBox.collectBoundingBox(aabbs::add); + } + double minX = 0; + double minY = 0; + double minZ = 0; + double maxX = 0; + double maxY = 0; + double maxZ = 0; + for (AABB aabb : aabbs) { + minX = Math.min(minX, aabb.minX); + minY = Math.min(minY, aabb.minY); + minZ = Math.min(minZ, aabb.minZ); + maxX = Math.max(maxX, aabb.maxX); + maxY = Math.max(maxY, aabb.maxY); + maxZ = Math.max(maxZ, aabb.maxZ); + } + AABB maxAABB = new AABB(minX, minY, minZ, maxX, maxY, maxZ); variants.put(variantName, new FurnitureVariant( variantName, parseCullingData(section.get("entity-culling"), maxAABB), @@ -160,7 +177,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index 689cb130c..f2361c098 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -23,4 +23,6 @@ public interface FurnitureHitBoxConfig { boolean canUseItemOn(); void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); + + void collectBoundingBox(Consumer aabbConsumer); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 61eec5c6c..2c4577909 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.plugin.context.function; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; @@ -15,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -@SuppressWarnings("deprecation") public class ReplaceFurnitureFunction extends AbstractConditionalFunction { private final Key newFurnitureId; private final NumberProvider x; @@ -23,7 +21,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi private final NumberProvider z; private final NumberProvider pitch; private final NumberProvider yaw; - private final AnchorType anchorType; + private final String variant; private final boolean dropLoot; private final boolean playSound; @@ -34,7 +32,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z, NumberProvider pitch, NumberProvider yaw, - AnchorType anchorType, + String variant, boolean dropLoot, boolean playSound, List> predicates @@ -46,7 +44,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi this.z = z; this.pitch = pitch; this.yaw = yaw; - this.anchorType = anchorType; + this.variant = variant; this.dropLoot = dropLoot; this.playSound = playSound; } @@ -72,8 +70,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi RemoveFurnitureFunction.removeFurniture(ctx, oldFurniture, dropLoot, playSound); // Place the new furniture - // fixme function -// SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); + SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.variant, this.playSound); } } @@ -96,10 +93,10 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); + return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, dropLoot, playSound, getPredicates(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java index d29b7758e..e297cf2f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.plugin.context.function; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; @@ -55,18 +57,13 @@ public class SpawnFurnitureFunction extends AbstractConditi float pitchValue = this.pitch.getFloat(ctx); float yawValue = this.yaw.getFloat(ctx); WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue); - // fixme api -// spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); + spawnFurniture(this.furnitureId, position, this.variant, this.playSound); }); } -// public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { -// CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { -// AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); -// FurnitureDataAccessor extraData = FurnitureDataAccessor.builder().anchorType(anchor).build(); -// CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); -// }); -// } + public static void spawnFurniture(Key furnitureId, WorldPosition position, String variant, boolean playSound) { + CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> CraftEngine.instance().furnitureManager().place(position, furniture, FurnitureDataAccessor.ofVariant(variant), playSound)); + } @Override public Key type() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index dc328fc4d..94c6f52e2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; import java.util.Optional; @@ -68,6 +69,17 @@ public class AABB { ); } + public static AABB makeBoundingBox(Vector3f pos, double width, double height) { + return new AABB( + pos.x - width / 2, + pos.y, + pos.z - width / 2, + pos.x + width / 2, + pos.y + height, + pos.z + width / 2 + ); + } + public Optional clip(Vec3d min, Vec3d max) { double[] traceDistance = {1.0}; double deltaX = max.x - min.x; From 14b0c711472eb75e1e84567d007af30b860d7bb6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 04:22:34 +0800 Subject: [PATCH 35/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84id=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 2 +- .../entity/furniture/BukkitCollider.java | 2 +- .../entity/furniture/BukkitFurniture.java | 4 +- .../furniture/BukkitFurnitureManager.java | 43 ++++++++++++++----- .../hitbox/CustomFurnitureHitbox.java | 7 +++ .../hitbox/HappyGhastFurnitureHitbox.java | 5 +++ .../hitbox/InteractionFurnitureHitbox.java | 8 ++++ .../hitbox/ShulkerFurnitureHitbox.java | 15 ++++--- .../hitbox/ShulkerFurnitureHitboxConfig.java | 22 ++++------ .../plugin/network/BukkitNetworkManager.java | 10 +++-- common-files/src/main/resources/config.yml | 2 +- .../configuration/furniture/wooden_chair.yml | 27 ++++++++---- .../core/entity/furniture/Furniture.java | 23 +++++++++- .../furniture/hitbox/FurnitureHitBox.java | 3 ++ gradle.properties | 2 +- 15 files changed, 128 insertions(+), 47 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index c8ab62b47..096f42b67 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -330,7 +330,7 @@ public final class CraftEngineFurniture { public static BukkitFurniture getLoadedFurnitureByCollider(@NotNull Entity collider) { Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(collider); if (nmsEntity instanceof CollisionEntity collisionEntity) { - return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getId()); + return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getEntityId()); } return null; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java index 69b8c9775..7b330e60a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java @@ -21,7 +21,7 @@ public class BukkitCollider implements Collider { @Override public int entityId() { - return this.collisionEntity.getId(); + return this.collisionEntity.getEntityId(); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 4455884dc..d7bbe6f9d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -31,10 +31,12 @@ public class BukkitFurniture extends Furniture { public void addCollidersToWorld() { Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); for (Collider entity : super.colliders) { - FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); bukkitEntity.setPersistent(false); + if (!bukkitEntity.isValid()) { + FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 21693ed18..0451bcf32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -1,15 +1,19 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionFurnitureHitboxConfig; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -29,6 +33,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); @@ -73,7 +78,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } catch (IOException e) { this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); } - handleMetaEntityAfterChunkLoad(display); + handleMetaEntityDuringChunkLoad(display); }); if (playSound) { SoundData sound = furniture.settings().sounds().placeSound(); @@ -228,6 +233,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { createFurnitureInstance(entity, customFurniture); } + @SuppressWarnings("deprecation") protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { // 实体可能不是持久的 if (!entity.isPersistent()) { @@ -239,7 +245,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (id == null) return; // 这个区块还处于加载实体中,这个时候不处理 - if (!isEntitiesLoaded(entity.getLocation())) { + Location location = entity.getLocation(); + if (!isEntitiesLoaded(location)) { return; } @@ -254,13 +261,16 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (previous != null) return; createFurnitureInstance(entity, customFurniture); + // 补发一次包 + for (Player player : entity.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ), false); + } } protected void handleCollisionEntityAfterChunkLoad(Entity entity) { - // 实体可能不是持久的 - if (!entity.isPersistent()) { - return; - } // 如果是碰撞实体,那么就忽略 if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { return; @@ -271,11 +281,13 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return; } // 实体未加载 - if (!isEntitiesLoaded(entity.getLocation())) { + Location location = entity.getLocation(); + if (!isEntitiesLoaded(location)) { return; } + // 移除被WorldEdit错误复制的碰撞实体 - entity.remove(); + runSafeEntityOperation(location, entity::remove); } public void handleCollisionEntityDuringChunkLoad(Entity collisionEntity) { @@ -306,7 +318,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 创建家具实例,并初始化碰撞实体 - private void createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { + private BukkitFurniture createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); for (int entityId : bukkitFurniture.virtualEntityIds()) { @@ -315,7 +327,18 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { for (Collider collisionEntity : bukkitFurniture.colliders()) { this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); } - bukkitFurniture.addCollidersToWorld(); + Location location = display.getLocation(); + runSafeEntityOperation(location, bukkitFurniture::addCollidersToWorld); + return bukkitFurniture; + } + + private void runSafeEntityOperation(Location location, Runnable action) { + boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (preventChange) { + this.plugin.scheduler().sync().runLater(action, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + } else { + action.run(); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java index 461f4ea07..900909794 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -27,6 +27,7 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final int entityId; public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) { super(furniture, config); @@ -54,6 +55,7 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.entityId = entityId; } @Override @@ -76,6 +78,11 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { player.sendPacket(this.despawnPacket, false); } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public CustomFurnitureHitboxConfig config() { return this.config; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index 750ee7c1c..e5306701a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -83,6 +83,11 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { player.sendPacket(this.despawnPacket, false); } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public HappyGhastFurnitureHitboxConfig config() { return this.config; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 65b0ffd9e..4b4a34110 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.List; import java.util.UUID; +import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; @@ -21,6 +22,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final int entityId; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); @@ -39,6 +41,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { )); this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive()); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); + this.entityId = interactionId; } @Override @@ -56,6 +59,11 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { return this.config; } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public void show(Player player) { player.sendPacket(this.spawnPacket, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index e4ef37a2a..db6c0b865 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -17,10 +17,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; @@ -30,11 +27,12 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { private final List colliders; private final Object spawnPacket; private final Object despawnPacket; + private final int[] entityIds; public ShulkerFurnitureHitbox(Furniture furniture, ShulkerFurnitureHitboxConfig config) { super(furniture, config); this.config = config; - int[] entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); + this.entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); WorldPosition position = furniture.position(); Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); Vector3f offset = conjugated.transform(new Vector3f(config.position())); @@ -99,6 +97,13 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { return this.parts; } + @Override + public void collectVirtualEntityId(Consumer collector) { + for (int entityId : entityIds) { + collector.accept(entityId); + } + } + @Override public void show(Player player) { player.sendPacket(this.spawnPacket, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index ebc750c2a..c418fd36b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -80,10 +80,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); - } + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); @@ -100,10 +98,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); - } + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); @@ -130,12 +126,10 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); - } + Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index afa959aec..e6c351aaa 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -3954,14 +3954,16 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(id); if (furniture != null) { - serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); + EntityPacketHandler previous = serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); if (Config.enableEntityCulling()) { serverPlayer.addTrackedFurniture(id, furniture); } else { - furniture.show(serverPlayer); + // 修复addEntityToWorld,包比事件先发的问题 (WE) + if (previous == null || previous instanceof ItemDisplayPacketHandler) { + furniture.show(serverPlayer); + } } - // fixme 外部模型 - if (Config.hideBaseEntity() && true) { + if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { event.setCancelled(true); } } else { diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 95815b4c5..5e33a6b41 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -558,7 +558,7 @@ chunk-system: client-optimization: # Requires a restart to fully apply. entity-culling: - enable: true + enable: false # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. ray-tracing: true # Cull entities based on distance diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml index 7ad0017fe..a399a75d3 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -27,14 +27,25 @@ items: billboard: FIXED translation: 0,0.5,0 hitboxes: - - position: 0,0,0 - type: custom - entity-type: slime - invisible: true - can-use-item-on: true - blocks-building: true - seats: - - 0,0,-0.1 0 + $$>=1.20.5: + - position: 0,0,0 + type: custom + entity-type: slime + invisible: true + can-use-item-on: true + blocks-building: true + seats: + - 0,0,-0.1 0 + $$fallback: + - position: 0,0,0 + type: interaction + blocks-building: true + invisible: true + width: 0.7 + height: 1.2 + interactive: true + seats: + - 0,0,-0.1 0 loot: template: default:loot_table/furniture arguments: diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index a69ccd26f..5099472cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; @@ -13,6 +14,7 @@ import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxCo import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; @@ -25,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.Optional; import java.util.UUID; public abstract class Furniture implements Cullable { @@ -41,6 +44,8 @@ public abstract class Furniture implements Cullable { protected int[] virtualEntityIds; protected int[] colliderEntityIds; + private boolean hasExternalModel; + protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; @@ -77,8 +82,8 @@ public abstract class Furniture implements Cullable { this.hitboxes[i] = hitbox; for (FurnitureHitboxPart part : hitbox.parts()) { this.hitboxMap.put(part.entityId(), hitbox); - virtualEntityIds.add(part.entityId()); } + hitbox.collectVirtualEntityId(virtualEntityIds::addLast); colliders.addAll(hitbox.colliders()); } // 虚拟碰撞箱的实体id @@ -86,6 +91,18 @@ public abstract class Furniture implements Cullable { this.colliders = colliders.toArray(new Collider[0]); this.colliderEntityIds = colliders.stream().mapToInt(Collider::entityId).toArray(); this.cullingData = createCullingData(variant.cullingData()); + // 外部模型 + Optional externalModel = variant.externalModel(); + if (externalModel.isPresent()) { + this.hasExternalModel = true; + try { + externalModel.get().bindModel((AbstractEntity) this.metaDataEntity); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e); + } + } else { + this.hasExternalModel = false; + } } private CullingData createCullingData(CullingData parent) { @@ -181,6 +198,10 @@ public abstract class Furniture implements Cullable { return this.metaDataEntity.entityID(); } + public boolean hasExternalModel() { + return hasExternalModel; + } + public Vec3d getRelativePosition(Vector3f position) { return getRelativePosition(this.position(), position); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 0a249acd4..95f7b652a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.world.Vec3d; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { @@ -24,6 +25,8 @@ public interface FurnitureHitBox extends SeatOwner { FurnitureHitBoxConfig config(); + void collectVirtualEntityId(Consumer collector); + default Optional clip(Vec3d min, Vec3d max) { for (FurnitureHitboxPart value : parts()) { Optional clip = value.aabb().clip(min, max); diff --git a/gradle.properties b/gradle.properties index 46db05f12..15594bad8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.138 +nms_helper_version=1.0.140 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From 26862498dc9df36c1b825cab2025c1f82bcc36cd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 04:50:09 +0800 Subject: [PATCH 36/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E7=9A=84=E6=91=84=E5=83=8F=E6=9C=BA=E4=BD=8D=E7=BD=AE=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index d887a07d7..98fd209ee 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -602,6 +602,9 @@ public class BukkitServerPlayer extends Player { @Override public void entityCullingTick() { this.culling.restoreTokenOnTick(); + if (this.firstPersonCameraVec3 == null || this.thirdPersonCameraVec3 == null) { + return; + } boolean useRayTracing = Config.entityCullingRayTracing(); if (this.enableEntityCulling) { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { From 6cf114b4f0f8dc50fdabdd6853fcee9fcfa7424d Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 05:45:14 +0800 Subject: [PATCH 37/46] Update BukkitFurnitureManager.java --- .../entity/furniture/BukkitFurnitureManager.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 0451bcf32..ff12e6dcc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -224,6 +224,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { Optional optionalFurniture = furnitureById(key); if (optionalFurniture.isEmpty()) return; + // 只对1.20.2及以上生效,1.20.1比较特殊 + if (!VersionHelper.isOrAbove1_20_2()) { + return; + } + // 已经在其他事件里加载过了 FurnitureConfig customFurniture = optionalFurniture.get(); BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); @@ -244,9 +249,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); if (id == null) return; - // 这个区块还处于加载实体中,这个时候不处理 + // 这个区块还处于加载实体中,这个时候不处理(1.20.1需要特殊处理) Location location = entity.getLocation(); - if (!isEntitiesLoaded(location)) { + if (VersionHelper.isOrAbove1_20_2() && !isEntitiesLoaded(location)) { return; } @@ -261,11 +266,12 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (previous != null) return; createFurnitureInstance(entity, customFurniture); - // 补发一次包 + + // 补发一次包,修复 for (Player player : entity.getTrackedPlayers()) { BukkitAdaptors.adapt(player).sendPacket(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), false); } } From fa6f837852b57c33547ccbdebb4d1785cef834bd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 05:57:38 +0800 Subject: [PATCH 38/46] Update gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e816ca6c7..c1b0ab52a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.15.1 +project_version=0.0.65.16 config_version=60 lang_version=41 project_group=net.momirealms From cac852eb8102ec3d6307fcfc4ba4326013ef65a8 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 17:32:51 +0800 Subject: [PATCH 39/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=91=94=E8=90=BD?= =?UTF-8?q?=E9=9F=B3=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/compatibility/build.gradle.kts | 6 +-- .../bukkit/block/BlockEventListener.java | 37 +++++++++++++++++-- .../craftengine/bukkit/util/EntityUtils.java | 10 ++--- .../plugin/context/event/EventTrigger.java | 3 +- gradle.properties | 2 +- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 949b79fe4..13d93e5cd 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -41,8 +41,8 @@ dependencies { compileOnly("io.github.toxicity188:bettermodel:1.14.0") compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") // MMOItems - compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") - compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") + compileOnly("net.Indyuce:MMOItems-API:6.10.1-SNAPSHOT") + compileOnly("io.lumine:MythicLib-dist:1.7.1-SNAPSHOT") // Nexo compileOnly("com.nexomc:nexo:1.13.0") // LuckPerms @@ -62,7 +62,7 @@ dependencies { // McMMO compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.2.038") // MMOCore - compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT") + compileOnly("net.Indyuce:MMOCore-API:1.13.1-SNAPSHOT") // JobsReborn compileOnly("com.github.Zrips:Jobs:v5.2.2.3") // CustomFishing diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 8da86d668..49ffc0057 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.ItemUtils; @@ -38,6 +39,7 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.world.GenericGameEvent; import org.bukkit.inventory.ItemStack; @@ -233,12 +235,14 @@ public final class BlockEventListener implements Listener { @EventHandler(priority = EventPriority.LOW) public void onStep(GenericGameEvent event) { - if (event.getEvent() != GameEvent.STEP) return; + GameEvent gameEvent = event.getEvent(); + // 只处理落地和走路 + if (gameEvent != GameEvent.STEP) return; Entity entity = event.getEntity(); if (!(entity instanceof Player player)) return; BlockPos pos = EntityUtils.getOnPos(player); Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z()); - Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ())); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos)); Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); if (optionalCustomState.isPresent()) { Location location = player.getLocation(); @@ -253,7 +257,8 @@ public final class BlockEventListener implements Listener { if (cancellable.isCancelled() && !Config.processCancelledStep()) { return; } - player.playSound(location, state.settings().sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.settings().sounds().stepSound().volume().get(), state.settings().sounds().stepSound().pitch().get()); + SoundData soundData = state.settings().sounds().stepSound(); + player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get()); } else if (Config.enableSoundSystem()) { if (event.isCancelled() && !Config.processCancelledStep()) { return; @@ -267,6 +272,32 @@ public final class BlockEventListener implements Listener { } } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onFall(EntityDamageEvent event) { + if (event.getCause() != EntityDamageEvent.DamageCause.FALL) + return; + if (!(event.getEntity() instanceof Player player)) return; + BlockPos pos = EntityUtils.getOnPos(player); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos)); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomState.isPresent()) { + Location location = player.getLocation(); + ImmutableBlockState state = optionalCustomState.get(); + SoundData soundData = state.settings().sounds().fallSound(); + player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get()); + } else if (Config.enableSoundSystem()) { + if (event.isCancelled() && !Config.processCancelledStep()) { + return; + } + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$fallSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (this.manager.isStepSoundMissing(soundId)) { + player.playSound(player.getLocation(), soundId.toString(), SoundCategory.BLOCKS, 0.15f, 1f); + } + } + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onBlockPhysics(BlockPhysicsEvent event) { // for vanilla blocks diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index f9cc52975..81396c16e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -26,13 +26,9 @@ public final class EntityUtils { } public static BlockPos getOnPos(Player player) { - try { - Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); - Object blockPos = CoreReflections.method$Entity$getOnPos.invoke(serverPlayer, 1.0E-5F); - return LocationUtils.fromBlockPos(blockPos); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); + Object blockPos = FastNMS.INSTANCE.method$Entity$getOnPos(serverPlayer); + return LocationUtils.fromBlockPos(blockPos); } public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer function) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java index 2a29e7fd9..0faee9842 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java @@ -11,7 +11,8 @@ public enum EventTrigger { BREAK("break", "dig"), PLACE("place", "build"), PICK_UP("pick_up", "pick"), - STEP("step"),; + STEP("step"), + FALL("fall"),; public static final Map BY_NAME = new HashMap<>(); private final String[] names; diff --git a/gradle.properties b/gradle.properties index c1b0ab52a..5e2c657b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.140 +nms_helper_version=1.0.141 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From cba5de8f9860caafff512c92a71050aae6d5611e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 19:05:02 +0800 Subject: [PATCH 40/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BukkitCompatibilityManager.java | 7 ++-- .../advancement/BukkitAdvancementManager.java | 5 +++ .../furniture/BukkitFurnitureManager.java | 3 -- .../hitbox/ShulkerFurnitureHitbox.java | 5 ++- .../bukkit/loot/BukkitVanillaLootManager.java | 12 ++++++ .../bukkit/pack/BukkitPackManager.java | 4 +- .../src/main/resources/translations/en.yml | 14 +++++++ .../src/main/resources/translations/zh_cn.yml | 14 +++++++ .../core/block/AbstractBlockManager.java | 17 ++++++++ .../furniture/AbstractFurnitureManager.java | 6 ++- .../core/font/AbstractFontManager.java | 12 +++++- .../core/item/AbstractItemManager.java | 10 +++++ .../item/recipe/AbstractRecipeManager.java | 5 +++ .../recipe/CustomSmithingTransformRecipe.java | 5 ++- .../core/pack/AbstractPackManager.java | 39 +++++++++++-------- .../core/pack/host/impl/AlistHost.java | 3 +- .../core/pack/host/impl/DropboxHost.java | 3 +- .../core/pack/host/impl/GitLabHost.java | 3 +- .../core/pack/host/impl/LobFileHost.java | 3 +- .../core/pack/host/impl/OneDriveHost.java | 3 +- .../pack/host/impl/SelfHostHttpServer.java | 3 +- .../core/plugin/config/ConfigParser.java | 8 ++++ .../config/template/TemplateManagerImpl.java | 5 +++ .../plugin/context/GlobalVariableManager.java | 5 +++ .../gui/category/ItemBrowserManagerImpl.java | 5 +++ .../plugin/locale/TranslationManager.java | 16 ++++++-- .../plugin/locale/TranslationManagerImpl.java | 24 ++++++++++++ .../core/sound/AbstractSoundManager.java | 10 +++++ gradle.properties | 2 +- 29 files changed, 213 insertions(+), 38 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 325159bdc..28d614af0 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -37,6 +37,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition; import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -181,7 +182,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager { } private void logHook(String plugin) { - this.plugin.logger().info("[Compatibility] " + plugin + " hooked"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.compatibility", plugin)); } @Override @@ -252,8 +253,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager { if (VersionHelper.isOrAbove1_20_3()) { this.plugin.logger().severe(""); if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) { - this.plugin.logger().severe("[Compatibility] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")"); - this.plugin.logger().severe("[Compatibility] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本"); + this.plugin.logger().severe("[兼容性] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")"); + this.plugin.logger().severe("[兼容性] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本"); } else { this.plugin.logger().severe("[Compatibility] Update FastAsyncWorldEdit to v2.13.0+ for better compatibility (Current: " + version + ")"); this.plugin.logger().severe("[Compatibility] Download latest version: https://ci.athion.net/job/FastAsyncWorldEdit/"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java index 9a5f8acc3..5d98d2130 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java @@ -119,6 +119,11 @@ public final class BukkitAdvancementManager extends AbstractAdvancementManager { return LoadingSequence.ADVANCEMENT; } + @Override + public int count() { + return 0; + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (advancements.containsKey(id)) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index ff12e6dcc..5b0f5cacc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -7,13 +7,11 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -33,7 +31,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; -import java.util.function.Consumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index db6c0b865..ac88d288d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -17,7 +17,10 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Supplier; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index d38e58a24..7836a0812 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -93,6 +93,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme public class VanillaLootParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"}; + private int count; @Override public int loadingSequence() { @@ -104,6 +105,16 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type"); @@ -147,6 +158,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme } } } + this.count++; } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 4b7aa72cc..fc961ac8d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -14,6 +14,8 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl; import net.momirealms.craftengine.core.util.Base64Utils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; @@ -94,7 +96,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { return; } if (!Config.sendPackOnUpload()) return; - CraftEngine.instance().logger().info("Completed uploading resource pack"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.resource_pack.upload")); for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) { sendResourcePack(player); } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 2377b162b..9e9be8c07 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -38,6 +38,20 @@ argument.parse.failure.aggregate.missing: "Missing component ''Invalid component '': " argument.parse.failure.either: "Could not resolve or from ''" argument.parse.failure.namedtextcolor: "'' is not a named text color" +info.pack.load: "Loaded pack: . Default namespace: " +info.resource.load: "Loaded in ms ()" +info.resource_pack.start: "Generating resource pack..." +info.resource_pack.generate: "Generated resource pack in ms" +info.resource_pack.validate: "Validated resource pack in ms" +info.resource_pack.optimize: "Optimized resource pack in ms" +info.resource_pack.optimize.json: "> Optimizing json files..." +info.resource_pack.optimize.texture: "> Optimizing textures..." +info.resource_pack.optimize.result: "□ Before/After/Ratio: KB/ KB/%" +info.resource_pack.create: "Created resource pack zip in ms" +info.resource_pack.upload: "Completed uploading resource pack" +info.host.self.netty_server: "Netty HTTP server started on port: " +info.host.cache.load: "[] Loaded cached resource pack metadata" +info.compatibility: "[Compatibility] hooked" command.reload.config.success: "Configs reloaded in ms. (Async: ms | Sync: ms)" command.reload.config.failure: "Config reload failed. Check console logs." command.reload.pack.success: "Resource pack reloaded in ms." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 06d51777c..54850c5f5 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -38,6 +38,20 @@ argument.parse.failure.aggregate.missing: "缺少组件 ''" argument.parse.failure.aggregate.failure: "无效的组件 '': " argument.parse.failure.either: "无法从 '' 解析 " argument.parse.failure.namedtextcolor: "'' 不是颜色代码" +info.pack.load: "已加载包: . 默认命名空间: " +info.resource.load: "加载 耗时 ms ()" +info.resource_pack.start: "正在开始生成资源包..." +info.resource_pack.generate: "生成资源包耗时 ms" +info.resource_pack.validate: "验证资源包耗时 ms" +info.resource_pack.optimize: "优化资源包耗时 ms" +info.resource_pack.optimize.json: "> 正在优化json文件..." +info.resource_pack.optimize.texture: "> 正在优化贴图文件..." +info.resource_pack.optimize.result: "□ 优化前/优化后/比例: KB/ KB/%" +info.resource_pack.create: "创建资源包文件耗时 ms" +info.resource_pack.upload: "资源包上传完成" +info.host.self.netty_server: "Netty HTTP 服务已在端口 开启" +info.host.cache.load: "[] 已加载缓存的资源包元数据" +info.compatibility: "[兼容性] 已挂钩 " command.reload.config.success: "重新加载配置完成. 耗时 毫秒 (异步: ms | 同步: ms)" command.reload.config.failure: "重新加载配置失败, 请检查控制台日志" command.reload.pack.success: "资源包重新加载完成. 耗时 毫秒" diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index 5707d7b2f..9f620cd82 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -261,6 +261,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem public class BlockStateMappingParser extends SectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[]{"block-state-mappings", "block-state-mapping"}; + private int count; @Override public String[] sectionId() { @@ -272,6 +273,16 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return LoadingSequence.BLOCK_STATE_MAPPING; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, Map section) throws LocalizedException { ExceptionCollector exceptionCollector = new ExceptionCollector<>(); @@ -302,6 +313,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem List blockStateWrappers = AbstractBlockManager.this.blockStateArranger.computeIfAbsent(blockOwnerId, k -> new ArrayList<>()); blockStateWrappers.add(beforeState); AbstractBlockManager.this.autoVisualBlockStateCandidates[beforeState.registryId()] = createVisualBlockCandidate(beforeState); + this.count++; } exceptionCollector.throwIfPresent(); } @@ -331,6 +343,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem this.visualBlockStateAllocator = new VisualBlockStateAllocator(AbstractBlockManager.this.plugin.dataFolderPath().resolve("cache").resolve("visual-block-states.json"), candidates, AbstractBlockManager.this::createVanillaBlockState); } + @Override + public int count() { + return AbstractBlockManager.this.byId.size(); + } + public void addPendingConfigSection(PendingConfigSection section) { this.pendingConfigSections.add(section); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index e056dfc73..98a5fb890 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; -import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; @@ -109,6 +108,11 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return LoadingSequence.FURNITURE; } + @Override + public int count() { + return AbstractFurnitureManager.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractFurnitureManager.this.byId.containsKey(id)) { 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 df9e4fdff..cfa52da0b 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 @@ -442,9 +442,14 @@ public abstract class AbstractFontManager implements FontManager { return LoadingSequence.EMOJI; } + @Override + public int count() { + return AbstractFontManager.this.emojis.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { - if (emojis.containsKey(id)) { + if (AbstractFontManager.this.emojis.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.emoji.duplicate"); } String permission = (String) section.get("permission"); @@ -510,6 +515,11 @@ public abstract class AbstractFontManager implements FontManager { return LoadingSequence.IMAGE; } + @Override + public int count() { + return AbstractFontManager.this.images.size(); + } + @Override public void postProcess() { for (Map.Entry entry : this.idAllocators.entrySet()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index d6defc9b4..3104b4e53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -351,6 +351,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl .toList(); registerArmorTrimPattern(trims); } + + @Override + public int count() { + return AbstractItemManager.this.equipments.size(); + } } public void addOrMergeEquipment(ComponentBasedEquipment equipment) { @@ -368,6 +373,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; private final Map idAllocators = new HashMap<>(); + @Override + public int count() { + return AbstractItemManager.this.customItemsById.size(); + } + private boolean isModernFormatRequired() { return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 93d1ea157..657906f95 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -134,6 +134,11 @@ public abstract class AbstractRecipeManager implements RecipeManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return Math.max(0, AbstractRecipeManager.this.byId.size() - AbstractRecipeManager.this.dataPackRecipes.size()); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (!Config.enableRecipeSystem()) return; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 87730aa0e..965772071 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -19,7 +19,10 @@ import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 1aa740527..ab9ffd4e5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -31,10 +31,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.SectionConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.locale.LangData; -import net.momirealms.craftengine.core.plugin.locale.LocalizedException; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.plugin.locale.*; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; @@ -407,7 +404,7 @@ public abstract class AbstractPackManager implements PackManager { } Pack pack = new Pack(path, new PackMeta(author, description, version, namespace), enable); this.loadedPacks.put(path.getFileName().toString(), pack); - this.plugin.logger().info("Loaded pack: " + pack.folder().getFileName() + ". Default namespace: " + namespace); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.pack.load", pack.folder().getFileName().toString(), namespace)); } } } catch (IOException e) { @@ -695,7 +692,12 @@ public abstract class AbstractPackManager implements PackManager { parser.loadAll(); parser.postProcess(); long t2 = System.nanoTime(); - this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms"); + int count = parser.count(); + if (parser.silentIfNotExists() && count == 0) { + continue; + } + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource.load", + parser.sectionId()[0], String.format("%.2f", ((t2 - t1) / 1_000_000.0)), String.valueOf(count))); } } @@ -720,7 +722,7 @@ public abstract class AbstractPackManager implements PackManager { @Override public void generateResourcePack() throws IOException { - this.plugin.logger().info("Generating resource pack..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.start")); long time1 = System.currentTimeMillis(); // Create cache data @@ -768,17 +770,17 @@ public abstract class AbstractPackManager implements PackManager { this.removeAllShaders(generatedPackPath); } long time2 = System.currentTimeMillis(); - this.plugin.logger().info("Generated resource pack in " + (time2 - time1) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate", String.valueOf(time2 - time1))); if (Config.validateResourcePack()) { this.validateResourcePack(generatedPackPath); } long time3 = System.currentTimeMillis(); - this.plugin.logger().info("Validated resource pack in " + (time3 - time2) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate", String.valueOf(time3 - time2))); if (Config.optimizeResourcePack()) { this.optimizeResourcePack(generatedPackPath); } long time4 = System.currentTimeMillis(); - this.plugin.logger().info("Optimized resource pack in " + (time4 - time3) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize", String.valueOf(time4 - time3))); Path finalPath = resourcePackPath(); Files.createDirectories(finalPath.getParent()); try { @@ -787,7 +789,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error zipping resource pack", e); } long time5 = System.currentTimeMillis(); - this.plugin.logger().info("Created resource pack zip file in " + (time5 - time4) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.create", String.valueOf(time5 - time4))); this.generationEventDispatcher.accept(generatedPackPath, finalPath); } } @@ -1042,7 +1044,7 @@ public abstract class AbstractPackManager implements PackManager { } if (Config.optimizeJson()) { - this.plugin.logger().info("> Optimizing json files..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.json")); AtomicLong previousBytes = new AtomicLong(0L); AtomicLong afterBytes = new AtomicLong(0L); List> futures = new ArrayList<>(); @@ -1109,11 +1111,11 @@ public abstract class AbstractPackManager implements PackManager { long originalSize = previousBytes.get(); long optimizedSize = afterBytes.get(); double compressionRatio = ((double) optimizedSize / originalSize) * 100; - this.plugin.logger().info("□ Before/After/Ratio: " + formatSize(originalSize) + "/" + formatSize(optimizedSize) + "/" + String.format("%.2f%%", compressionRatio)); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.result", formatSize(originalSize), formatSize(optimizedSize), String.format("%.2f%%", compressionRatio))); } if (Config.optimizeTexture()) { - this.plugin.logger().info("> Optimizing textures..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.texture")); AtomicLong previousBytes = new AtomicLong(0L); AtomicLong afterBytes = new AtomicLong(0L); List> futures = new ArrayList<>(); @@ -1155,7 +1157,7 @@ public abstract class AbstractPackManager implements PackManager { long originalSize = previousBytes.get(); long optimizedSize = afterBytes.get(); double compressionRatio = ((double) optimizedSize / originalSize) * 100; - this.plugin.logger().info("□ Before/After/Ratio: " + formatSize(originalSize) + "/" + formatSize(optimizedSize) + "/" + String.format("%.2f%%", compressionRatio)); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.result", formatSize(originalSize), formatSize(optimizedSize), String.format("%.2f%%", compressionRatio))); } } @@ -1170,7 +1172,7 @@ public abstract class AbstractPackManager implements PackManager { " ".repeat(Math.max(0, emptyLength)) + "]"; return String.format( - "%s %d/%d (%.1f%%) | Time: %ss", + "%s %d/%d (%.1f%%) | %ss", progressBar, current, total, @@ -2854,6 +2856,11 @@ public abstract class AbstractPackManager implements PackManager { this.excludeJson.clear(); } + @Override + public int count() { + return this.excludeJson.size() + this.excludeTexture.size(); + } + public Set excludeTexture() { return excludeTexture; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java index 674fbf908..8b36730a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.Nullable; @@ -85,7 +86,7 @@ public class AlistHost implements ResourcePackHost { new TypeToken>(){}.getType() ); this.cachedSha1 = cache.get("sha1"); - CraftEngine.instance().logger().info("[Alist] Loaded cached resource pack metadata"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "Alist")); } catch (Exception e) { CraftEngine.instance().logger().warn("[Alist] Failed to load cache " + cachePath, e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java index beab2350a..98b65751b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.IOException; @@ -58,7 +59,7 @@ public class DropboxHost implements ResourcePackHost { this.refreshToken = getString(cache, "refresh_token"); this.accessToken = getString(cache, "access_token"); this.expiresAt = getLong(cache, "expires_at"); - CraftEngine.instance().logger().info("[Dropbox] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "Dropbox")); } catch (Exception e) { CraftEngine.instance().logger().warn("[Dropbox] Failed to load cache " + cachePath, e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java index b94adf1f1..d2e4fd58b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.IOException; @@ -58,7 +59,7 @@ public class GitLabHost implements ResourcePackHost { if (uuidString != null && !uuidString.isEmpty()) { this.uuid = UUID.fromString(uuidString); } - CraftEngine.instance().logger().info("[GitLab] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "GitLab")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[GitLab] Failed to read cache file: " + cachePath, e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java index 22a0dfc37..89db7002d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -70,7 +71,7 @@ public class LobFileHost implements ResourcePackHost { if (uuidString != null && !uuidString.isEmpty()) { this.uuid = UUID.fromString(uuidString); } - CraftEngine.instance().logger().info("[LobFile] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "LobFile")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[LobFile] Failed to read cache file: " + e.getMessage()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java index a15fa632f..3fb95aa92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.FileNotFoundException; @@ -76,7 +77,7 @@ public class OneDriveHost implements ResourcePackHost { this.sha1 = cache.get("sha1"); this.fileId = cache.get("file-id"); - CraftEngine.instance().logger().info("[OneDrive] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "OneDrive")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[OneDrive] Failed to load cache" + cachePath, e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 39961f3aa..29563b49c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -21,6 +21,7 @@ import io.netty.util.CharsetUtil; import io.netty.util.concurrent.GlobalEventExecutor; import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; @@ -166,7 +167,7 @@ public class SelfHostHttpServer { }); try { serverChannel = b.bind(port).sync().channel(); - CraftEngine.instance().logger().info("Netty HTTP server started on port: " + port); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.self.netty_server", String.valueOf(port))); } catch (InterruptedException e) { CraftEngine.instance().logger().warn("Failed to start Netty server", e); Thread.currentThread().interrupt(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java index 5fc06c91e..915fb7cc7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java @@ -25,4 +25,12 @@ public interface ConfigParser extends Comparable { void loadAll(); void clear(); + + default int count() { + return -1; + } + + default boolean silentIfNotExists() { + return true; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 3b0502db5..cfe45b6ca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -51,6 +51,11 @@ public class TemplateManagerImpl implements TemplateManager { return LoadingSequence.TEMPLATE; } + @Override + public int count() { + return TemplateManagerImpl.this.templates.size(); + } + @Override public void parseObject(Pack pack, Path path, String node, Key id, Object obj) { if (TemplateManagerImpl.this.templates.containsKey(id)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java index 078a8c600..42af0daa5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java @@ -49,6 +49,11 @@ public class GlobalVariableManager implements Manageable { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return GlobalVariableManager.this.globalVariables.size(); + } + @Override public void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException { if (object != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index b4d6d30f4..27baae5e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -109,6 +109,11 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { return LoadingSequence.CATEGORY; } + @Override + public int count() { + return ItemBrowserManagerImpl.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String name = section.getOrDefault("name", id).toString(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index 89fa4edea..8f9909250 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -4,13 +4,12 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.Translator; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag; +import net.momirealms.craftengine.core.util.AdventureHelper; import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public interface TranslationManager extends Manageable { @@ -74,6 +73,15 @@ public interface TranslationManager extends Manageable { } } + default String translateLog(String id, String... arguments) { + String translation = miniMessageTranslation(id); + if (translation == null) { + return id; + } + Component deserialize = AdventureHelper.customMiniMessage().deserialize(translation, new IndexedArgumentTag(Arrays.stream(arguments).map(Component::text).toList())); + return AdventureHelper.plainTextContent(deserialize); + } + Set translationKeys(); void log(String id, String... args); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index 44cd01d8c..e193d837b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -277,6 +277,7 @@ public class TranslationManagerImpl implements TranslationManager { public class TranslationParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"translations", "translation", "l10n", "localization", "i18n", "internationalization"}; + private int count; @Override public int loadingSequence() { @@ -288,6 +289,16 @@ public class TranslationManagerImpl implements TranslationManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { Locale locale = TranslationManager.parseLocale(id.value()); @@ -300,6 +311,7 @@ public class TranslationManagerImpl implements TranslationManager { String key = entry.getKey(); bundle.put(key, entry.getValue().toString()); TranslationManagerImpl.this.translationKeys.add(key); + this.count++; } TranslationManagerImpl.this.registry.registerAll(locale, bundle); @@ -313,6 +325,7 @@ public class TranslationManagerImpl implements TranslationManager { Component deserialize = AdventureHelper.miniMessage().deserialize(AdventureHelper.legacyToMiniMessage(s), ShiftTag.INSTANCE, ImageTag.INSTANCE); return AdventureHelper.getLegacy().serialize(deserialize); }; + private int count; @Override public int loadingSequence() { @@ -324,6 +337,16 @@ public class TranslationManagerImpl implements TranslationManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { String langId = id.value().toLowerCase(Locale.ENGLISH); @@ -333,6 +356,7 @@ public class TranslationManagerImpl implements TranslationManager { entry -> this.langProcessor.apply(String.valueOf(entry.getValue())) )); TranslationManagerImpl.this.addClientTranslation(langId, sectionData); + this.count += sectionData.size(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index 0972a6eed..1a31d0140 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -97,6 +97,11 @@ public abstract class AbstractSoundManager implements SoundManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return AbstractSoundManager.this.songs.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.songs.containsKey(id)) { @@ -124,6 +129,11 @@ public abstract class AbstractSoundManager implements SoundManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return AbstractSoundManager.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.byId.containsKey(id)) { diff --git a/gradle.properties b/gradle.properties index 5e2c657b8..255046cbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings project_version=0.0.65.16 config_version=60 -lang_version=41 +lang_version=43 project_group=net.momirealms latest_supported_version=1.21.10 From 64a510d89d440de2df926b260bda5541118986ee Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 19:18:25 +0800 Subject: [PATCH 41/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=89=E8=90=BD?= =?UTF-8?q?=E7=BB=8F=E9=AA=8C=E6=96=B9=E5=9D=97=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java | 2 ++ .../bukkit/block/behavior/DropExperienceBlockBehavior.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index 090512f4c..aae33ecc1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -48,6 +48,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); + public static final Key DROP_EXP_BLOCK = Key.from("craftengine:drop_exp_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -94,5 +95,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); + register(DROP_EXP_BLOCK, DropExperienceBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java index ef629c558..78de11707 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java @@ -90,7 +90,7 @@ public class DropExperienceBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 0)); + NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.get(arguments, "amount", "count")); Condition conditions = null; List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap); if (conditionList.size() == 1) { From be84b0292a835b96ab6c604c94ae36347053bdd7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 20:48:13 +0800 Subject: [PATCH 42/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E8=BD=BD?= =?UTF-8?q?=E5=90=8E=E5=AE=B9=E5=99=A8=E9=97=A8=E5=85=B3=E4=B8=8D=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/entity/SimpleStorageBlockEntity.java | 2 +- .../bukkit/plugin/user/BukkitServerPlayer.java | 17 ++++++++++++++++- .../craftengine/core/entity/player/Player.java | 4 ++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java index 5649bccc4..706ebf20d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java @@ -172,7 +172,7 @@ public class SimpleStorageBlockEntity extends BlockEntity { public void updateOpenBlockState(boolean open) { ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos); - if (state == null || state.behavior() != this.behavior) return; + if (state == null) return; SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null); if (behavior == null) return; Property property = behavior.openProperty(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 98fd209ee..adcf7bcc5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -76,6 +76,7 @@ public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); public static final Key ENABLE_ENTITY_CULLING = Key.of("craftengine:enable_entity_culling"); + public static final Key ENABLE_FURNITURE_DEBUG = Key.of("craftengine:enable_furniture_debug"); private final BukkitCraftEngine plugin; // connection state @@ -153,6 +154,8 @@ public class BukkitServerPlayer extends Player { private boolean enableEntityCulling; // 玩家眼睛所在位置 private Location eyeLocation; + // 是否启用家具调试 + private boolean enableFurnitureDebug; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -180,6 +183,7 @@ public class BukkitServerPlayer extends Player { String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); this.enableEntityCulling = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN)).orElse(true); + this.enableFurnitureDebug = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_FURNITURE_DEBUG), PersistentDataType.BOOLEAN)).orElse(false); this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); this.selectedLocale = TranslationManager.parseLocale(locale); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(512, 0.5f); @@ -1374,7 +1378,18 @@ public class BukkitServerPlayer extends Player { @Override public boolean enableEntityCulling() { - return enableEntityCulling; + return this.enableEntityCulling; + } + + @Override + public void setEnableFurnitureDebug(boolean enable) { + this.enableFurnitureDebug = enable; + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENABLE_FURNITURE_DEBUG), PersistentDataType.BOOLEAN, enable); + } + + @Override + public boolean enableFurnitureDebug() { + return enableFurnitureDebug; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 2f89bf90b..19e240b33 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -198,6 +198,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract boolean enableEntityCulling(); + public abstract boolean enableFurnitureDebug(); + + public abstract void setEnableFurnitureDebug(boolean enableFurnitureDebug); + public abstract void giveExperiencePoints(int xpPoints); public abstract void giveExperienceLevels(int levels); From af592e4287fd1c995c39393215c8eb08fca9be11 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 22:16:12 +0800 Subject: [PATCH 43/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=89=94=E9=99=A4?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/behavior/FurnitureItemBehavior.java | 4 + .../SetEntityViewDistanceScaleCommand.java | 4 +- .../feature/ToggleEntityCullingCommand.java | 2 +- .../plugin/network/BukkitNetworkManager.java | 1 + .../plugin/user/BukkitServerPlayer.java | 8 ++ .../furniture/AbstractFurnitureManager.java | 31 ++------ .../core/entity/furniture/Furniture.java | 62 +++++++++++++++- .../core/entity/player/Player.java | 2 + .../plugin/entityculling/EntityCulling.java | 56 ++++++++------ .../core/world/collision/AABB.java | 74 +++++++++++++++++++ 10 files changed, 191 insertions(+), 53 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index b74926aa9..83f516cff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; +import org.bukkit.Particle; import org.bukkit.World; import java.nio.file.Path; @@ -119,6 +120,9 @@ public class FurnitureItemBehavior extends ItemBehavior { // 检查方块、实体阻挡 if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { +// bukkitPlayer.getWorld().spawnParticle(Particle.FLAME, , List.of(bukkitPlayer), ); + } return InteractionResult.FAIL; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index df4afd16e..d8a9d0d9a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -30,11 +30,11 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } if (Config.entityCullingViewDistance() <= 0) { - context.sender().sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java index 29971f70a..1d8786ec9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -32,7 +32,7 @@ public class ToggleEntityCullingCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 7728e1641..19c66e96d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.network; +import com.destroystokyo.paper.ParticleBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.JsonElement; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index adcf7bcc5..1ddab9cd6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -1480,6 +1480,14 @@ public class BukkitServerPlayer extends Player { return LocationUtils.toWorldPosition(this.getEyeLocation()); } + @Override + public void playParticle(Key particleId, double x, double y, double z) { + Particle particle = Registry.PARTICLE_TYPE.get(KeyUtils.toNamespacedKey(particleId)); + if (particle != null) { + platformPlayer().getWorld().spawnParticle(particle, List.of(platformPlayer()), null, x, y, z, 1, 0, 0,0, 0, null, false); + } + } + public Location getEyeLocation() { org.bukkit.entity.Player player = platformPlayer(); Location eyeLocation = player.getEyeLocation(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 98a5fb890..2e3a308a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -146,28 +146,9 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } - List aabbs = new ArrayList<>(); - for (FurnitureHitBoxConfig hitBox : hitboxes) { - hitBox.collectBoundingBox(aabbs::add); - } - double minX = 0; - double minY = 0; - double minZ = 0; - double maxX = 0; - double maxY = 0; - double maxZ = 0; - for (AABB aabb : aabbs) { - minX = Math.min(minX, aabb.minX); - minY = Math.min(minY, aabb.minY); - minZ = Math.min(minZ, aabb.minZ); - maxX = Math.max(maxX, aabb.maxX); - maxY = Math.max(maxY, aabb.maxY); - maxZ = Math.max(maxZ, aabb.maxZ); - } - AABB maxAABB = new AABB(minX, minY, minZ, maxX, maxY, maxZ); variants.put(variantName, new FurnitureVariant( variantName, - parseCullingData(section.get("entity-culling"), maxAABB), + parseCullingData(section.get("entity-culling")), elements.toArray(new FurnitureElementConfig[0]), hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, @@ -185,16 +166,18 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { AbstractFurnitureManager.this.byId.put(id, furniture); } - private CullingData parseCullingData(Object arguments, AABB maxHitbox) { + + + private CullingData parseCullingData(Object arguments) { if (arguments instanceof Boolean b && !b) return null; if (!(arguments instanceof Map)) - return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true); + return new CullingData(null, Config.entityCullingViewDistance(), 0.25, true); Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( - ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"), + ResourceConfigUtils.getOrDefault(argumentsMap.get("aabb"), it -> ResourceConfigUtils.getAsAABB(it, "aabb"), null), ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), - ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.25), "aabb-expansion"), ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 5099472cf..9210c649e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -23,10 +23,13 @@ import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -109,9 +112,62 @@ public abstract class Furniture implements Cullable { if (parent == null) return null; AABB aabb = parent.aabb; WorldPosition position = position(); - Vec3d pos1 = getRelativePosition(position, new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ)); - Vec3d pos2 = getRelativePosition(position, new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ)); - return new CullingData(new AABB(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + if (aabb == null) { + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBoxConfig : this.currentVariant.hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(position, aabbs::add); + } + return new CullingData(getMaxAABB(aabbs), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } else { + Vector3f[] vertices = new Vector3f[] { + // 底面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.minY, (float) aabb.maxZ), + // 顶面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.maxY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ) + }; + double minX = Double.MAX_VALUE, minY = aabb.minY; // Y方向不变 + double maxX = -Double.MAX_VALUE, maxY = aabb.maxY; // Y方向不变 + double minZ = Double.MAX_VALUE, maxZ = -Double.MAX_VALUE; + for (Vector3f vertex : vertices) { + Vec3d rotatedPos = getRelativePosition(position, vertex); + minX = Math.min(minX, rotatedPos.x); + minZ = Math.min(minZ, rotatedPos.z); + maxX = Math.max(maxX, rotatedPos.x); + maxZ = Math.max(maxZ, rotatedPos.z); + } + return new CullingData(new AABB(minX, minY, minZ, maxX, maxY, maxZ), + parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } + } + + private static @NotNull AABB getMaxAABB(List aabbs) { + double minX = 0; + double minY = 0; + double minZ = 0; + double maxX = 0; + double maxY = 0; + double maxZ = 0; + for (int i = 0; i < aabbs.size(); i++) { + AABB aabb = aabbs.get(i); + if (i == 0) { + minX = aabb.minX; + minY = aabb.minY; + minZ = aabb.minZ; + maxX = aabb.maxX; + maxY = aabb.maxY; + maxZ = aabb.maxZ; + } else { + minX = Math.min(minX, aabb.minX); + minY = Math.min(minY, aabb.minY); + minZ = Math.min(minZ, aabb.minZ); + maxX = Math.max(maxX, aabb.maxX); + maxY = Math.max(maxY, aabb.maxY); + maxZ = Math.max(maxZ, aabb.maxZ); + } + } + return new AABB(minX, minY, minZ, maxX, maxY, maxZ); } @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 19e240b33..6fd240dbb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -230,6 +230,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public void remove() { } + public abstract void playParticle(Key particleId, double x, double y, double z); + public abstract void removeTrackedFurniture(int entityId); public abstract void clearTrackedFurniture(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 5ced80c59..df5d26b97 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -59,13 +60,12 @@ public final class EntityCulling { AABB aabb = cullable.aabb; double aabbExpansion = cullable.aabbExpansion; - // 根据AABB获取能包裹此AABB的最小长方体 - int minX = MiscUtils.floor(aabb.minX - aabbExpansion); - int minY = MiscUtils.floor(aabb.minY - aabbExpansion); - int minZ = MiscUtils.floor(aabb.minZ - aabbExpansion); - int maxX = MiscUtils.ceil(aabb.maxX + aabbExpansion); - int maxY = MiscUtils.ceil(aabb.maxY + aabbExpansion); - int maxZ = MiscUtils.ceil(aabb.maxZ + aabbExpansion); + double minX = aabb.minX - aabbExpansion; + double minY = aabb.minY - aabbExpansion; + double minZ = aabb.minZ - aabbExpansion; + double maxX = aabb.maxX + aabbExpansion; + double maxY = aabb.maxY + aabbExpansion; + double maxZ = aabb.maxZ + aabbExpansion; double cameraX = cameraPos.x; double cameraY = cameraPos.y; @@ -95,6 +95,10 @@ public final class EntityCulling { if (distanceSq > maxDistanceSq) { return false; } + // 太近了,不剔除 + else if (distanceSq < 1) { + return true; + } } if (!rayTracing || !cullable.rayTracing) { @@ -120,25 +124,31 @@ public final class EntityCulling { } int size = 0; - if (this.dotSelectors[0]) targetPoints[size++].set(minX + 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[1]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[2]) targetPoints[size++].set(minX + 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[3]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[4]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[5]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[6]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, maxZ - 0.05); - if (this.dotSelectors[7]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, maxZ - 0.05); + if (this.dotSelectors[0]) targetPoints[size++].set(minX, minY, minZ); + if (this.dotSelectors[1]) targetPoints[size++].set(maxX, minY, minZ); + if (this.dotSelectors[2]) targetPoints[size++].set(minX, minY, maxZ); + if (this.dotSelectors[3]) targetPoints[size++].set(maxX, minY, maxZ); + if (this.dotSelectors[4]) targetPoints[size++].set(minX, maxY, minZ); + if (this.dotSelectors[5]) targetPoints[size++].set(maxX, maxY, minZ); + if (this.dotSelectors[6]) targetPoints[size++].set(minX, maxY, maxZ); + if (this.dotSelectors[7]) targetPoints[size++].set(maxX, maxY, maxZ); // 面中心点 double averageX = (minX + maxX) / 2.0; double averageY = (minY + maxY) / 2.0; double averageZ = (minZ + maxZ) / 2.0; - if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ + 0.05); - if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ - 0.05); - if (this.dotSelectors[10]) targetPoints[size++].set(minX + 0.05, averageY, averageZ); - if (this.dotSelectors[11]) targetPoints[size++].set(maxX - 0.05, averageY, averageZ); - if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY + 0.05, averageZ); - if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY - 0.05, averageZ); + if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ); + if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ); + if (this.dotSelectors[10]) targetPoints[size++].set(minX, averageY, averageZ); + if (this.dotSelectors[11]) targetPoints[size++].set(maxX, averageY, averageZ); + if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY, averageZ); + if (this.dotSelectors[13]) targetPoints[size++].set(averageX, maxY, averageZ); +// if (Config.debugEntityCulling()) { +// for (int i = 0; i < size; i++) { +// MutableVec3d targetPoint = this.targetPoints[i]; +// this.player.playParticle(Key.of("flame"), targetPoint.x, targetPoint.y, targetPoint.z); +// } +// } return isVisible(cameraPos, this.targetPoints, size); } @@ -356,7 +366,7 @@ public final class EntityCulling { return deltaX + 32 * deltaY + 32 * 32 * deltaZ; } - private double distanceSq(int min, int max, double camera, Relative rel) { + private double distanceSq(double min, double max, double camera, Relative rel) { if (rel == Relative.NEGATIVE) { double dx = camera - max; return dx * dx; @@ -394,7 +404,7 @@ public final class EntityCulling { private enum Relative { INSIDE, POSITIVE, NEGATIVE; - public static Relative from(int min, int max, double pos) { + public static Relative from(double min, double max, double pos) { if (min > pos) return POSITIVE; else if (max < pos) return NEGATIVE; return INSIDE; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index 94c6f52e2..1f759dada 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -7,6 +7,8 @@ import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; public class AABB { @@ -159,6 +161,78 @@ public class AABB { return (value >= min - EPSILON) && (value <= max + EPSILON); } + public List getEdgePoints(double interval) { + List points = new ArrayList<>(); + + // AABB的8个顶点 + Vec3d[] vertices = { + new Vec3d(minX, minY, minZ), // 0 + new Vec3d(maxX, minY, minZ), // 1 + new Vec3d(minX, maxY, minZ), // 2 + new Vec3d(maxX, maxY, minZ), // 3 + new Vec3d(minX, minY, maxZ), // 4 + new Vec3d(maxX, minY, maxZ), // 5 + new Vec3d(minX, maxY, maxZ), // 6 + new Vec3d(maxX, maxY, maxZ) // 7 + }; + + // 12条边的定义(连接哪两个顶点) + int[][] edges = { + {0, 1}, // 底部X边(前) + {1, 3}, // 底部Y边(右) + {3, 2}, // 底部X边(后) + {2, 0}, // 底部Y边(左) + + {4, 5}, // 顶部X边(前) + {5, 7}, // 顶部Y边(右) + {7, 6}, // 顶部X边(后) + {6, 4}, // 顶部Y边(左) + + {0, 4}, // Z边(左下前) + {1, 5}, // Z边(右下前) + {2, 6}, // Z边(左后上) + {3, 7} // Z边(右后上) + }; + + for (int[] edge : edges) { + Vec3d start = vertices[edge[0]]; + Vec3d end = vertices[edge[1]]; + points.addAll(sampleLine(start, end, interval)); + } + + return points; + } + + private List sampleLine(Vec3d start, Vec3d end, double interval) { + List points = new ArrayList<>(); + + // 计算线段长度 + double dx = end.x - start.x; + double dy = end.y - start.y; + double dz = end.z - start.z; + double length = Math.sqrt(dx * dx + dy * dy + dz * dz); + + // 计算采样点数(去掉终点避免重复) + int numPoints = (int) Math.floor(length / interval); + + // 如果线段太短,至少返回起点 + if (numPoints <= 0) { + points.add(start); + return points; + } + + // 按间隔采样 + for (int i = 0; i <= numPoints; i++) { + double t = (double) i / numPoints; + double x = start.x + dx * t; + double y = start.y + dy * t; + double z = start.z + dz * t; + points.add(new Vec3d(x, y, z)); + } + + return points; + } + @Override public String toString() { return "AABB{" + From 568d1c2b7b96f3f8ab1546e5f58fa71afd486822 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 22:56:00 +0800 Subject: [PATCH 44/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=B6=E5=85=B7debug?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/CustomFurnitureHitboxConfig.java | 5 --- .../HappyGhastFurnitureHitboxConfig.java | 5 --- .../InteractionFurnitureHitboxConfig.java | 5 --- .../hitbox/ShulkerFurnitureHitboxConfig.java | 5 --- .../item/behavior/FurnitureItemBehavior.java | 10 ++++-- .../bukkit/pack/BukkitPackManager.java | 1 - .../plugin/command/BukkitCommandManager.java | 1 + .../feature/DebugFurnitureCommand.java | 36 +++++++++++++++++++ .../plugin/network/BukkitNetworkManager.java | 1 - .../plugin/user/BukkitServerPlayer.java | 35 ++++++++++++++++++ common-files/src/main/resources/commands.yml | 7 ++++ .../furniture/AbstractFurnitureManager.java | 3 -- .../hitbox/FurnitureHitBoxConfig.java | 1 - .../core/pack/AbstractPackManager.java | 5 ++- .../plugin/entityculling/EntityCulling.java | 5 --- 15 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java index 58e2ed92f..d7147708c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -78,11 +78,6 @@ public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, this.width, this.height)); - } - @Override public CustomFurnitureHitbox create(Furniture furniture) { return new CustomFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index df7458e19..6264b3a5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -63,11 +63,6 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, 4 * this.scale, 4 * this.scale)); - } - public static class Factory implements FurnitureHitBoxConfigFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 3f823705a..909335b0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -77,11 +77,6 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, size.x, size.y)); - } - @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index c418fd36b..2b309b806 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -153,11 +153,6 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(this.aabbCreator.create(position.x, position.y, position.z, 180, new Vector3f(0))); - } - public float scale() { return this.scale; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 83f516cff..3c3d34027 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -31,7 +31,6 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; -import org.bukkit.Particle; import org.bukkit.World; import java.nio.file.Path; @@ -121,7 +120,14 @@ public class FurnitureItemBehavior extends ItemBehavior { if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { -// bukkitPlayer.getWorld().spawnParticle(Particle.FLAME, , List.of(bukkitPlayer), ); + player.playSound(Key.of("minecraft:entity.villager.no")); + Key flame = Key.of("flame"); + for (AABB aabb : aabbs) { + List edgePoints = aabb.getEdgePoints(0.125); + for (Vec3d edgePoint : edgePoints) { + player.playParticle(flame, edgePoint.x(), edgePoint.y(), edgePoint.z()); + } + } } return InteractionResult.FAIL; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index fc961ac8d..e074577b1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -15,7 +15,6 @@ import net.momirealms.craftengine.core.pack.obfuscation.ObfA; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; -import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl; import net.momirealms.craftengine.core.util.Base64Utils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java index fb43eac52..533032261 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java @@ -50,6 +50,7 @@ public class BukkitCommandManager extends AbstractCommandManager new DebugAppearanceStateUsageCommand(this, plugin), new DebugClearCooldownCommand(this, plugin), new DebugEntityIdCommand(this, plugin), + new DebugFurnitureCommand(this, plugin), new DebugRealStateUsageCommand(this, plugin), new DebugItemDataCommand(this, plugin), new DebugSetBlockCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java new file mode 100644 index 000000000..ee2ac39d5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java @@ -0,0 +1,36 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; + +public class DebugFurnitureCommand extends BukkitCommandFeature { + + public DebugFurnitureCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(context.sender()); + boolean b = !serverPlayer.enableFurnitureDebug(); + serverPlayer.setEnableFurnitureDebug(b); + serverPlayer.sendMessage(Component.text("Furniture Debug Mode: ").append(Component.text(b ? "ON" : "OFF").color(b ? NamedTextColor.GREEN : NamedTextColor.RED)), false); + }); + } + + @Override + public String getFeatureID() { + return "debug_furniture"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 19c66e96d..7728e1641 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.plugin.network; -import com.destroystokyo.paper.ParticleBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.JsonElement; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 1ddab9cd6..f9e38db29 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -7,7 +7,9 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; @@ -26,6 +28,8 @@ import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.data.EntityData; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; @@ -156,6 +160,10 @@ public class BukkitServerPlayer extends Player { private Location eyeLocation; // 是否启用家具调试 private boolean enableFurnitureDebug; + // 上一次对准的家具 + private BukkitFurniture lastHitFurniture; + // 缓存的tick + private int lastHitFurnitureTick; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -542,6 +550,33 @@ public class BukkitServerPlayer extends Player { this.updateGUI(); } + if (this.enableFurnitureDebug) { + BukkitFurniture furniture = CraftEngineFurniture.rayTrace(platformPlayer()); + boolean forceShow = furniture != this.lastHitFurniture; + if (forceShow) { + this.lastHitFurnitureTick = 0; + } else { + this.lastHitFurnitureTick++; + if (this.lastHitFurnitureTick % 30 == 0) { + forceShow = true; + } + } + this.lastHitFurniture = furniture; + if (furniture != null && forceShow) { + FurnitureVariant currentVariant = furniture.getCurrentVariant(); + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig config : currentVariant.hitBoxConfigs()) { + config.prepareForPlacement(furniture.position(), aabbs::add); + } + Key endRod = Key.of("end_rod"); + for (AABB aabb : aabbs) { + for (Vec3d point : aabb.getEdgePoints(0.125)) { + this.playParticle(endRod, point.x, point.y, point.z); + } + } + } + } + // 更新眼睛位置 { Location unsureEyeLocation = bukkitPlayer.getEyeLocation(); diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index 41fbb153b..24e0bcccf 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -270,6 +270,13 @@ debug_generate_internal_assets: - /craftengine debug generate-internal-assets - /ce debug generate-internal-assets +debug_furniture: + enable: true + permission: ce.command.debug.furniture + usage: + - /craftengine debug furniture + - /ce debug furniture + debug_test: enable: true permission: ce.command.debug.test diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 2e3a308a6..23b3227ba 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -18,7 +18,6 @@ import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.collision.AABB; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -166,8 +165,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { AbstractFurnitureManager.this.byId.put(id, furniture); } - - private CullingData parseCullingData(Object arguments) { if (arguments instanceof Boolean b && !b) return null; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index f2361c098..7c8325e00 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -24,5 +24,4 @@ public interface FurnitureHitBoxConfig { void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); - void collectBoundingBox(Consumer aabbConsumer); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index ab9ffd4e5..8c66bbff7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -31,7 +31,10 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.SectionConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.locale.*; +import net.momirealms.craftengine.core.plugin.locale.LangData; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index df5d26b97..79dac8655 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -95,10 +94,6 @@ public final class EntityCulling { if (distanceSq > maxDistanceSq) { return false; } - // 太近了,不剔除 - else if (distanceSq < 1) { - return true; - } } if (!rayTracing || !cullable.rayTracing) { From e3e3489198b19c80394e8ec718e5b5dedab2a9d2 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 23:03:41 +0800 Subject: [PATCH 45/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=B2=92=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- .../default/configuration/furniture/flower_basket.yml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index f9e38db29..2bc3a8635 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -568,7 +568,7 @@ public class BukkitServerPlayer extends Player { for (FurnitureHitBoxConfig config : currentVariant.hitBoxConfigs()) { config.prepareForPlacement(furniture.position(), aabbs::add); } - Key endRod = Key.of("end_rod"); + Key endRod = Key.of("soul_fire_flame"); for (AABB aabb : aabbs) { for (Vec3d point : aabb.getEdgePoints(0.125)) { this.playParticle(endRod, point.x, point.y, point.z); diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml index 416b43aad..4fdd08219 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml @@ -59,6 +59,7 @@ furniture: width: 0.7 height: 0.5 interactive: true + invisible: true wall: elements: - item: default:flower_basket_wall @@ -75,6 +76,7 @@ furniture: width: 0.46 height: 0.75 interactive: true + invisible: true - type: interaction can-use-item-on: true can-be-hit-by-projectile: true @@ -83,6 +85,7 @@ furniture: width: 0.46 height: 0.75 interactive: true + invisible: true ceiling: elements: - item: default:flower_basket_ceiling @@ -98,4 +101,5 @@ furniture: position: 0,-0.7,0 width: 0.7 height: 0.7 - interactive: true \ No newline at end of file + interactive: true + invisible: true \ No newline at end of file From df24a4047dd48a62048feb4d99237720e63598a1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 01:19:59 +0800 Subject: [PATCH 46/46] =?UTF-8?q?=E4=B8=BA=E6=B2=99=E5=8F=91=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=97=8B=E8=BD=AC=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/SeatBlockBehavior.java | 2 +- .../bukkit/block/entity/SeatBlockEntity.java | 12 ++- .../entity/furniture/BukkitFurniture.java | 91 ++++++++++++++++++- .../furniture/BukkitFurnitureManager.java | 28 ++++-- .../furniture/FurnitureEventListener.java | 59 ++++++++++++ .../item/listener/DebugStickListener.java | 13 ++- .../plugin/network/BukkitNetworkManager.java | 6 ++ .../default/configuration/blocks/sofa.yml | 2 + .../configuration/templates/events.yml | 31 +++++++ .../furniture/AbstractFurnitureManager.java | 2 +- .../core/entity/furniture/Furniture.java | 35 +++++-- .../entity/furniture/FurnitureConfigImpl.java | 3 +- .../craftengine/core/item/ItemKeys.java | 1 + .../core/pack/AbstractPackManager.java | 1 + .../function/CycleBlockPropertyFunction.java | 3 +- 15 files changed, 257 insertions(+), 32 deletions(-) create mode 100644 common-files/src/main/resources/resources/default/configuration/templates/events.yml diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java index e1c68747b..c54f6eb66 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java @@ -33,7 +33,7 @@ public class SeatBlockBehavior extends BukkitBlockBehavior implements EntityBloc @Override public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { - return new SeatBlockEntity(pos, state, this.seats, this.directionProperty); + return new SeatBlockEntity(pos, state, this.seats); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java index 5ff7a8812..00352c68c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.block.entity; +import net.momirealms.craftengine.bukkit.block.behavior.SeatBlockBehavior; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.entity.BlockEntity; @@ -13,14 +14,14 @@ import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; +import java.util.Optional; + public class SeatBlockEntity extends BlockEntity implements SeatOwner { private final Seat[] seats; - private final Property facing; @SuppressWarnings("unchecked") - public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats, Property directionProperty) { + public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats) { super(BukkitBlockEntityTypes.SEAT, pos, blockState); - this.facing = directionProperty; this.seats = new Seat[seats.length]; for (int i = 0; i < seats.length; i++) { this.seats[i] = new BukkitSeat<>(this, seats[i]); @@ -41,7 +42,10 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner { public boolean spawnSeat(Player player) { int yRot = 0; - if (this.facing != null) { + Optional behavior = super.blockState.behavior().getAs(SeatBlockBehavior.class); + if (behavior.isEmpty()) return false; + Property facing = behavior.get().directionProperty(); + if (facing != null) { HorizontalDirection direction = super.blockState.get(facing); yRot = switch (direction) { case NORTH -> 0; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index d7bbe6f9d..e7fe83ce7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,21 +1,34 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; -import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; public class BukkitFurniture extends Furniture { private final WeakReference metaEntity; @@ -40,6 +53,76 @@ public class BukkitFurniture extends Furniture { } } + @Override + public boolean setVariant(String variantName) { + FurnitureVariant variant = this.config.getVariant(variantName); + if (variant == null) return false; + if (this.currentVariant == variant) return false; + BukkitFurnitureManager.instance().invalidateFurniture(this); + super.clearColliders(); + super.setVariantInternal(variant); + BukkitFurnitureManager.instance().initFurniture(this); + this.addCollidersToWorld(); + this.refresh(); + return true; + } + + @SuppressWarnings("deprecation") + @Override + public CompletableFuture moveTo(WorldPosition position) { + ItemDisplay itemDisplay = this.metaEntity.get(); + if (itemDisplay == null) return CompletableFuture.completedFuture(false); + // 检查新位置是否可用 + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBoxConfig : getCurrentVariant().hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(position, aabbs::add); + } + if (!aabbs.isEmpty()) { + if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + return CompletableFuture.completedFuture(false); + } + } + // 准备传送 + CompletableFuture future = new CompletableFuture<>(); + BukkitFurnitureManager.instance().invalidateFurniture(this); + super.clearColliders(); + this.location = LocationUtils.toLocation(position); + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(removePacket, false); + } + itemDisplay.teleportAsync(this.location).thenAccept(result -> { + if (result) { + super.setVariantInternal(getCurrentVariant()); + BukkitFurnitureManager.instance().initFurniture(this); + this.addCollidersToWorld(); + Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(), + itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(addPacket, false); + } + future.complete(true); + } else { + future.complete(false); + } + }); + return future; + } + + @SuppressWarnings("deprecation") + @Override + protected void refresh() { + ItemDisplay itemDisplay = this.metaEntity.get(); + if (itemDisplay == null) return; + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(), + itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(removePacket, false); + BukkitAdaptors.adapt(player).sendPacket(addPacket, false); + } + } + @Override public void destroy() { Optional.ofNullable(this.metaEntity.get()).ifPresent(Entity::remove); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 5b0f5cacc..c1cb3f717 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -323,18 +323,32 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { // 创建家具实例,并初始化碰撞实体 private BukkitFurniture createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); - this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.virtualEntityIds()) { - this.byVirtualEntityId.put(entityId, bukkitFurniture); - } - for (Collider collisionEntity : bukkitFurniture.colliders()) { - this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); - } + initFurniture(bukkitFurniture); Location location = display.getLocation(); runSafeEntityOperation(location, bukkitFurniture::addCollidersToWorld); return bukkitFurniture; } + protected void initFurniture(BukkitFurniture furniture) { + this.byMetaEntityId.put(furniture.entityId(), furniture); + for (int entityId : furniture.virtualEntityIds()) { + this.byVirtualEntityId.put(entityId, furniture); + } + for (Collider collisionEntity : furniture.colliders()) { + this.byColliderEntityId.put(collisionEntity.entityId(), furniture); + } + } + + protected void invalidateFurniture(BukkitFurniture furniture) { + this.byMetaEntityId.remove(furniture.entityId()); + for (int entityId : furniture.virtualEntityIds()) { + this.byVirtualEntityId.remove(entityId); + } + for (Collider collisionEntity : furniture.colliders()) { + this.byColliderEntityId.remove(collisionEntity.entityId()); + } + } + private void runSafeEntityOperation(Location location, Runnable action) { boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); if (preventChange) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index 534a3b841..c71a480de 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -2,11 +2,26 @@ package net.momirealms.craftengine.bukkit.entity.furniture; import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -14,8 +29,12 @@ import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.EntitiesLoadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; import java.util.List; +import java.util.Map; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; @@ -104,4 +123,44 @@ public class FurnitureEventListener implements Listener { this.manager.handleCollisionEntityUnload(entity); } } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onInteractFurniture(FurnitureInteractEvent event) { + Player bukkitPlayer = event.getPlayer(); + BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer); + if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) { + return; + } + Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); + if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return; + BukkitFurniture furniture = event.furniture(); + List variants = new ArrayList<>(furniture.config.variants().keySet()); + if (variants.size() == 1) { + try { + Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( + ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(furniture.id().asString()))), true); + player.sendPacket(systemChatPacket, false); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Could not create system chat packet", e); + } + } else { + String variantName = furniture.getCurrentVariant().name(); + int index = variants.indexOf(variantName) + 1; + if (index >= variants.size()) { + index = 0; + } + furniture.setVariant(variants.get(index)); + try { + Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( + ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.update") + .arguments( + Component.text("variant"), + Component.text(variants.get(index)) + )), true); + player.sendPacket(systemChatPacket, false); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Could not create system chat packet", e); + } + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java index b3f763ce1..88d7d106c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java @@ -16,7 +16,9 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.util.MiscUtils; import org.bukkit.Material; import org.bukkit.block.Block; @@ -44,12 +46,10 @@ public class DebugStickListener implements Listener { public void onUseDebugStick(PlayerInteractEvent event) { Block clickedBlock = event.getClickedBlock(); if (clickedBlock == null) return; - ItemStack itemInHand = event.getItem(); - if (ItemStackUtils.isEmpty(itemInHand)) return; - Material material = itemInHand.getType(); - if (material != Material.DEBUG_STICK) return; Player bukkitPlayer = event.getPlayer(); BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer); + Item itemInHand = player.getItemInHand(event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return; if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) { return; } @@ -73,8 +73,7 @@ public class DebugStickListener implements Listener { ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true); player.sendPacket(systemChatPacket, false); } else { - Item wrapped = BukkitItemManager.instance().wrap(itemInHand); - Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state"); + Object storedData = itemInHand.getJavaTag("craftengine:debug_stick_state"); if (storedData == null) storedData = new HashMap<>(); if (storedData instanceof Map map) { Map data = new HashMap<>(MiscUtils.castToMap(map, false)); @@ -96,7 +95,7 @@ public class DebugStickListener implements Listener { } else { currentProperty = getRelative(properties, currentProperty, player.isSecondaryUseActive()); data.put(blockId, currentProperty.name()); - wrapped.setTag(data, "craftengine:debug_stick_state"); + itemInHand.setTag(data, "craftengine:debug_stick_state"); Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.select") .arguments( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 7728e1641..18cc1033c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -67,6 +67,7 @@ import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipeHolder; @@ -3804,6 +3805,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return; } + // 不处理调试棒 + if (itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) { + return; + } + // 必须从网络包层面处理,否则无法获取交互的具体实体 if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitBox.config().canUseItemOn()) { Optional> optionalCustomItem = itemInHand.getCustomItem(); diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml index 120c02811..bdd8f8131 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml @@ -53,6 +53,8 @@ items: behavior: type: block_item block: + events: + - template: default:rotatable_block loot: template: default:loot_table/self settings: diff --git a/common-files/src/main/resources/resources/default/configuration/templates/events.yml b/common-files/src/main/resources/resources/default/configuration/templates/events.yml new file mode 100644 index 000000000..d0727291f --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/templates/events.yml @@ -0,0 +1,31 @@ +templates: + default:rotatable_block: + on: right_click + conditions: + - type: expression + expression: + functions: + - type: update_interaction_tick + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + - type: swing_hand + - type: cycle_block_property + property: facing + rules: + north: east + east: south + south: west + west: north + default:rotatable_furniture: + on: right_click + conditions: + - type: expression + expression: + functions: + - type: rotate_furniture + degree: 90 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + on-failure: [] \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 23b3227ba..c3c0d50a8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -123,7 +123,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { throw new LocalizedResourceConfigException("warning.config.furniture.missing_variants"); } - Map variants = new HashMap<>(); + Map variants = new LinkedHashMap<>(); for (Map.Entry e0 : variantsMap.entrySet()) { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 9210c649e..d5d51596e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public abstract class Furniture implements Cullable { public final FurnitureConfig config; @@ -53,7 +54,7 @@ public abstract class Furniture implements Cullable { this.config = config; this.dataAccessor = data; this.metaDataEntity = metaDataEntity; - this.setVariant(config.getVariant(data)); + this.setVariantInternal(config.getVariant(data)); } public Entity metaDataEntity() { @@ -64,7 +65,21 @@ public abstract class Furniture implements Cullable { return this.currentVariant; } - public void setVariant(FurnitureVariant variant) { + public abstract boolean setVariant(String variantName); + + public abstract CompletableFuture moveTo(WorldPosition position); + + protected abstract void refresh(); + + protected void clearColliders() { + if (this.colliders != null) { + for (Collider collider : this.colliders) { + collider.destroy(); + } + } + } + + protected void setVariantInternal(FurnitureVariant variant) { this.currentVariant = variant; this.hitboxMap = new Int2ObjectOpenHashMap<>(); // 初始化家具元素 @@ -201,20 +216,28 @@ public abstract class Furniture implements Cullable { @Override public void show(Player player) { for (FurnitureElement element : this.elements) { - element.show(player); + if (element != null) { + element.show(player); + } } for (FurnitureHitBox hitbox : this.hitboxes) { - hitbox.show(player); + if (hitbox != null) { + hitbox.show(player); + } } } @Override public void hide(Player player) { for (FurnitureElement element : this.elements) { - element.hide(player); + if (element != null) { + element.hide(player); + } } for (FurnitureHitBox hitbox : this.hitboxes) { - hitbox.hide(player); + if (hitbox != null) { + hitbox.hide(player); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index a68613fef..2833306c8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; import net.momirealms.craftengine.core.loot.LootTable; @@ -33,7 +34,7 @@ class FurnitureConfigImpl implements FurnitureConfig { @Nullable LootTable lootTable) { this.id = id; this.settings = settings; - this.variants = ImmutableMap.copyOf(variants); + this.variants = ImmutableSortedMap.copyOf(variants); this.lootTable = lootTable; this.behavior = behavior; this.events = events; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 4242b8d6a..161ea63a7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -65,6 +65,7 @@ public final class ItemKeys { public static final Key PURPLE_DYE = Key.of("minecraft:purple_dye"); public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye"); public static final Key PINK_DYE = Key.of("minecraft:pink_dye"); + public static final Key DEBUG_STICK = Key.of("minecraft:debug_stick"); public static final Key CARROT = Key.of("minecraft:carrot"); public static final Key POTATO = Key.of("minecraft:potato"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 8c66bbff7..8ec309b93 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -471,6 +471,7 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/configuration/templates/loot_tables.yml"); plugin.saveResource("resources/default/configuration/templates/recipes.yml"); plugin.saveResource("resources/default/configuration/templates/tool_levels.yml"); + plugin.saveResource("resources/default/configuration/templates/events.yml"); plugin.saveResource("resources/default/configuration/categories.yml"); plugin.saveResource("resources/default/configuration/emoji.yml"); plugin.saveResource("resources/default/configuration/translations.yml"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java index b99ef0e72..393468694 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -71,7 +72,7 @@ public class CycleBlockPropertyFunction extends AbstractCon if (value == null) { return wrapper.cycleProperty(this.property, inverse); } - String mapValue = this.rules.get(value.toString()); + String mapValue = this.rules.get(value.toString().toLowerCase(Locale.ROOT)); if (mapValue == null) { return wrapper.cycleProperty(this.property, inverse); }