diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts
index 7b21b01a6..d2e52a736 100644
--- a/bukkit/build.gradle.kts
+++ b/bukkit/build.gradle.kts
@@ -93,6 +93,7 @@ tasks {
relocate("net.objecthunter.exp4j", "net.momirealms.craftengine.libraries.exp4j")
relocate("net.bytebuddy", "net.momirealms.craftengine.libraries.bytebuddy")
relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml")
+ relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
}
}
diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts
index 9c093d371..20a743b2a 100644
--- a/bukkit/loader/build.gradle.kts
+++ b/bukkit/loader/build.gradle.kts
@@ -72,5 +72,6 @@ tasks {
relocate("net.objecthunter.exp4j", "net.momirealms.craftengine.libraries.exp4j")
relocate("net.bytebuddy", "net.momirealms.craftengine.libraries.bytebuddy")
relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml")
+ relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
}
}
diff --git a/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/BukkitBootstrap.java b/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/BukkitBootstrap.java
index 27c93a874..3e73cf8ea 100644
--- a/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/BukkitBootstrap.java
+++ b/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/BukkitBootstrap.java
@@ -12,16 +12,16 @@ public class BukkitBootstrap extends JavaPlugin {
@Override
public void onLoad() {
- this.plugin.load();
+ this.plugin.onPluginLoad();
}
@Override
public void onEnable() {
- this.plugin.enable();
+ this.plugin.onPluginEnable();
}
@Override
public void onDisable() {
- this.plugin.disable();
+ this.plugin.onPluginDisable();
}
}
diff --git a/bukkit/loader/src/main/resources/commands.yml b/bukkit/loader/src/main/resources/commands.yml
index 1e4acc716..125d3e971 100644
--- a/bukkit/loader/src/main/resources/commands.yml
+++ b/bukkit/loader/src/main/resources/commands.yml
@@ -69,6 +69,13 @@ search_recipe_admin:
- /craftengine item search-recipe
- /ce item search-recipe
+totem_animation:
+ enable: true
+ permission: ce.command.admin.totem_animation
+ usage:
+ - /craftengine feature totem-animation
+ - /ce feature totem-animation
+
# Debug commands
debug_set_block:
enable: true
diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml
index 5624e7bd8..9fededa9d 100644
--- a/bukkit/loader/src/main/resources/config.yml
+++ b/bukkit/loader/src/main/resources/config.yml
@@ -152,7 +152,14 @@ image:
sign: true
recipe:
+ # Enable the plugin's recipe system
enable: true
+ # Disable vanilla recipes
+ disable-vanilla-recipes:
+ # Disable all vanilla recipes
+ all: false
+ # Disable the recipes in list
+ list: []
gui:
browser:
diff --git a/bukkit/loader/src/main/resources/craft-engine.properties b/bukkit/loader/src/main/resources/craft-engine.properties
index 496fba7c7..1972baa7d 100644
--- a/bukkit/loader/src/main/resources/craft-engine.properties
+++ b/bukkit/loader/src/main/resources/craft-engine.properties
@@ -25,4 +25,5 @@ snake-yaml=${snake_yaml_version}
adventure-text-minimessage=${adventure_bundle_version}
adventure-text-serializer-gson=${adventure_bundle_version}
adventure-text-serializer-json=${adventure_bundle_version}
-netty-codec-http=${netty_version}
\ No newline at end of file
+netty-codec-http=${netty_version}
+ahocorasick=${ahocorasick_version}
\ No newline at end of file
diff --git a/bukkit/loader/src/main/resources/internal/textures/entity/shulker/shulker.png b/bukkit/loader/src/main/resources/internal/textures/entity/shulker/shulker.png
deleted file mode 100644
index b6c9cade6..000000000
Binary files a/bukkit/loader/src/main/resources/internal/textures/entity/shulker/shulker.png and /dev/null differ
diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml
index 641bf7396..08e34f0a2 100644
--- a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml
+++ b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml
@@ -471,15 +471,15 @@ templates#settings#sounds:
arguments:
block_type: bamboo
default:sound/vine:
- template: default:block
+ template: default:sound/block_template
arguments:
block_type: vine
default:sound/lantern:
- template: default:block
+ template: default:sound/block_template
arguments:
block_type: lantern
default:sound/amethyst_block:
- template: default:block
+ template: default:sound/block_template
arguments:
block_type: amethyst_block
diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml
index eab86ce3f..d2c95dfb1 100644
--- a/bukkit/loader/src/main/resources/translations/en.yml
+++ b/bukkit/loader/src/main/resources/translations/en.yml
@@ -38,11 +38,11 @@ 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"
-command.reload.config.success: "Configs reloaded in ms."
+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."
command.reload.pack.failure: "Resource pack reload failed. Check console logs."
-command.reload.all.success: "Reload completed in ms."
+command.reload.all.success: "Reload completed in ms. (Async: ms | Sync: ms | Pack: ms)"
command.reload.all.failure: "Reload failed. Check console logs."
command.item.get.success: "Got "
command.item.get.failure.not_exist: "'>"
@@ -52,4 +52,54 @@ command.item.give.failure.not_exist: "No recipe found for this item"
command.search_usage.not_found: "No usage found for this item"
command.search_recipe.no_item: "Please hold an item before running this command"
-command.search_usage.no_item: "Please hold an item before running this command"
\ No newline at end of file
+command.search_usage.no_item: "Please hold an item before running this command"
+command.totem_animation.failure.not_totem: "Item '' is not minecraft:totem_of_undying"
+warning.config.image.duplicated: "Issue found in file - Duplicated image ''."
+warning.config.image.lack_height: "Issue found in file - The image '' is missing the required 'height' argument."
+warning.config.image.height_smaller_than_ascent: "Issue found in file - The image '' violates the bitmap image rule: 'height' should be no lower than 'ascent'."
+warning.config.image.no_file: "Issue found in file - The image '' is missing the required 'file' argument."
+warning.config.image.invalid_resource_location: "Issue found in file - The image '' has a 'file' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters"
+warning.config.image.invalid_font_name: "Issue found in file - The image '' has a 'font' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters"
+warning.config.image.lack_char: "Issue found in file - The image '' is missing the required 'char' argument."
+warning.config.image.codepoint_in_use: "Issue found in file - The image '' is using a character[()] in font that has been used by another image ''."
+warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grind."
+warning.config.image.file_not_exist: "Issue found in file - PNG file '' not found for image ''."
+warning.config.recipe.duplicated: "Issue found in file - Duplicated recipe ''."
+warning.config.i18n.unknown_locale: "Issue found in file - Unknown locale ''."
+warning.config.template.duplicated: "Issue found in file - Duplicated template ''."
+warning.config.vanilla_loot.type_not_exist: "Issue found in file - 'type' not set for vanilla loot ''."
+warning.config.vanilla_loot.block.invalid_target: "Issue found in file - Invalid block target [] in vanilla loot ''."
+warning.config.sound.duplicated: "Issue found in file - Duplicated sound ''."
+warning.config.jukebox_song.duplicated: "Issue found in file - Duplicated jukebox song ''."
+warning.config.furniture.duplicated: "Issue found in file - Duplicated furniture ''."
+warning.config.furniture.lack_placement: "Issue found in file - The furniture '' is missing the required 'placement' argument."
+warning.config.furniture.element.lack_item: "Issue found in file - The furniture '' is missing the required 'item' argument for one of its elements."
+warning.config.item.duplicated: "Issue found in file - Duplicated item ''."
+warning.config.item.lack_material: "Issue found in file - The item '' is missing the required 'material' argument."
+warning.config.item.invalid_material: "Issue found in file - The item '' is using an invalid material type ''."
+warning.config.item.bad_custom_model_data_value: "Issue found in file - The item '' is using a custom model data [] that is too large. It's recommended to use a value lower than 16,777,216."
+warning.config.item.custom_model_data_conflict: "Issue found in file - The item '' is using a custom model data [] that has been occupied by item ''"
+warning.config.item.lack_model_id: "Issue found in file - The item '' is missing the required 'custom-model-data' or 'item-model' argument."
+warning.config.block.duplicated: "Issue found in file - Duplicated block ''."
+warning.config.block.lack_state: "Issue found in file - The block '' is missing the required 'state' argument."
+warning.config.block.state.lack_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'."
+warning.config.block.state.lack_state: "Issue found in file - The block '' is missing the required 'state' argument for 'state'."
+warning.config.block.state.lack_properties: "Issue found in file - The block '' is missing the required 'properties' section for 'states'."
+warning.config.block.state.lack_appearances: "Issue found in file - The block '' is missing the required 'appearances' section for 'states'."
+warning.config.block.state.lack_variants: "Issue found in file - The block '' is missing the required 'variants' section for 'states'."
+warning.config.block.state.variant.lack_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''."
+warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''."
+warning.config.block.state.invalid_state: "Issue found in file - The block '' is using an invalid vanilla block state ''."
+warning.config.block.state.unavailable_state: "Issue found in file - The block '' is using an unavailable vanilla block state ''."
+warning.config.block.state.invalid_vanilla_state_id: "Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'."
+warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''."
+warning.config.block.state.bind_real_state: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''."
+warning.config.block.state.invalid_property_structure: "Issue found in file - The block '' has an invalid property structure ''."
+warning.config.block.state.invalid_property: "Issue found in file - Failed to create property '' for block '': ."
+warning.config.block.state.no_model_set: "Issue found in file - The block '' is missing the required 'model' or 'models' argument."
+warning.config.block.state.invalid_real_state_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up."
+warning.config.block.state.model.lack_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'."
+warning.config.block.state.model.invalid_resource_location: "Issue found in file - The block '' has a 'path' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters"
+warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''"
+warning.config.model.generation.texture.invalid_resource_location: "Issue found in file - The config '' has a '' texture argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters"
+warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters"
\ No newline at end of file
diff --git a/bukkit/loader/src/main/resources/translations/es.yml b/bukkit/loader/src/main/resources/translations/es.yml
index b716978d9..eda313eaf 100644
--- a/bukkit/loader/src/main/resources/translations/es.yml
+++ b/bukkit/loader/src/main/resources/translations/es.yml
@@ -38,11 +38,11 @@ argument.parse.failure.aggregate.missing: "Componente Faltante ''Componente Invalido '': "
argument.parse.failure.either: "No se ha podido resolver o de ''"
argument.parse.failure.namedtextcolor: "'' no es un color de texto con nombre"
-command.reload.config.success: "Recargado. Tomó ms."
+command.reload.config.success: "Recargado. Tomó ms. (Async: ms | Sync: ms)"
command.reload.config.failure: "Error al recargar la configuración. Por favor, revisa el registro de la consola."
command.reload.pack.success: "Paquete de recursos recargado. Tomó ms."
command.reload.pack.failure: "Error al recargar el paquete de recursos. Por favor, revisa el registro de la consola."
-command.reload.all.success: "Todo recargado. Tomó ms."
+command.reload.all.success: "Todo recargado. Tomó ms. (Async: ms | Sync: ms | Pack: ms)"
command.reload.all.failure: "Error al recargar. Por favor, revisa el registro de la consola."
command.item.get.success: "Obtener "
command.item.get.failure.not_exist: "'>"
@@ -52,4 +52,5 @@ command.item.give.failure.not_exist: "No se encontró ninguna receta para este objeto"
command.search_usage.not_found: "No se encontró ningún uso para este objeto"
command.search_recipe.no_item: "Por favor, sostén un objeto antes de ejecutar este comando"
-command.search_usage.no_item: "Por favor, sostén un objeto antes de ejecutar este comando"
\ No newline at end of file
+command.search_usage.no_item: "Por favor, sostén un objeto antes de ejecutar este comando"
+command.totem_animation.failure.not_totem: "'' no es del tipo totem_of_undying"
\ No newline at end of file
diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml
index 3de54b7d3..822f32e54 100644
--- a/bukkit/loader/src/main/resources/translations/zh_cn.yml
+++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml
@@ -38,12 +38,12 @@ argument.parse.failure.aggregate.missing: "缺少组件 ''"
argument.parse.failure.aggregate.failure: "无效的组件 '': "
argument.parse.failure.either: "无法从 '' 解析 或 "
argument.parse.failure.namedtextcolor: "'' 不是颜色代码"
-command.reload.config.success: "重新加载配置完成. 耗时 毫秒"
-command.reload.config.failure: "重新加载配置失败,请检查控制台日志。"
+command.reload.config.success: "重新加载配置完成. 耗时 毫秒 (异步: ms | 同步: ms)"
+command.reload.config.failure: "重新加载配置失败,请检查控制台日志"
command.reload.pack.success: "资源包重新加载完成. 耗时 毫秒"
-command.reload.pack.failure: "重新加载资源包失败,请检查控制台日志。"
-command.reload.all.success: "全部重新加载完成. 耗时 毫秒"
-command.reload.all.failure: "重新加载失败,请检查控制台日志。"
+command.reload.pack.failure: "重新加载资源包失败,请检查控制台日志"
+command.reload.all.success: "全部重新加载完成. 耗时 毫秒 (异步: ms | 同步: ms | 资源包: ms)"
+command.reload.all.failure: "重新加载失败,请检查控制台日志"
command.item.get.success: "获得个"
command.item.get.failure.not_exist: "'>"
command.item.give.success.single: "':'':''>"
@@ -52,4 +52,54 @@ command.item.give.failure.not_exist: "找不到此物品的配方"
command.search_usage.not_found: "找不到此物品的用途"
command.search_recipe.no_item: "请手持物品后再执行此命令"
-command.search_usage.no_item: "请手持物品后再执行此命令"
\ No newline at end of file
+command.search_usage.no_item: "请手持物品后再执行此命令"
+command.totem_animation.failure.not_totem: "'' 不是 totem_of_undying 类型"
+warning.config.image.duplicated: "在文件 中发现问题 - 图片 '' 重复定义"
+warning.config.image.lack_height: "在文件 中发现问题 - 图片 '' 缺少必要的 'height' 高度参数"
+warning.config.image.height_smaller_than_ascent: "在文件 中发现问题 - 图片 '' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度"
+warning.config.image.no_file: "在文件 中发现问题 - 图片 '' 缺少必要的 'file' 文件参数"
+warning.config.image.invalid_resource_location: "在文件 中发现问题 - 图片 '' 的 'file' 参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6"
+warning.config.image.invalid_font_name: "在文件 中发现问题 - 图片 '' 的 'font' 字体参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6"
+warning.config.image.lack_char: "在文件 中发现问题 - 图片 '' 缺少必要的 'char' 字符参数"
+warning.config.image.codepoint_in_use: "在文件 中发现问题 - 图片 '' 使用的字体 字符 [()] 已被其他图片 '' 占用"
+warning.config.image.invalid_codepoint_grid: "在文件 中发现问题 - 图片 '' 的 'chars' 码位网格配置无效"
+warning.config.image.file_not_exist: "在文件 中发现问题 - 图片 '' 对应的PNG文件 不存在"
+warning.config.recipe.duplicated: "在文件 中发现问题 - 配方 '' 重复定义"
+warning.config.i18n.unknown_locale: "在文件 中发现问题 - 未知的语言区域 ''"
+warning.config.template.duplicated: "在文件 中发现问题 - 模板 '' 重复定义"
+warning.config.vanilla_loot.type_not_exist: "在文件 中发现问题 - 原版战利品 '' 未设置 'type' 类型参数"
+warning.config.vanilla_loot.block.invalid_target: "在文件 中发现问题 - 原版战利品 '' 中的方块目标 [] 无效"
+warning.config.sound.duplicated: "在文件 中发现问题 - 音效 '' 重复定义"
+warning.config.jukebox_song.duplicated: "在文件 中发现问题 - 唱片机歌曲 '' 重复定义"
+warning.config.furniture.duplicated: "在文件 中发现问题 - 家具 '' 重复定义"
+warning.config.furniture.lack_placement: "在文件 中发现问题 - 家具 '' 缺少必要的 'placement' 放置参数"
+warning.config.furniture.element.lack_item: "在文件 中发现问题 - 家具 '' 的某个元素缺少必要的 'item' 物品参数"
+warning.config.item.duplicated: "在文件 中发现问题 - 物品 '' 重复定义"
+warning.config.item.lack_material: "在文件 中发现问题 - 物品 '' 缺少必要的 'material' 材料参数"
+warning.config.item.invalid_material: "在文件 中发现问题 - 物品 '' 使用了无效的材料类型 ''"
+warning.config.item.bad_custom_model_data_value: "在文件 中发现问题 - 物品 '' 的自定义模型数据值 [] 过大,建议使用低于16,777,216的值"
+warning.config.item.custom_model_data_conflict: "在文件 中发现问题 - 物品 '' 的自定义模型数据 [] 与物品 '' 发生冲突"
+warning.config.item.lack_model_id: "在文件 中发现问题 - 物品 '' 缺少必要的 'custom-model-data' 或 'item-model' 模型标识参数"
+warning.config.block.duplicated: "在文件 中发现问题 - 方块 '' 重复定义"
+warning.config.block.lack_state: "在文件 中发现问题 - 方块 '' 缺少必要的 'state' 状态参数"
+warning.config.block.state.lack_real_id: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'id' 标识参数"
+warning.config.block.state.lack_state: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'state' 状态参数"
+warning.config.block.state.lack_properties: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'properties' 属性配置"
+warning.config.block.state.lack_appearances: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'appearances' 外观配置"
+warning.config.block.state.lack_variants: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'variants' 变体配置"
+warning.config.block.state.variant.lack_appearance: "在文件 中发现问题 - 方块 '' 的 '' 变体配置缺少必要的 'appearance' 外观参数"
+warning.config.block.state.variant.invalid_appearance: "在文件 中发现问题 - 方块 '' 的 '' 变体引用了不存在的外观配置 ''"
+warning.config.block.state.invalid_state: "在文件 中发现问题 - 方块 '' 使用了无效的原版方块状态 ''"
+warning.config.block.state.unavailable_state: "在文件 中发现问题 - 方块 '' 使用了不可用的原版方块状态 ''"
+warning.config.block.state.invalid_vanilla_state_id: "在文件 中发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'"
+warning.config.block.state.conflict: "在文件 中发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用"
+warning.config.block.state.bind_real_state: "在文件 中发现问题 - 方块 '' 未能绑定真实方块状态 '',该状态已被 '' 占用"
+warning.config.block.state.invalid_property_structure: "在文件 中发现问题 - 方块 '' 的属性结构 '' 配置无效"
+warning.config.block.state.invalid_property: "在文件 中发现问题 - 无法为方块 '' 创建属性 '':"
+warning.config.block.state.no_model_set: "在文件 中发现问题 - 方块 '' 缺少必要的 'model' 或 'models' 模型参数"
+warning.config.block.state.invalid_real_state_id: "在文件 中发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~'。若槽位已用尽,请考虑在 additional-real-blocks.yml 中添加更多真实状态"
+warning.config.block.state.model.lack_path: "在文件 中发现问题 - 方块 '' 的 'model' 配置缺少必要的 'path' 路径参数"
+warning.config.block.state.model.invalid_resource_location: "在文件 中发现问题 - 方块 '' 的 'path' 路径参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6"
+warning.config.model.generation.conflict: "在文件 中发现问题 - 无法为 '' 生成模型,多个配置尝试用相同路径 '' 生成不同的JSON模型"
+warning.config.model.generation.texture.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的 '' 纹理参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6"
+warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6"
\ No newline at end of file
diff --git a/bukkit/loader/src/main/resources/translations/zh_tw.yml b/bukkit/loader/src/main/resources/translations/zh_tw.yml
index 8a48c57d8..dfff5088e 100644
--- a/bukkit/loader/src/main/resources/translations/zh_tw.yml
+++ b/bukkit/loader/src/main/resources/translations/zh_tw.yml
@@ -38,11 +38,11 @@ argument.parse.failure.aggregate.missing: "缺少元件 ''"
argument.parse.failure.aggregate.failure: "無效的元件 '': "
argument.parse.failure.either: "無法從 '' 解析 或 "
argument.parse.failure.namedtextcolor: "'' 不是顏色代碼"
-command.reload.config.success: "重新加載配置完成. 耗時 毫秒"
+command.reload.config.success: "重新加載配置完成. 耗時 毫秒 (非同步: ms | 同步: ms)"
command.reload.config.failure: "重新加載配置失敗,請檢查控制台日誌。"
command.reload.pack.success: "資源包重新加載完成. 耗時 毫秒"
command.reload.pack.failure: "重新加載資源包失敗,請檢查控制台日誌。"
-command.reload.all.success: "全部重新加載完成. 耗時 毫秒"
+command.reload.all.success: "全部重新加載完成. 耗時 毫秒 (非同步: ms | 同步: ms | 資源包: ms)"
command.reload.all.failure: "重新加載失敗,請檢查控制台日誌。"
command.item.get.success: "獲得個"
command.item.get.failure.not_exist: "'>"
@@ -52,4 +52,5 @@ command.item.give.failure.not_exist: "找不到此物品的配方"
command.search_usage.not_found: "找不到此物品的用途"
command.search_recipe.no_item: "執行此命令前請手持物品"
-command.search_usage.no_item: "執行此命令前請手持物品"
\ No newline at end of file
+command.search_usage.no_item: "執行此命令前請手持物品"
+command.totem_animation.failure.not_totem: "'' 不是 totem_of_undying 類型"
\ No newline at end of file
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java
index 3618b8d43..b7bbe8e5a 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java
@@ -6,7 +6,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
-import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -14,7 +13,6 @@ import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
-import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.context.ContextHolder;
import net.momirealms.craftengine.core.world.Vec3d;
@@ -39,7 +37,7 @@ public final class CraftEngineBlocks {
*/
@Nullable
public static CustomBlock byId(@NotNull Key id) {
- return BukkitBlockManager.instance().getBlock(id).orElse(null);
+ return BukkitBlockManager.instance().blockById(id).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 436f677b3..fca21a6cc 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
@@ -34,7 +34,7 @@ public class CraftEngineFurniture {
* @return the custom furniture
*/
public static CustomFurniture byId(@NotNull Key id) {
- return BukkitFurnitureManager.instance().getFurniture(id).orElse(null);
+ return BukkitFurnitureManager.instance().furnitureById(id).orElse(null);
}
/**
@@ -139,7 +139,7 @@ public class CraftEngineFurniture {
*/
@Nullable
public static LoadedFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
- return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntity.getEntityId());
+ return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId());
}
/**
@@ -152,7 +152,7 @@ public class CraftEngineFurniture {
public static LoadedFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
Integer baseEntityId = seat.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseEntityId == null) return null;
- return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntityId);
+ return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntityId);
}
/**
@@ -163,7 +163,7 @@ public class CraftEngineFurniture {
*/
public static boolean remove(@NotNull Entity furniture) {
if (!isFurniture(furniture)) return false;
- LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
+ LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
loadedFurniture.destroy();
return true;
@@ -181,7 +181,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
- LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
+ LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
return true;
@@ -201,7 +201,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
- LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
+ LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, player, dropLoot, playSound);
return true;
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 3bc68b877..239bc3e61 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
@@ -8,14 +8,13 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
-import net.momirealms.craftengine.core.block.PushReaction;
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.loot.LootTable;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
-import net.momirealms.craftengine.core.plugin.config.ConfigManager;
+import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.context.ContextHolder;
import net.momirealms.craftengine.core.world.BlockPos;
@@ -29,7 +28,10 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
-import org.bukkit.event.block.*;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockExplodeEvent;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.world.GenericGameEvent;
import org.bukkit.inventory.ItemStack;
@@ -58,7 +60,7 @@ public class BlockEventListener implements Listener {
player.swingHand(event.getHand());
}
// send sound if the placed block's sounds are removed
- if (ConfigManager.enableSoundSystem()) {
+ if (Config.enableSoundSystem()) {
Block block = event.getBlock();
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
@@ -169,7 +171,7 @@ public class BlockEventListener implements Listener {
});
}
// sound system
- if (ConfigManager.enableSoundSystem()) {
+ if (Config.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (this.manager.isBlockSoundRemoved(ownerBlock)) {
try {
@@ -241,7 +243,7 @@ public class BlockEventListener implements Listener {
if (!BlockStateUtils.isVanillaBlock(stateId)) {
ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId);
player.playSound(playerLocation, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch());
- } else if (ConfigManager.enableSoundSystem()) {
+ } else if (Config.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (manager.isBlockSoundRemoved(ownerBlock)) {
try {
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java
index 0757e0cf4..a326b0678 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java
@@ -19,10 +19,14 @@ import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.properties.Properties;
import net.momirealms.craftengine.core.block.properties.Property;
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.ResourceLocation;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
-import net.momirealms.craftengine.core.plugin.config.ConfigManager;
+import net.momirealms.craftengine.core.plugin.config.Config;
+import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
+import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -33,7 +37,6 @@ import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
-import org.incendo.cloud.suggestion.Suggestion;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
@@ -45,6 +48,7 @@ import java.util.*;
public class BukkitBlockManager extends AbstractBlockManager {
private static BukkitBlockManager instance;
private final BukkitCraftEngine plugin;
+ private final BlockParser blockParser;
// A temporary map used to detect whether the same block state corresponds to multiple models.
private final Map tempRegistryIdConflictMap = new HashMap<>();
@@ -55,11 +59,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
// The total amount of blocks registered
private int customBlockCount;
-
- // CraftEngine objects
- private final Map id2CraftEngineBlocks = new HashMap<>();
- private final ImmutableBlockState[] stateId2ImmutableBlockStates;
-
+ protected final ImmutableBlockState[] stateId2ImmutableBlockStates;
// Minecraft objects
// Cached new blocks $ holders
private ImmutableMap internalId2StateId;
@@ -83,25 +83,21 @@ public class BukkitBlockManager extends AbstractBlockManager {
private final Map> blockStateOverrides = new HashMap<>();
// for mod, real block id -> state models
private final Map modBlockStates = new HashMap<>();
- // Cached command suggestions
- private final List cachedSuggestions = new ArrayList<>();
- // Cached Namespace
- private final Set namespacesInUse = new HashSet<>();
// Event listeners
private final BlockEventListener blockEventListener;
private final FallingBlockRemoveListener fallingBlockRemoveListener;
-
private WorldEditCommandHelper weCommandHelper;
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
this.plugin = plugin;
+ this.blockParser = new BlockParser();
this.initVanillaRegistry();
this.loadMappingsAndAdditionalBlocks();
if (plugin.hasMod() && plugin.requiresRestart()) {
blockEventListener = null;
fallingBlockRemoveListener = null;
- stateId2ImmutableBlockStates = null;
+ stateId2ImmutableBlockStates = new ImmutableBlockState[]{};
return;
}
this.registerBlocks();
@@ -147,11 +143,9 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public void unload() {
- super.clearModelsToGenerate();
+ super.unload();
this.clearCache();
this.appearanceToRealState.clear();
- this.id2CraftEngineBlocks.clear();
- this.cachedSuggestions.clear();
this.blockStateOverrides.clear();
this.modBlockStates.clear();
if (EmptyBlock.INSTANCE != null)
@@ -221,47 +215,16 @@ public class BukkitBlockManager extends AbstractBlockManager {
return Collections.unmodifiableMap(this.modBlockStates);
}
+ @Override
+ public ConfigSectionParser parser() {
+ return this.blockParser;
+ }
+
@Override
public Map> blockOverrides() {
return Collections.unmodifiableMap(this.blockStateOverrides);
}
- @Override
- public Map blocks() {
- return Collections.unmodifiableMap(this.id2CraftEngineBlocks);
- }
-
- @Override
- public Optional getBlock(Key key) {
- return Optional.ofNullable(this.id2CraftEngineBlocks.get(key));
- }
-
- @Override
- public Collection cachedSuggestions() {
- return Collections.unmodifiableCollection(this.cachedSuggestions);
- }
-
- @Override
- public void initSuggestions() {
- this.cachedSuggestions.clear();
- this.namespacesInUse.clear();
- Set states = new HashSet<>();
- for (CustomBlock block : this.id2CraftEngineBlocks.values()) {
- states.add(block.id().toString());
- this.namespacesInUse.add(block.id().namespace());
- for (ImmutableBlockState state : block.variantProvider().states()) {
- states.add(state.toString());
- }
- }
- for (String state : states) {
- this.cachedSuggestions.add(Suggestion.suggestion(state));
- }
- }
-
- public Set namespacesInUse() {
- return Collections.unmodifiableSet(namespacesInUse);
- }
-
public ImmutableMap> blockAppearanceArranger() {
return blockAppearanceArranger;
}
@@ -368,162 +331,224 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
- @Override
- public void parseSection(Pack pack, Path path, Key id, Map section) {
- // read block settings
- BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
- // read loot table
- LootTable lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
- // read states
- Map> properties;
- Map appearances;
- Map variants;
- Map stateSection = MiscUtils.castToMap(section.get("state"), true);
- if (stateSection != null) {
- properties = Map.of();
- int internalId = MiscUtils.getAsInt(stateSection.getOrDefault("id", -1));
- if (PreConditions.runIfTrue(internalId < 0, () -> plugin.logger().warn(path, "No state id configured for block " + id))) return;
- Pair pair = parseAppearanceSection(path, stateSection, id);
- if (pair == null) return;
- appearances = Map.of("default", pair.right());
- Key internalBlockId = Key.of(CraftEngine.NAMESPACE, pair.left().value() + "_" + internalId);
- int internalBlockRegistryId = MiscUtils.getAsInt(this.internalId2StateId.getOrDefault(internalBlockId, -1));
- if (internalBlockRegistryId == -1) {
- plugin.logger().warn(path, "Failed to register " + id + " because id " + internalId + " is not a value between 0~" + (MiscUtils.getAsInt(this.registeredRealBlockSlots.get(pair.left()))-1) +
- ". Consider editing additional-real-blocks.yml if the number of real block IDs is insufficient while there are still available appearances");
- return;
- }
- variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
- } else {
- Map statesSection = MiscUtils.castToMap(section.get("states"), true);
- if (statesSection == null) {
- plugin.logger().warn(path, "No states configured for block " + id);
- return;
- }
- Map propertySection = MiscUtils.castToMap(statesSection.get("properties"), true);
- if (PreConditions.isNull(propertySection, () -> plugin.logger().warn(path, "No properties configured for block " + id))) return;
- properties = parseProperties(path, propertySection);
- Map appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true);
- if (PreConditions.isNull(appearancesSection, () -> plugin.logger().warn(path, "No appearances configured for block " + id))) return;
- appearances = new HashMap<>();
- Map tempTypeMap = new HashMap<>();
- for (Map.Entry appearanceEntry : appearancesSection.entrySet()) {
- if (appearanceEntry.getValue() instanceof Map, ?> appearanceSection) {
- Pair pair = parseAppearanceSection(path, MiscUtils.castToMap(appearanceSection, false), id);
- if (pair == null) return;
- appearances.put(appearanceEntry.getKey(), pair.right());
- tempTypeMap.put(appearanceEntry.getKey(), pair.left());
- }
- }
- Map variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true);
- if (PreConditions.isNull(variantsSection, () -> plugin.logger().warn(path, "No variants configured for block " + id))) return;
- variants = new HashMap<>();
- for (Map.Entry variantEntry : variantsSection.entrySet()) {
- if (variantEntry.getValue() instanceof Map, ?> variantSection0) {
- Map variantSection = MiscUtils.castToMap(variantSection0, false);
- String variantName = variantEntry.getKey();
- String appearance = (String) variantSection.get("appearance");
- if (appearance == null) {
- plugin.logger().warn(path, "No appearance configured for variant " + variantName);
- return;
- }
- if (!appearances.containsKey(appearance)) {
- plugin.logger().warn(path, appearance + " is not a valid appearance for block " + id);
- return;
- }
- int internalId = MiscUtils.getAsInt(variantSection.getOrDefault("id", -1));
- Key baseBlock = tempTypeMap.get(appearance);
- Key internalBlockId = Key.of(CraftEngine.NAMESPACE, baseBlock.value() + "_" + internalId);
- int internalBlockRegistryId = MiscUtils.getAsInt(this.internalId2StateId.getOrDefault(internalBlockId, -1));
- if (internalBlockRegistryId == -1) {
- plugin.logger().warn(path, "Failed to register " + id + " because id " + internalId + " is not a value between 0~" + (MiscUtils.getAsInt(this.registeredRealBlockSlots.getOrDefault(baseBlock, 1))-1) +
- ". Consider editing additional-real-blocks.yml if the number of real block IDs is insufficient while there are still available appearances");
- return;
- }
- Map anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
- variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
- }
- }
+ public class BlockParser implements ConfigSectionParser {
+ public static final String[] CONFIG_SECTION_NAME = new String[] {"blocks", "block"};
+
+ @Override
+ public String[] sectionId() {
+ return CONFIG_SECTION_NAME;
}
- // create or get block holder
- Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
- ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
- // create block
- Map behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
- BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable);
+ @Override
+ public int loadingSequence() {
+ return LoadingSequence.BLOCK;
+ }
- // bind appearance
- bindAppearance(block);
- this.id2CraftEngineBlocks.put(id, block);
+ @Override
+ public void parseSection(Pack pack, Path path, Key id, Map section) {
+ // check duplicated config
+ if (byId.containsKey(id)) {
+ TranslationManager.instance().log("warning.config.block.duplicated", path.toString(), id.toString());
+ return;
+ }
+ // read block settings
+ BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
+ // read loot table
+ LootTable lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
+ // read states
+ Map> properties;
+ Map appearances;
+ Map variants;
+ Map stateSection = MiscUtils.castToMap(section.get("state"), true);
+ if (stateSection != null) {
+ properties = Map.of();
+ int internalId = MiscUtils.getAsInt(stateSection.getOrDefault("id", -1));
+ if (internalId < 0) {
+ TranslationManager.instance().log("warning.config.block.state.lack_real_id", path.toString(), id.toString());
+ return;
+ }
- // generate mod assets
- if (ConfigManager.generateModAssets()) {
+ Pair pair = parseAppearanceSection(pack, path, id, stateSection);
+ if (pair == null) return;
+
+ appearances = Map.of("default", pair.right());
+ Key internalBlockId = Key.of(CraftEngine.NAMESPACE, pair.left().value() + "_" + internalId);
+ int internalBlockRegistryId = MiscUtils.getAsInt(internalId2StateId.getOrDefault(internalBlockId, -1));
+ if (internalBlockRegistryId == -1) {
+ TranslationManager.instance().log("warning.config.block.state.invalid_real_state_id",
+ path.toString(),
+ id.toString(),
+ pair.left().value() + "_" + internalId,
+ String.valueOf(MiscUtils.getAsInt(registeredRealBlockSlots.get(pair.left()))-1)
+ );
+ return;
+ }
+ variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
+ } else {
+ // states
+ Map statesSection = MiscUtils.castToMap(section.get("states"), true);
+ if (statesSection == null) {
+ TranslationManager.instance().log("warning.config.block.lack_state", path.toString(), id.toString());
+ return;
+ }
+ // properties
+ Map propertySection = MiscUtils.castToMap(statesSection.get("properties"), true);
+ if (propertySection == null) {
+ TranslationManager.instance().log("warning.config.block.state.lack_properties", path.toString(), id.toString());
+ return;
+ }
+ properties = parseProperties(path, id, propertySection);
+ // appearance
+ Map appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true);
+ if (appearancesSection == null) {
+ TranslationManager.instance().log("warning.config.block.state.lack_appearances", path.toString(), id.toString());
+ return;
+ }
+ appearances = new HashMap<>();
+ Map tempTypeMap = new HashMap<>();
+ for (Map.Entry appearanceEntry : appearancesSection.entrySet()) {
+ if (appearanceEntry.getValue() instanceof Map, ?> appearanceSection) {
+ Pair pair = parseAppearanceSection(pack, path, id, MiscUtils.castToMap(appearanceSection, false));
+ if (pair == null) return;
+ appearances.put(appearanceEntry.getKey(), pair.right());
+ tempTypeMap.put(appearanceEntry.getKey(), pair.left());
+ }
+ }
+ // variants
+ Map variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true);
+ if (variantsSection == null) {
+ TranslationManager.instance().log("warning.config.block.state.lack_variants", path.toString(), id.toString());
+ return;
+ }
+ variants = new HashMap<>();
+ for (Map.Entry variantEntry : variantsSection.entrySet()) {
+ if (variantEntry.getValue() instanceof Map, ?> variantSection0) {
+ Map variantSection = MiscUtils.castToMap(variantSection0, false);
+ String variantName = variantEntry.getKey();
+ String appearance = (String) variantSection.get("appearance");
+ if (appearance == null) {
+ TranslationManager.instance().log("warning.config.block.state.variant.lack_appearance", path.toString(), id.toString(), variantName);
+ return;
+ }
+ if (!appearances.containsKey(appearance)) {
+ TranslationManager.instance().log("warning.config.block.state.variant.invalid_appearance", path.toString(), id.toString(), variantName, appearance);
+ return;
+ }
+ int internalId = MiscUtils.getAsInt(variantSection.getOrDefault("id", -1));
+ Key baseBlock = tempTypeMap.get(appearance);
+ Key internalBlockId = Key.of(CraftEngine.NAMESPACE, baseBlock.value() + "_" + internalId);
+ int internalBlockRegistryId = MiscUtils.getAsInt(internalId2StateId.getOrDefault(internalBlockId, -1));
+ if (internalBlockRegistryId == -1) {
+ TranslationManager.instance().log("warning.config.block.state.invalid_real_state_id",
+ path.toString(),
+ id.toString(),
+ internalBlockId.toString(),
+ String.valueOf(MiscUtils.getAsInt(registeredRealBlockSlots.getOrDefault(baseBlock, 1)) - 1)
+ );
+ return;
+ }
+ Map anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
+ variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
+ }
+ }
+ }
+
+ // create or get block holder
+ Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
+ ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
+ // create block
+ Map behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
+ BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable);
+
+ // bind appearance and real state
for (ImmutableBlockState state : block.variantProvider().states()) {
- Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
- this.modBlockStates.put(realBlockId, this.tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
+ ImmutableBlockState previous = stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
+ if (previous != null && !previous.isEmpty()) {
+ TranslationManager.instance().log("warning.config.block.state.bind_real_state", path.toString(), id.toString(), state.toString(), previous.toString());
+ continue;
+ }
+ stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
+ tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
+ appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId());
+ }
+
+ byId.put(id, block);
+ // generate mod assets
+ if (Config.generateModAssets()) {
+ for (ImmutableBlockState state : block.variantProvider().states()) {
+ Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
+ modBlockStates.put(realBlockId, tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
+ }
}
}
}
- private void bindAppearance(CustomBlock block) {
- for (ImmutableBlockState state : block.variantProvider().states()) {
- ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
- if (previous != null && !previous.isEmpty()) {
- this.plugin.logger().severe("[Fatal] Failed to bind real block state for " + state + ": the state is already occupied by " + previous);
- continue;
- }
- this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
- this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
- this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId());
- }
- }
-
- private Map> parseProperties(Path path, Map propertiesSection) {
+ private Map> parseProperties(Path path, Key id, Map propertiesSection) {
Map> properties = new HashMap<>();
for (Map.Entry entry : propertiesSection.entrySet()) {
if (entry.getValue() instanceof Map, ?> params) {
- Property> property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(params, false));
- properties.put(entry.getKey(), property);
+ try {
+ Property> property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(params, false));
+ properties.put(entry.getKey(), property);
+ } catch (Exception e) {
+ TranslationManager.instance().log("warning.config.block.state.invalid_property", path.toString(), id.toString(), entry.getKey(), e.getMessage());
+ }
} else {
- this.plugin.logger().warn(path, "Invalid property format: " + entry.getKey());
+ TranslationManager.instance().log("warning.config.block.state.invalid_property_structure", path.toString(), id.toString(), entry.getKey());
}
}
return properties;
}
@Nullable
- private Pair parseAppearanceSection(Path path, Map section, Key id) {
+ private Pair parseAppearanceSection(Pack pack, Path path, Key id, Map section) {
+ // require state non null
String vanillaStateString = (String) section.get("state");
- if (PreConditions.isNull(vanillaStateString,
- () -> this.plugin.logger().warn(path, "No block state found for: " + id))) return null;
- int vanillaStateRegistryId;
- try {
- vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString);
- } catch (BlockStateArrangeException e) {
- this.plugin.logger().warn(path, "Failed to load " + id + " - " + e.getMessage(), e);
+ if (vanillaStateString == null) {
+ TranslationManager.instance().log("warning.config.block.state.lack_state", path.toString(), id.toString());
return null;
}
+
+ // get its registry id
+ int vanillaStateRegistryId;
+ VanillaStateParseResult parseResult = parseVanillaStateRegistryId(vanillaStateString);
+ if (parseResult.success()) {
+ vanillaStateRegistryId = parseResult.result;
+ } else {
+ String[] args = new String[parseResult.args.length + 2];
+ args[0] = path.toString();
+ args[1] = id.toString();
+ System.arraycopy(parseResult.args, 0, args, 2, parseResult.args.length);
+ TranslationManager.instance().log(parseResult.reason(), args);
+ return null;
+ }
+
+ // check conflict
Key ifAny = this.tempRegistryIdConflictMap.get(vanillaStateRegistryId);
if (ifAny != null && !ifAny.equals(id)) {
- this.plugin.logger().warn(path, "Can't use " + BlockStateUtils.idToBlockState(vanillaStateRegistryId) + " as the base block for " + id + " because the state has already been used by " + ifAny);
+ TranslationManager.instance().log("warning.config.block.state.conflict", path.toString(), id.toString(), BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaStateRegistryId)).getAsString(), ifAny.toString());
return null;
}
+
+ // require models not to be null
Object models = section.getOrDefault("models", section.get("model"));
+ if (models == null) {
+ TranslationManager.instance().log("warning.config.block.state.no_model_set", path.toString(), id.toString());
+ return null;
+ }
+
List variants = new ArrayList<>();
if (models instanceof Map, ?> singleModelSection) {
- loadVariantModel(variants, MiscUtils.castToMap(singleModelSection, false));
+ loadVariantModel(pack, path, id, variants, MiscUtils.castToMap(singleModelSection, false));
} else if (models instanceof List> modelList) {
for (Object model : modelList) {
if (model instanceof Map,?> singleModelMap) {
- loadVariantModel(variants, MiscUtils.castToMap(singleModelMap, false));
+ loadVariantModel(pack, path, id, variants, MiscUtils.castToMap(singleModelMap, false));
}
}
- } else {
- this.plugin.logger().warn(path, "No model set for " + id);
- return null;
}
if (variants.isEmpty()) return null;
+
this.tempRegistryIdConflictMap.put(vanillaStateRegistryId, id);
String blockState = BlockStateUtils.idToBlockState(vanillaStateRegistryId).toString();
Key block = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}')));
@@ -543,9 +568,17 @@ public class BukkitBlockManager extends AbstractBlockManager {
return Pair.of(block, vanillaStateRegistryId);
}
- private void loadVariantModel(List variants, Map singleModelMap) {
+ private void loadVariantModel(Pack pack, Path path, Key id, List variants, Map singleModelMap) {
JsonObject json = new JsonObject();
String modelPath = (String) singleModelMap.get("path");
+ if (modelPath == null) {
+ TranslationManager.instance().log("warning.config.block.state.model.lack_path", path.toString(), id.toString());
+ return;
+ }
+ if (!ResourceLocation.isValid(modelPath)) {
+ TranslationManager.instance().log("warning.config.block.state.model.invalid_resource_location", path.toString(), id.toString(), modelPath);
+ return;
+ }
json.addProperty("model", modelPath);
if (singleModelMap.containsKey("x")) json.addProperty("x", MiscUtils.getAsInt(singleModelMap.get("x")));
if (singleModelMap.containsKey("y")) json.addProperty("y", MiscUtils.getAsInt(singleModelMap.get("y")));
@@ -553,57 +586,61 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (singleModelMap.containsKey("weight")) json.addProperty("weight", MiscUtils.getAsInt(singleModelMap.get("weight")));
Map generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true);
if (generationMap != null) {
- prepareModelGeneration(new ModelGeneration(Key.of(modelPath), generationMap));
+ prepareModelGeneration(path, id, new ModelGeneration(Key.of(modelPath), generationMap));
}
variants.add(json);
}
- private int parseVanillaStateRegistryId(String blockState) throws BlockStateArrangeException {
+ private VanillaStateParseResult parseVanillaStateRegistryId(String blockState) {
String[] split = blockState.split(":", 3);
- PreConditions.runIfTrue(split.length >= 4, () -> {
- throw new BlockStateArrangeException("Invalid vanilla block state: " + blockState);
- });
+ if (split.length >= 4) {
+ return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState});
+ }
int registryId;
- // minecraft:xxx:0
- // xxx:0
String stateOrId = split[split.length - 1];
boolean isId = !stateOrId.contains("[") && !stateOrId.contains("]");
if (isId) {
- if (split.length == 1) {
- throw new BlockStateArrangeException("Invalid vanilla block state: " + blockState);
- }
+ if (split.length == 1) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState});
Key block = split.length == 2 ? Key.of(split[0]) : Key.of(split[0], split[1]);
try {
int id = split.length == 2 ? Integer.parseInt(split[1]) : Integer.parseInt(split[2]);
- PreConditions.runIfTrue(id < 0, () -> {
- throw new BlockStateArrangeException("Invalid block state: " + blockState);
- });
+ if (id < 0) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState});
List arranger = this.blockAppearanceArranger.get(block);
- if (arranger == null) {
- throw new BlockStateArrangeException("No freed block state is available for block " + block);
- }
- if (id >= arranger.size()) {
- throw new BlockStateArrangeException(blockState + " is not a valid block state because " + id + " is outside of the range (0~" + (arranger.size() - 1) + ")");
- }
+ if (arranger == null) return VanillaStateParseResult.failure("warning.config.block.state.unavailable_state", new String[]{blockState});
+ if (id >= arranger.size()) return VanillaStateParseResult.failure("warning.config.block.state.invalid_vanilla_state_id", new String[]{blockState, String.valueOf(arranger.size() - 1)});
registryId = arranger.get(id);
} catch (NumberFormatException e) {
- throw new BlockStateArrangeException("Invalid block state: " + blockState);
+ return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState});
}
} else {
try {
BlockData blockData = Bukkit.createBlockData(blockState);
registryId = BlockStateUtils.blockDataToId(blockData);
+ if (!this.blockAppearanceMapper.containsKey(registryId)) {
+ return VanillaStateParseResult.failure("warning.config.block.state.unavailable_state", new String[]{blockState});
+ }
} catch (IllegalArgumentException e) {
- throw new BlockStateArrangeException("Invalid block state: " + blockState);
+ return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState});
}
}
- return registryId;
+ return VanillaStateParseResult.success(registryId);
+ }
+
+ public record VanillaStateParseResult(boolean success, int result, String reason, String[] args) {
+
+ public static VanillaStateParseResult success(int result) {
+ return new VanillaStateParseResult(true, result, null, null);
+ }
+
+ public static VanillaStateParseResult failure(String reason, String[] args) {
+ return new VanillaStateParseResult(false, -1, reason, args);
+ }
}
private void loadMappingsAndAdditionalBlocks() {
this.plugin.logger().info("Loading mappings.yml.");
File mappingFile = new File(plugin.dataFolderFile(), "mappings.yml");
- YamlDocument mappings = ConfigManager.instance().loadOrCreateYamlData("mappings.yml");
+ YamlDocument mappings = Config.instance().loadOrCreateYamlData("mappings.yml");
Map blockStateMappings = loadBlockStateMappings(mappings);
this.validateBlockStateMappings(mappingFile, blockStateMappings);
Map stateMap = new HashMap<>();
@@ -616,7 +653,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
- YamlDocument additionalYaml = ConfigManager.instance().loadOrCreateYamlData("additional-real-blocks.yml");
+ YamlDocument additionalYaml = Config.instance().loadOrCreateYamlData("additional-real-blocks.yml");
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
}
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java
index 2f79abc20..451c60f5e 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java
@@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
if (this.defaultBlockState != null) {
return this.defaultBlockState;
}
- Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(this.targetBlock);
+ Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock);
if (optionalCustomBlock.isPresent()) {
CustomBlock customBlock = optionalCustomBlock.get();
this.defaultBlockState = customBlock.defaultState().customBlockState().handle();
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java
index cb2651ba5..979d8aff0 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java
@@ -13,14 +13,13 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootContext;
+import net.momirealms.craftengine.core.loot.number.NumberProvider;
+import net.momirealms.craftengine.core.loot.number.NumberProviders;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.Tuple;
-import net.momirealms.craftengine.core.loot.number.NumberProvider;
-import net.momirealms.craftengine.core.loot.number.NumberProviders;
import net.momirealms.craftengine.core.util.context.ContextHolder;
-import net.momirealms.craftengine.core.util.context.ContextKey;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.Vec3i;
import net.momirealms.craftengine.shared.block.BlockBehavior;
@@ -29,7 +28,6 @@ import org.bukkit.World;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
-import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadLocalRandom;
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java
index baae1cf12..c5db7a6e3 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java
@@ -30,6 +30,7 @@ public class EntityDataValue {
public static final Object Serializers$OPTIONAL_BLOCK_POS;
public static final Object Serializers$DIRECTION;
public static final Object Serializers$OPTIONAL_UUID;
+ public static final Object Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE;
public static final Object Serializers$OPTIONAL_GLOBAL_POS;
public static final Object Serializers$COMPOUND_TAG;
public static final Object Serializers$VILLAGER_DATA;
@@ -64,7 +65,10 @@ public class EntityDataValue {
Serializers$BLOCK_POS = initSerializersByName("BLOCK_POS");
Serializers$OPTIONAL_BLOCK_POS = initSerializersByName("OPTIONAL_BLOCK_POS");
Serializers$DIRECTION = initSerializersByName("DIRECTION");
- Serializers$OPTIONAL_UUID = initSerializersByName("OPTIONAL_UUID");
+ if (!VersionHelper.isVersionNewerThan1_21_5()) Serializers$OPTIONAL_UUID = initSerializersByName("OPTIONAL_UUID");
+ else Serializers$OPTIONAL_UUID = null;
+ if (VersionHelper.isVersionNewerThan1_21_5()) Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = initSerializersByName("OPTIONAL_LIVING_ENTITY_REFERENCE");
+ else Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = null;
Serializers$OPTIONAL_GLOBAL_POS = initSerializersByName("OPTIONAL_GLOBAL_POS");
Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG");
Serializers$VILLAGER_DATA = initSerializersByName("VILLAGER_DATA");
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 c499b31c8..f34fe39e8 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
@@ -10,19 +10,21 @@ import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.furniture.*;
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.plugin.config.ConfigManager;
-import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
+import net.momirealms.craftengine.core.plugin.config.Config;
+import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
+import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
+import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataType;
-import org.incendo.cloud.suggestion.Suggestion;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector3f;
@@ -31,35 +33,35 @@ import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
-public class BukkitFurnitureManager implements FurnitureManager {
+public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_id"));
public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type"));
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_to_base_entity"));
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_vector"));
private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin;
-
- private final Map byId = new HashMap<>();
-
+ private final FurnitureParser furnitureParser;
private final Map furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
// Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener;
- // tick task
- private SchedulerTask tickTask;
- // Cached command suggestions
- private final List cachedSuggestions = new ArrayList<>();
public static BukkitFurnitureManager instance() {
return instance;
}
public BukkitFurnitureManager(BukkitCraftEngine plugin) {
+ instance = this;
this.plugin = plugin;
+ this.furnitureParser = new FurnitureParser();
this.furnitureEventListener = new FurnitureEventListener(this);
this.dismountListener = VersionHelper.isVersionNewerThan1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
- instance = this;
+ }
+
+ @Override
+ public Furniture place(CustomFurniture furniture, Vec3d vec3d, net.momirealms.craftengine.core.world.World world, AnchorType anchorType, boolean playSound) {
+ return this.place(furniture, new Location((World) world.platformWorld(), vec3d.x(), vec3d.y(), vec3d.z()), anchorType, playSound);
}
public LoadedFurniture place(CustomFurniture furniture, Location location, AnchorType anchorType, boolean playSound) {
@@ -77,151 +79,138 @@ public class BukkitFurnitureManager implements FurnitureManager {
SoundData data = furniture.settings().sounds().placeSound();
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch());
}
- return getLoadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
+ return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
}
@Override
- public void delayedLoad() {
- this.initSuggestions();
+ public ConfigSectionParser parser() {
+ return this.furnitureParser;
}
- @Override
- public void initSuggestions() {
- this.cachedSuggestions.clear();
- for (Key key : this.byId.keySet()) {
- this.cachedSuggestions.add(Suggestion.suggestion(key.toString()));
- }
- }
+ public class FurnitureParser implements ConfigSectionParser {
+ public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
- @Override
- public Collection cachedSuggestions() {
- return Collections.unmodifiableCollection(this.cachedSuggestions);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public void parseSection(Pack pack, Path path, Key id, Map section) {
- Map lootMap = MiscUtils.castToMap(section.get("loot"), true);
- Map settingsMap = MiscUtils.castToMap(section.get("settings"), true);
- Map placementMap = MiscUtils.castToMap(section.get("placement"), true);
- EnumMap placements = new EnumMap<>(AnchorType.class);
- if (placementMap == null) {
- throw new IllegalArgumentException("Missing required parameter 'placement' for furniture " + id);
+ @Override
+ public String[] sectionId() {
+ return CONFIG_SECTION_NAME;
}
- for (Map.Entry entry : placementMap.entrySet()) {
- // anchor type
- AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH));
- Map placementArguments = MiscUtils.castToMap(entry.getValue(), true);
+ @Override
+ public int loadingSequence() {
+ return LoadingSequence.FURNITURE;
+ }
- // furniture display elements
- List elements = new ArrayList<>();
- List