mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-28 03:19:14 +00:00
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}
|
||||
netty-codec-http=${netty_version}
|
||||
ahocorasick=${ahocorasick_version}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -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
|
||||
|
||||
|
||||
@@ -38,11 +38,11 @@ argument.parse.failure.aggregate.missing: "<red>Missing component '<arg:0>'</red
|
||||
argument.parse.failure.aggregate.failure: "<red>Invalid component '<arg:0>': <arg:1></red>"
|
||||
argument.parse.failure.either: "<red>Could not resolve <arg:1> or <arg:2> from '<arg:0>'</red>"
|
||||
argument.parse.failure.namedtextcolor: "<red>'<arg:0>' is not a named text color</red>"
|
||||
command.reload.config.success: "<white>Configs reloaded in <green><arg:0></green> ms.</white>"
|
||||
command.reload.config.success: "<white>Configs reloaded in <green><arg:0></green> ms.</white> <gray>(Async: <arg:1>ms | Sync: <arg:2>ms)</gray>"
|
||||
command.reload.config.failure: "<red>Config reload failed. Check console logs.</red>"
|
||||
command.reload.pack.success: "<white>Resource pack reloaded in <green><arg:0></green> ms.</white>"
|
||||
command.reload.pack.failure: "<red>Resource pack reload failed. Check console logs.</red>"
|
||||
command.reload.all.success: "<white>Reload completed in <green><arg:0></green> ms.</white>"
|
||||
command.reload.all.success: "<white>Reload completed in <green><arg:0></green> ms.</white> <gray>(Async: <arg:1>ms | Sync: <arg:2>ms | Pack: <arg:3>ms)</gray>"
|
||||
command.reload.all.failure: "<red>Reload failed. Check console logs.</red>"
|
||||
command.item.get.success: "<white>Got <arg:0> <arg:1></white>"
|
||||
command.item.get.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0>'></red>"
|
||||
@@ -52,4 +52,54 @@ command.item.give.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:
|
||||
command.search_recipe.not_found: "<red>No recipe found for this item</red>"
|
||||
command.search_usage.not_found: "<red>No usage found for this item</red>"
|
||||
command.search_recipe.no_item: "<red>Please hold an item before running this command</red>"
|
||||
command.search_usage.no_item: "<red>Please hold an item before running this command</red>"
|
||||
command.search_usage.no_item: "<red>Please hold an item before running this command</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>Item '<arg:0>' is not minecraft:totem_of_undying</red>"
|
||||
warning.config.image.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'.</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' should be no lower than 'ascent'.</yellow>"
|
||||
warning.config.image.no_file: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'file' argument.</yellow>"
|
||||
warning.config.image.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'file' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
warning.config.image.invalid_font_name: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'font' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
warning.config.image.lack_char: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'char' argument.</yellow>"
|
||||
warning.config.image.codepoint_in_use: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character[<arg:3>(<arg:4>)] in font <arg:2> that has been used by another image '<arg:5>'.</yellow>"
|
||||
warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grind.</yellow>"
|
||||
warning.config.image.file_not_exist: "<yellow>Issue found in file <arg:0> - PNG file '<arg:2>' not found for image '<arg:1>'.</yellow>"
|
||||
warning.config.recipe.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'.</yellow>"
|
||||
warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>"
|
||||
warning.config.template.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'.</yellow>"
|
||||
warning.config.vanilla_loot.type_not_exist: "<yellow>Issue found in file <arg:0> - 'type' not set for vanilla loot '<arg:1>'.</yellow>"
|
||||
warning.config.vanilla_loot.block.invalid_target: "<yellow>Issue found in file <arg:0> - Invalid block target [<arg:2>] in vanilla loot '<arg:1>'.</yellow>"
|
||||
warning.config.sound.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated sound '<arg:1>'.</yellow>"
|
||||
warning.config.jukebox_song.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated jukebox song '<arg:1>'.</yellow>"
|
||||
warning.config.furniture.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated furniture '<arg:1>'.</yellow>"
|
||||
warning.config.furniture.lack_placement: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'placement' argument.</yellow>"
|
||||
warning.config.furniture.element.lack_item: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'item' argument for one of its elements.</yellow>"
|
||||
warning.config.item.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'.</yellow>"
|
||||
warning.config.item.lack_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>"
|
||||
warning.config.item.invalid_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid material type '<arg:2>'.</yellow>"
|
||||
warning.config.item.bad_custom_model_data_value: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data [<arg:2>] that is too large. It's recommended to use a value lower than 16,777,216.</yellow>"
|
||||
warning.config.item.custom_model_data_conflict: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data [<arg:2>] that has been occupied by item '<arg:3>'</yellow>"
|
||||
warning.config.item.lack_model_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'custom-model-data' or 'item-model' argument.</yellow>"
|
||||
warning.config.block.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated block '<arg:1>'.</yellow>"
|
||||
warning.config.block.lack_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument.</yellow>"
|
||||
warning.config.block.state.lack_real_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'id' argument for 'state'.</yellow>"
|
||||
warning.config.block.state.lack_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument for 'state'.</yellow>"
|
||||
warning.config.block.state.lack_properties: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'properties' section for 'states'.</yellow>"
|
||||
warning.config.block.state.lack_appearances: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearances' section for 'states'.</yellow>"
|
||||
warning.config.block.state.lack_variants: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'variants' section for 'states'.</yellow>"
|
||||
warning.config.block.state.variant.lack_appearance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearance' argument for variant '<arg:2>'.</yellow>"
|
||||
warning.config.block.state.variant.invalid_appearance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has an error that the variant '<arg:2>' is using a non-existing appearance '<arg:3>'.</yellow>"
|
||||
warning.config.block.state.invalid_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid vanilla block state '<arg:2>'."
|
||||
warning.config.block.state.unavailable_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an unavailable vanilla block state '<arg:2>'."
|
||||
warning.config.block.state.invalid_vanilla_state_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'."
|
||||
warning.config.block.state.conflict: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that has been occupied by '<arg:3>'."
|
||||
warning.config.block.state.bind_real_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' failed to bind real block state for '<arg:2>' as the state has been occupied by '<arg:3>'.</yellow>"
|
||||
warning.config.block.state.invalid_property_structure: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has an invalid property structure '<arg:2>'."
|
||||
warning.config.block.state.invalid_property: "<yellow>Issue found in file <arg:0> - Failed to create property '<arg:2>' for block '<arg:1>': <arg:3>."
|
||||
warning.config.block.state.no_model_set: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'model' or 'models' argument.</yellow>"
|
||||
warning.config.block.state.invalid_real_state_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a real block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up.</yellow>"
|
||||
warning.config.block.state.model.lack_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'path' option for 'model'.</yellow>"
|
||||
warning.config.block.state.model.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has a 'path' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'</yellow>"
|
||||
warning.config.model.generation.texture.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a '<arg:2>' texture argument [<arg:3>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
warning.config.model.generation.parent.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
@@ -38,11 +38,11 @@ argument.parse.failure.aggregate.missing: "<red>Componente Faltante '<arg:0>'</r
|
||||
argument.parse.failure.aggregate.failure: "<red>Componente Invalido '<arg:0>': <arg:1></red>"
|
||||
argument.parse.failure.either: "<red>No se ha podido resolver <arg:1> o <arg:2> de '<arg:0>'</red>"
|
||||
argument.parse.failure.namedtextcolor: "<red>'<arg:0>' no es un color de texto con nombre</red>"
|
||||
command.reload.config.success: "<white>Recargado. Tomó <green><arg:0></green> ms.</white>"
|
||||
command.reload.config.success: "<white>Recargado. Tomó <green><arg:0></green> ms.</white> <gray>(Async: <arg:1>ms | Sync: <arg:2>ms)</gray>"
|
||||
command.reload.config.failure: "<red>Error al recargar la configuración. Por favor, revisa el registro de la consola.</red>"
|
||||
command.reload.pack.success: "<white>Paquete de recursos recargado. Tomó <green><arg:0></green> ms.</white>"
|
||||
command.reload.pack.failure: "<red>Error al recargar el paquete de recursos. Por favor, revisa el registro de la consola.</red>"
|
||||
command.reload.all.success: "<white>Todo recargado. Tomó <green><arg:0></green> ms.</white>"
|
||||
command.reload.all.success: "<white>Todo recargado. Tomó <green><arg:0></green> ms.</white> <gray>(Async: <arg:1>ms | Sync: <arg:2>ms | Pack: <arg:3>ms)</gray>"
|
||||
command.reload.all.failure: "<red>Error al recargar. Por favor, revisa el registro de la consola.</red>"
|
||||
command.item.get.success: "<white>Obtener <arg:0> <arg:1></white>"
|
||||
command.item.get.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0>'></red>"
|
||||
@@ -52,4 +52,5 @@ command.item.give.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:
|
||||
command.search_recipe.not_found: "<red>No se encontró ninguna receta para este objeto</red>"
|
||||
command.search_usage.not_found: "<red>No se encontró ningún uso para este objeto</red>"
|
||||
command.search_recipe.no_item: "<red>Por favor, sostén un objeto antes de ejecutar este comando</red>"
|
||||
command.search_usage.no_item: "<red>Por favor, sostén un objeto antes de ejecutar este comando</red>"
|
||||
command.search_usage.no_item: "<red>Por favor, sostén un objeto antes de ejecutar este comando</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>'<arg:0>' no es del tipo totem_of_undying</red>"
|
||||
@@ -38,12 +38,12 @@ argument.parse.failure.aggregate.missing: "<red>缺少组件 '<arg:0>'</red>"
|
||||
argument.parse.failure.aggregate.failure: "<red>无效的组件 '<arg:0>': <arg:1></red>"
|
||||
argument.parse.failure.either: "<red>无法从 '<arg:0>' 解析 <arg:1> 或 <arg:2></red>"
|
||||
argument.parse.failure.namedtextcolor: "<red>'<arg:0>' 不是颜色代码</red>"
|
||||
command.reload.config.success: "<white>重新加载配置完成. 耗时 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.config.failure: "<red>重新加载配置失败,请检查控制台日志。</red>"
|
||||
command.reload.config.success: "<white>重新加载配置完成. 耗时 <green><arg:0></green> 毫秒</white> <gray>(异步: <arg:1>ms | 同步: <arg:2>ms)</gray>"
|
||||
command.reload.config.failure: "<red>重新加载配置失败,请检查控制台日志</red>"
|
||||
command.reload.pack.success: "<white>资源包重新加载完成. 耗时 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.pack.failure: "<red>重新加载资源包失败,请检查控制台日志。</red>"
|
||||
command.reload.all.success: "<white>全部重新加载完成. 耗时 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.all.failure: "<red>重新加载失败,请检查控制台日志。</red>"
|
||||
command.reload.pack.failure: "<red>重新加载资源包失败,请检查控制台日志</red>"
|
||||
command.reload.all.success: "<white>全部重新加载完成. 耗时 <green><arg:0></green> 毫秒</white> <gray>(异步: <arg:1>ms | 同步: <arg:2>ms | 资源包: <arg:3>ms)</gray>"
|
||||
command.reload.all.failure: "<red>重新加载失败,请检查控制台日志</red>"
|
||||
command.item.get.success: "<white>获得<arg:0>个<arg:1></white>"
|
||||
command.item.get.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0>'></red>"
|
||||
command.item.give.success.single: "<lang:commands.give.success.single:'<arg:0>':'<arg:1>':'<arg:2>'>"
|
||||
@@ -52,4 +52,54 @@ command.item.give.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:
|
||||
command.search_recipe.not_found: "<red>找不到此物品的配方</red>"
|
||||
command.search_usage.not_found: "<red>找不到此物品的用途</red>"
|
||||
command.search_recipe.no_item: "<red>请手持物品后再执行此命令</red>"
|
||||
command.search_usage.no_item: "<red>请手持物品后再执行此命令</red>"
|
||||
command.search_usage.no_item: "<red>请手持物品后再执行此命令</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>'<arg:0>' 不是 totem_of_undying 类型</red>"
|
||||
warning.config.image.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'height' 高度参数</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度</yellow>"
|
||||
warning.config.image.no_file: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'file' 文件参数</yellow>"
|
||||
warning.config.image.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 的 'file' 参数 [<arg:2>] 包含非法字符,请参考资源路径规范: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</yellow>"
|
||||
warning.config.image.invalid_font_name: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 的 'font' 字体参数 [<arg:2>] 包含非法字符,请参考资源路径规范: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</yellow>"
|
||||
warning.config.image.lack_char: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'char' 字符参数</yellow>"
|
||||
warning.config.image.codepoint_in_use: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 使用的字体 <arg:2> 字符 [<arg:3>(<arg:4>)] 已被其他图片 '<arg:5>' 占用</yellow>"
|
||||
warning.config.image.invalid_codepoint_grid: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 的 'chars' 码位网格配置无效</yellow>"
|
||||
warning.config.image.file_not_exist: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 对应的PNG文件 <arg:2> 不存在</yellow>"
|
||||
warning.config.recipe.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 配方 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 中发现问题 - 未知的语言区域 '<arg:1>'</yellow>"
|
||||
warning.config.template.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 模板 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.vanilla_loot.type_not_exist: "<yellow>在文件 <arg:0> 中发现问题 - 原版战利品 '<arg:1>' 未设置 'type' 类型参数</yellow>"
|
||||
warning.config.vanilla_loot.block.invalid_target: "<yellow>在文件 <arg:0> 中发现问题 - 原版战利品 '<arg:1>' 中的方块目标 [<arg:2>] 无效</yellow>"
|
||||
warning.config.sound.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 音效 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.jukebox_song.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 唱片机歌曲 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.furniture.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 家具 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.furniture.lack_placement: "<yellow>在文件 <arg:0> 中发现问题 - 家具 '<arg:1>' 缺少必要的 'placement' 放置参数</yellow>"
|
||||
warning.config.furniture.element.lack_item: "<yellow>在文件 <arg:0> 中发现问题 - 家具 '<arg:1>' 的某个元素缺少必要的 'item' 物品参数</yellow>"
|
||||
warning.config.item.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.item.lack_material: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 缺少必要的 'material' 材料参数</yellow>"
|
||||
warning.config.item.invalid_material: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 使用了无效的材料类型 '<arg:2>'</yellow>"
|
||||
warning.config.item.bad_custom_model_data_value: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 的自定义模型数据值 [<arg:2>] 过大,建议使用低于16,777,216的值</yellow>"
|
||||
warning.config.item.custom_model_data_conflict: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 的自定义模型数据 [<arg:2>] 与物品 '<arg:3>' 发生冲突</yellow>"
|
||||
warning.config.item.lack_model_id: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 缺少必要的 'custom-model-data' 或 'item-model' 模型标识参数</yellow>"
|
||||
warning.config.block.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.block.lack_state: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 缺少必要的 'state' 状态参数</yellow>"
|
||||
warning.config.block.state.lack_real_id: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'state' 配置缺少必要的 'id' 标识参数</yellow>"
|
||||
warning.config.block.state.lack_state: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'state' 配置缺少必要的 'state' 状态参数</yellow>"
|
||||
warning.config.block.state.lack_properties: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'states' 配置缺少必要的 'properties' 属性配置</yellow>"
|
||||
warning.config.block.state.lack_appearances: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'states' 配置缺少必要的 'appearances' 外观配置</yellow>"
|
||||
warning.config.block.state.lack_variants: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'states' 配置缺少必要的 'variants' 变体配置</yellow>"
|
||||
warning.config.block.state.variant.lack_appearance: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 '<arg:2>' 变体配置缺少必要的 'appearance' 外观参数</yellow>"
|
||||
warning.config.block.state.variant.invalid_appearance: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 '<arg:2>' 变体引用了不存在的外观配置 '<arg:3>'</yellow>"
|
||||
warning.config.block.state.invalid_state: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 使用了无效的原版方块状态 '<arg:2>'</yellow>"
|
||||
warning.config.block.state.unavailable_state: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 使用了不可用的原版方块状态 '<arg:2>'</yellow>"
|
||||
warning.config.block.state.invalid_vanilla_state_id: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'</yellow>"
|
||||
warning.config.block.state.conflict: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 已被 '<arg:3>' 占用</yellow>"
|
||||
warning.config.block.state.bind_real_state: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 未能绑定真实方块状态 '<arg:2>',该状态已被 '<arg:3>' 占用</yellow>"
|
||||
warning.config.block.state.invalid_property_structure: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的属性结构 '<arg:2>' 配置无效</yellow>"
|
||||
warning.config.block.state.invalid_property: "<yellow>在文件 <arg:0> 中发现问题 - 无法为方块 '<arg:1>' 创建属性 '<arg:2>':<arg:3></yellow>"
|
||||
warning.config.block.state.no_model_set: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 缺少必要的 'model' 或 'models' 模型参数</yellow>"
|
||||
warning.config.block.state.invalid_real_state_id: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 使用的真实方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'。若槽位已用尽,请考虑在 additional-real-blocks.yml 中添加更多真实状态</yellow>"
|
||||
warning.config.block.state.model.lack_path: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'model' 配置缺少必要的 'path' 路径参数</yellow>"
|
||||
warning.config.block.state.model.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 方块 '<arg:1>' 的 'path' 路径参数 [<arg:2>] 包含非法字符,请参考资源路径规范: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</yellow>"
|
||||
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 中发现问题 - 无法为 '<arg:1>' 生成模型,多个配置尝试用相同路径 '<arg:2>' 生成不同的JSON模型</yellow>"
|
||||
warning.config.model.generation.texture.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 的 '<arg:2>' 纹理参数 [<arg:3>] 包含非法字符,请参考资源路径规范: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</yellow>"
|
||||
warning.config.model.generation.parent.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 的父模型参数 [<arg:2>] 包含非法字符,请参考资源路径规范: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</yellow>"
|
||||
@@ -38,11 +38,11 @@ argument.parse.failure.aggregate.missing: "<red>缺少元件 '<arg:0>'</red>"
|
||||
argument.parse.failure.aggregate.failure: "<red>無效的元件 '<arg:0>': <arg:1></red>"
|
||||
argument.parse.failure.either: "<red>無法從 '<arg:0>' 解析 <arg:1> 或 <arg:2></red>"
|
||||
argument.parse.failure.namedtextcolor: "<red>'<arg:0>' 不是顏色代碼</red>"
|
||||
command.reload.config.success: "<white>重新加載配置完成. 耗時 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.config.success: "<white>重新加載配置完成. 耗時 <green><arg:0></green> 毫秒</white> <gray>(非同步: <arg:1>ms | 同步: <arg:2>ms)</gray>"
|
||||
command.reload.config.failure: "<red>重新加載配置失敗,請檢查控制台日誌。</red>"
|
||||
command.reload.pack.success: "<white>資源包重新加載完成. 耗時 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.pack.failure: "<red>重新加載資源包失敗,請檢查控制台日誌。</red>"
|
||||
command.reload.all.success: "<white>全部重新加載完成. 耗時 <green><arg:0></green> 毫秒</white>"
|
||||
command.reload.all.success: "<white>全部重新加載完成. 耗時 <green><arg:0></green> 毫秒</white> <gray>(非同步: <arg:1>ms | 同步: <arg:2>ms | 資源包: <arg:3>ms)</gray>"
|
||||
command.reload.all.failure: "<red>重新加載失敗,請檢查控制台日誌。</red>"
|
||||
command.item.get.success: "<white>獲得<arg:0>個<arg:1></white>"
|
||||
command.item.get.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0>'></red>"
|
||||
@@ -52,4 +52,5 @@ command.item.give.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:
|
||||
command.search_recipe.not_found: "<red>找不到此物品的配方</red>"
|
||||
command.search_usage.not_found: "<red>找不到此物品的用途</red>"
|
||||
command.search_recipe.no_item: "<red>執行此命令前請手持物品</red>"
|
||||
command.search_usage.no_item: "<red>執行此命令前請手持物品</red>"
|
||||
command.search_usage.no_item: "<red>執行此命令前請手持物品</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>'<arg:0>' 不是 totem_of_undying 類型</red>"
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Integer, Key> 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<Key, CustomBlock> id2CraftEngineBlocks = new HashMap<>();
|
||||
private final ImmutableBlockState[] stateId2ImmutableBlockStates;
|
||||
|
||||
protected final ImmutableBlockState[] stateId2ImmutableBlockStates;
|
||||
// Minecraft objects
|
||||
// Cached new blocks $ holders
|
||||
private ImmutableMap<Key, Integer> internalId2StateId;
|
||||
@@ -83,25 +83,21 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
private final Map<Key, Map<String, JsonElement>> blockStateOverrides = new HashMap<>();
|
||||
// for mod, real block id -> state models
|
||||
private final Map<Key, JsonElement> modBlockStates = new HashMap<>();
|
||||
// Cached command suggestions
|
||||
private final List<Suggestion> cachedSuggestions = new ArrayList<>();
|
||||
// Cached Namespace
|
||||
private final Set<String> 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<Key, Map<String, JsonElement>> blockOverrides() {
|
||||
return Collections.unmodifiableMap(this.blockStateOverrides);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key, CustomBlock> blocks() {
|
||||
return Collections.unmodifiableMap(this.id2CraftEngineBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CustomBlock> getBlock(Key key) {
|
||||
return Optional.ofNullable(this.id2CraftEngineBlocks.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Suggestion> cachedSuggestions() {
|
||||
return Collections.unmodifiableCollection(this.cachedSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSuggestions() {
|
||||
this.cachedSuggestions.clear();
|
||||
this.namespacesInUse.clear();
|
||||
Set<String> 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<String> namespacesInUse() {
|
||||
return Collections.unmodifiableSet(namespacesInUse);
|
||||
}
|
||||
|
||||
public ImmutableMap<Key, List<Integer>> blockAppearanceArranger() {
|
||||
return blockAppearanceArranger;
|
||||
}
|
||||
@@ -368,162 +331,224 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
// read block settings
|
||||
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
|
||||
// read loot table
|
||||
LootTable<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
|
||||
// read states
|
||||
Map<String, Property<?>> properties;
|
||||
Map<String, Integer> appearances;
|
||||
Map<String, VariantState> variants;
|
||||
Map<String, Object> 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<Key, Integer> 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<String, Object> statesSection = MiscUtils.castToMap(section.get("states"), true);
|
||||
if (statesSection == null) {
|
||||
plugin.logger().warn(path, "No states configured for block " + id);
|
||||
return;
|
||||
}
|
||||
Map<String, Object> 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<String, Object> 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<String, Key> tempTypeMap = new HashMap<>();
|
||||
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
|
||||
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
|
||||
Pair<Key, Integer> 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<String, Object> 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<String, Object> variantEntry : variantsSection.entrySet()) {
|
||||
if (variantEntry.getValue() instanceof Map<?, ?> variantSection0) {
|
||||
Map<String, Object> 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<String, Object> 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<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
|
||||
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
|
||||
// create block
|
||||
Map<String, Object> 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<String, Object> 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<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
|
||||
// read states
|
||||
Map<String, Property<?>> properties;
|
||||
Map<String, Integer> appearances;
|
||||
Map<String, VariantState> variants;
|
||||
Map<String, Object> 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<Key, Integer> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Key> tempTypeMap = new HashMap<>();
|
||||
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
|
||||
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
|
||||
Pair<Key, Integer> 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<String, Object> 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<String, Object> variantEntry : variantsSection.entrySet()) {
|
||||
if (variantEntry.getValue() instanceof Map<?, ?> variantSection0) {
|
||||
Map<String, Object> 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<String, Object> 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<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
|
||||
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
|
||||
// create block
|
||||
Map<String, Object> 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<String, Property<?>> parseProperties(Path path, Map<String, Object> propertiesSection) {
|
||||
private Map<String, Property<?>> parseProperties(Path path, Key id, Map<String, Object> propertiesSection) {
|
||||
Map<String, Property<?>> properties = new HashMap<>();
|
||||
for (Map.Entry<String, Object> 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<Key, Integer> parseAppearanceSection(Path path, Map<String, Object> section, Key id) {
|
||||
private Pair<Key, Integer> parseAppearanceSection(Pack pack, Path path, Key id, Map<String, Object> 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<JsonObject> 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<JsonObject> variants, Map<String, Object> singleModelMap) {
|
||||
private void loadVariantModel(Pack pack, Path path, Key id, List<JsonObject> variants, Map<String, Object> 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<String, Object> 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<Integer> 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<String, String> blockStateMappings = loadBlockStateMappings(mappings);
|
||||
this.validateBlockStateMappings(mappingFile, blockStateMappings);
|
||||
Map<Integer, String> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
if (this.defaultBlockState != null) {
|
||||
return this.defaultBlockState;
|
||||
}
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().getBlock(this.targetBlock);
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock);
|
||||
if (optionalCustomBlock.isPresent()) {
|
||||
CustomBlock customBlock = optionalCustomBlock.get();
|
||||
this.defaultBlockState = customBlock.defaultState().customBlockState().handle();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<Key, CustomFurniture> byId = new HashMap<>();
|
||||
|
||||
private final FurnitureParser furnitureParser;
|
||||
private final Map<Integer, LoadedFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
|
||||
private final Map<Integer, LoadedFurniture> 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<Suggestion> 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<Suggestion> cachedSuggestions() {
|
||||
return Collections.unmodifiableCollection(this.cachedSuggestions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
Map<String, Object> lootMap = MiscUtils.castToMap(section.get("loot"), true);
|
||||
Map<String, Object> settingsMap = MiscUtils.castToMap(section.get("settings"), true);
|
||||
Map<String, Object> placementMap = MiscUtils.castToMap(section.get("placement"), true);
|
||||
EnumMap<AnchorType, CustomFurniture.Placement> 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<String, Object> entry : placementMap.entrySet()) {
|
||||
// anchor type
|
||||
AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH));
|
||||
Map<String, Object> placementArguments = MiscUtils.castToMap(entry.getValue(), true);
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.FURNITURE;
|
||||
}
|
||||
|
||||
// furniture display elements
|
||||
List<FurnitureElement> elements = new ArrayList<>();
|
||||
List<Map<String, Object>> elementConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("elements", List.of());
|
||||
for (Map<String, Object> element : elementConfigs) {
|
||||
String key = (String) element.get("item");
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Missing required parameter 'item' for furniture " + id);
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
if (byId.containsKey(id)) {
|
||||
TranslationManager.instance().log("warning.config.furniture.duplicated", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> lootMap = MiscUtils.castToMap(section.get("loot"), true);
|
||||
Map<String, Object> settingsMap = MiscUtils.castToMap(section.get("settings"), true);
|
||||
Map<String, Object> placementMap = MiscUtils.castToMap(section.get("placement"), true);
|
||||
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
|
||||
if (placementMap == null) {
|
||||
TranslationManager.instance().log("warning.config.furniture.lack_placement", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : placementMap.entrySet()) {
|
||||
// anchor type
|
||||
AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH));
|
||||
Map<String, Object> placementArguments = MiscUtils.castToMap(entry.getValue(), true);
|
||||
|
||||
// furniture display elements
|
||||
List<FurnitureElement> elements = new ArrayList<>();
|
||||
List<Map<String, Object>> elementConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("elements", List.of());
|
||||
for (Map<String, Object> element : elementConfigs) {
|
||||
String key = (String) element.get("item");
|
||||
if (key == null) {
|
||||
TranslationManager.instance().log("warning.config.furniture.element.lack_item", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
ItemDisplayContext transform = ItemDisplayContext.valueOf(element.getOrDefault("transform", "NONE").toString().toUpperCase(Locale.ENGLISH));
|
||||
Billboard billboard = Billboard.valueOf(element.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH));
|
||||
FurnitureElement furnitureElement = new BukkitFurnitureElement(Key.of(key), billboard, transform,
|
||||
MiscUtils.getVector3f(element.getOrDefault("scale", "1")),
|
||||
MiscUtils.getVector3f(element.getOrDefault("translation", "0")),
|
||||
MiscUtils.getVector3f(element.getOrDefault("position", "0")),
|
||||
MiscUtils.getQuaternionf(element.getOrDefault("rotation", "0"))
|
||||
);
|
||||
elements.add(furnitureElement);
|
||||
}
|
||||
|
||||
// add colliders
|
||||
List<Collider> colliders = new ArrayList<>();
|
||||
|
||||
// external model providers
|
||||
Optional<ExternalModel> externalModel;
|
||||
if (placementArguments.containsKey("model-engine")) {
|
||||
externalModel = Optional.of(new ModelEngineModel(placementArguments.get("model-engine").toString()));
|
||||
} else if (placementArguments.containsKey("better-model")) {
|
||||
externalModel = Optional.of(new BetterModelModel(placementArguments.get("better-model").toString()));
|
||||
} else {
|
||||
externalModel = Optional.empty();
|
||||
}
|
||||
|
||||
// add hitboxes
|
||||
List<Map<String, Object>> hitboxConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("hitboxes", List.of());
|
||||
List<HitBox> hitboxes = new ArrayList<>();
|
||||
for (Map<String, Object> config : hitboxConfigs) {
|
||||
HitBox hitBox = HitBoxTypes.fromMap(config);
|
||||
hitboxes.add(hitBox);
|
||||
hitBox.optionalCollider().ifPresent(colliders::add);
|
||||
}
|
||||
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
|
||||
hitboxes.add(InteractionHitBox.DEFAULT);
|
||||
}
|
||||
|
||||
// rules
|
||||
Map<String, Object> ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true);
|
||||
if (ruleSection != null) {
|
||||
RotationRule rotationRule = Optional.ofNullable((String) ruleSection.get("rotation"))
|
||||
.map(it -> RotationRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
|
||||
.orElse(RotationRule.ANY);
|
||||
AlignmentRule alignmentRule = Optional.ofNullable((String) ruleSection.get("alignment"))
|
||||
.map(it -> AlignmentRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
|
||||
.orElse(AlignmentRule.CENTER);
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
rotationRule,
|
||||
alignmentRule,
|
||||
externalModel
|
||||
));
|
||||
} else {
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
RotationRule.ANY,
|
||||
AlignmentRule.CENTER,
|
||||
externalModel
|
||||
));
|
||||
}
|
||||
ItemDisplayContext transform = ItemDisplayContext.valueOf(element.getOrDefault("transform", "NONE").toString().toUpperCase(Locale.ENGLISH));
|
||||
Billboard billboard = Billboard.valueOf(element.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH));
|
||||
FurnitureElement furnitureElement = new BukkitFurnitureElement(Key.of(key), billboard, transform,
|
||||
MiscUtils.getVector3f(element.getOrDefault("scale", "1")),
|
||||
MiscUtils.getVector3f(element.getOrDefault("translation", "0")),
|
||||
MiscUtils.getVector3f(element.getOrDefault("position", "0")),
|
||||
MiscUtils.getQuaternionf(element.getOrDefault("rotation", "0"))
|
||||
);
|
||||
elements.add(furnitureElement);
|
||||
}
|
||||
|
||||
// add colliders
|
||||
List<Collider> colliders = new ArrayList<>();
|
||||
// List<Map<String, Object>> colliderConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("colliders", List.of());
|
||||
// for (Map<String, Object> config : colliderConfigs) {
|
||||
// if (!config.containsKey("position")) {
|
||||
// colliders.add(new Collider(
|
||||
// (boolean) config.getOrDefault("can-be-hit-by-projectile", false),
|
||||
// MiscUtils.getVector3d(config.getOrDefault("point-1", "0")),
|
||||
// MiscUtils.getVector3d(config.getOrDefault("point-2", "0"))
|
||||
// ));
|
||||
// } else {
|
||||
// colliders.add(new Collider(
|
||||
// (boolean) config.getOrDefault("can-be-hit-by-projectile", false),
|
||||
// MiscUtils.getVector3f(config.getOrDefault("position", "0")),
|
||||
// MiscUtils.getAsFloat(config.getOrDefault("width", "1")),
|
||||
// MiscUtils.getAsFloat(config.getOrDefault("height", "1"))
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
CustomFurniture furniture = new CustomFurniture(
|
||||
id,
|
||||
FurnitureSettings.fromMap(settingsMap),
|
||||
placements,
|
||||
lootMap == null ? null : LootTable.fromMap(lootMap)
|
||||
);
|
||||
|
||||
// external model providers
|
||||
Optional<ExternalModel> externalModel;
|
||||
if (placementArguments.containsKey("model-engine")) {
|
||||
externalModel = Optional.of(new ModelEngineModel(placementArguments.get("model-engine").toString()));
|
||||
} else if (placementArguments.containsKey("better-model")) {
|
||||
externalModel = Optional.of(new BetterModelModel(placementArguments.get("better-model").toString()));
|
||||
} else {
|
||||
externalModel = Optional.empty();
|
||||
}
|
||||
|
||||
// add hitboxes
|
||||
List<Map<String, Object>> hitboxConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("hitboxes", List.of());
|
||||
List<HitBox> hitboxes = new ArrayList<>();
|
||||
for (Map<String, Object> config : hitboxConfigs) {
|
||||
HitBox hitBox = HitBoxTypes.fromMap(config);
|
||||
hitboxes.add(hitBox);
|
||||
hitBox.optionalCollider().ifPresent(colliders::add);
|
||||
}
|
||||
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
|
||||
hitboxes.add(InteractionHitBox.DEFAULT);
|
||||
}
|
||||
|
||||
// rules
|
||||
Map<String, Object> ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true);
|
||||
if (ruleSection != null) {
|
||||
RotationRule rotationRule = Optional.ofNullable((String) ruleSection.get("rotation"))
|
||||
.map(it -> RotationRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
|
||||
.orElse(RotationRule.ANY);
|
||||
AlignmentRule alignmentRule = Optional.ofNullable((String) ruleSection.get("alignment"))
|
||||
.map(it -> AlignmentRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
|
||||
.orElse(AlignmentRule.CENTER);
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
rotationRule,
|
||||
alignmentRule,
|
||||
externalModel
|
||||
));
|
||||
} else {
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
RotationRule.ANY,
|
||||
AlignmentRule.CENTER,
|
||||
externalModel
|
||||
));
|
||||
}
|
||||
byId.put(id, furniture);
|
||||
}
|
||||
|
||||
CustomFurniture furniture = new CustomFurniture(
|
||||
id,
|
||||
FurnitureSettings.fromMap(settingsMap),
|
||||
placements,
|
||||
lootMap == null ? null : LootTable.fromMap(lootMap)
|
||||
);
|
||||
|
||||
this.byId.put(id, furniture);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.bootstrap());
|
||||
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.bootstrap());
|
||||
this.tickTask = plugin.scheduler().sync().runRepeating(this::tick, 1, 1);
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
List<Entity> entities = world.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
@@ -230,18 +219,10 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.byId.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this.dismountListener);
|
||||
HandlerList.unregisterAll(this.furnitureEventListener);
|
||||
if (tickTask != null && !tickTask.cancelled()) {
|
||||
tickTask.cancel();
|
||||
}
|
||||
unload();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Entity vehicle = player.getVehicle();
|
||||
@@ -251,23 +232,20 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CustomFurniture> getFurniture(Key id) {
|
||||
return Optional.ofNullable(this.byId.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFurnitureRealEntity(int entityId) {
|
||||
return this.furnitureByRealEntityId.containsKey(entityId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LoadedFurniture getLoadedFurnitureByRealEntityId(int entityId) {
|
||||
@Override
|
||||
public LoadedFurniture loadedFurnitureByRealEntityId(int entityId) {
|
||||
return this.furnitureByRealEntityId.get(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public LoadedFurniture getLoadedFurnitureByEntityId(int entityId) {
|
||||
public LoadedFurniture loadedFurnitureByEntityId(int entityId) {
|
||||
return this.furnitureByEntityId.get(entityId);
|
||||
}
|
||||
|
||||
@@ -296,7 +274,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
||||
if (id == null) return;
|
||||
Key key = Key.of(id);
|
||||
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
|
||||
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
||||
if (optionalFurniture.isEmpty()) return;
|
||||
CustomFurniture customFurniture = optionalFurniture.get();
|
||||
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
|
||||
@@ -318,7 +296,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
||||
if (id == null) return;
|
||||
Key key = Key.of(id);
|
||||
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
|
||||
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
||||
if (optionalFurniture.isPresent()) {
|
||||
CustomFurniture customFurniture = optionalFurniture.get();
|
||||
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
|
||||
@@ -327,8 +305,8 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
return;
|
||||
}
|
||||
// Remove the entity if it's not a valid furniture
|
||||
if (ConfigManager.removeInvalidFurniture()) {
|
||||
if (ConfigManager.furnitureToRemove().isEmpty() || ConfigManager.furnitureToRemove().contains(id)) {
|
||||
if (Config.removeInvalidFurniture()) {
|
||||
if (Config.furnitureToRemove().isEmpty() || Config.furnitureToRemove().contains(id)) {
|
||||
entity.remove();
|
||||
}
|
||||
}
|
||||
@@ -375,7 +353,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
|
||||
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
|
||||
if (baseFurniture == null) return;
|
||||
vehicle.remove();
|
||||
LoadedFurniture furniture = getLoadedFurnitureByRealEntityId(baseFurniture);
|
||||
LoadedFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture);
|
||||
if (furniture == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.ArrayUtils;
|
||||
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.World;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.entity.*;
|
||||
@@ -24,7 +27,7 @@ import org.joml.Vector3f;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
public class LoadedFurniture {
|
||||
public class LoadedFurniture implements Furniture {
|
||||
private final Key id;
|
||||
private final CustomFurniture furniture;
|
||||
private final AnchorType anchorType;
|
||||
@@ -144,6 +147,7 @@ public class LoadedFurniture {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeColliders() {
|
||||
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
|
||||
for (CollisionEntity entity : this.collisionEntities) {
|
||||
@@ -161,6 +165,16 @@ public class LoadedFurniture {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d position() {
|
||||
return new Vec3d(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public World world() {
|
||||
return new BukkitWorld(this.location.getWorld());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Location location() {
|
||||
return this.location.clone();
|
||||
@@ -175,10 +189,12 @@ public class LoadedFurniture {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return baseEntity().isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
@@ -197,6 +213,7 @@ public class LoadedFurniture {
|
||||
this.seats.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroySeats() {
|
||||
for (Entity entity : this.seats) {
|
||||
entity.remove();
|
||||
@@ -204,6 +221,7 @@ public class LoadedFurniture {
|
||||
this.seats.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Seat> findFirstAvailableSeat(int targetEntityId) {
|
||||
HitBox hitbox = hitBoxes.get(targetEntityId);
|
||||
if (hitbox == null) return Optional.empty();
|
||||
@@ -216,14 +234,12 @@ public class LoadedFurniture {
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOccupiedSeat(Vector3f seat) {
|
||||
return this.occupiedSeats.remove(seat);
|
||||
}
|
||||
|
||||
public boolean removeOccupiedSeat(Seat seat) {
|
||||
return this.removeOccupiedSeat(seat.offset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryOccupySeat(Seat seat) {
|
||||
if (this.occupiedSeats.contains(seat.offset())) {
|
||||
return false;
|
||||
@@ -232,10 +248,12 @@ public class LoadedFurniture {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return this.baseEntity().getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int baseEntityId() {
|
||||
return this.baseEntityId;
|
||||
}
|
||||
@@ -254,31 +272,29 @@ public class LoadedFurniture {
|
||||
return this.collisionEntities;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AnchorType anchorType() {
|
||||
@Override
|
||||
public @NotNull AnchorType anchorType() {
|
||||
return this.anchorType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Key id() {
|
||||
@Override
|
||||
public @NotNull Key id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CustomFurniture config() {
|
||||
@Override
|
||||
public @NotNull CustomFurniture config() {
|
||||
return this.furniture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExternalModel() {
|
||||
return hasExternalModel;
|
||||
}
|
||||
|
||||
public CustomFurniture.Placement placement() {
|
||||
return this.placement;
|
||||
}
|
||||
|
||||
public Map<Integer, HitBox> hitBoxes() {
|
||||
return this.hitBoxes;
|
||||
@Override
|
||||
public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) {
|
||||
spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat);
|
||||
}
|
||||
|
||||
public void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
|
||||
ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues);
|
||||
ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues);
|
||||
// ShulkerData.AttachFace.addEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(direction), this.cachedShulkerValues);
|
||||
// ShulkerData.AttachFace.addEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(direction), this.cachedShulkerValues);
|
||||
ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues);
|
||||
ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues);
|
||||
ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // 无ai
|
||||
@@ -119,16 +119,27 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
public void addSpawnPackets(int[] entityIds, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
try {
|
||||
double originalY = y + offset.y;
|
||||
double integerPart = Math.floor(originalY);
|
||||
double fractionalPart = originalY - integerPart;
|
||||
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), false);
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityIds[1], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), false);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]), false);
|
||||
if (originalY != processedY) {
|
||||
double deltaY = originalY - processedY;
|
||||
short ya = (short) (deltaY * 8192);
|
||||
packets.accept(Reflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance(
|
||||
entityIds[1], (short) 0, ya, (short) 0, true
|
||||
), false);
|
||||
}
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer<?>) (o) -> {});
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, scale);
|
||||
|
||||
@@ -4,9 +4,9 @@ import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.font.AbstractImageManager;
|
||||
import net.momirealms.craftengine.core.font.ImageManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.font.AbstractFontManager;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -19,11 +19,11 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BukkitImageManager extends AbstractImageManager implements Listener {
|
||||
public class BukkitFontManager extends AbstractFontManager implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final Object serializer;
|
||||
|
||||
public BukkitImageManager(BukkitCraftEngine plugin) {
|
||||
public BukkitFontManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
try {
|
||||
@@ -48,22 +48,22 @@ public class BukkitImageManager extends AbstractImageManager implements Listener
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public void onChat(AsyncChatDecorateEvent event) {
|
||||
if (!ConfigManager.filterChat()) return;
|
||||
if (!Config.filterChat()) return;
|
||||
this.processChatEvent(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public void onChatCommand(AsyncChatCommandDecorateEvent event) {
|
||||
if (!ConfigManager.filterChat()) return;
|
||||
if (!Config.filterChat()) return;
|
||||
this.processChatEvent(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onCommand(PlayerCommandPreprocessEvent event) {
|
||||
if (!ConfigManager.filterCommand()) return;
|
||||
if (!Config.filterCommand()) return;
|
||||
if (!this.isDefaultFontInUse()) return;
|
||||
if (event.getPlayer().hasPermission(ImageManager.BYPASS_COMMAND)) {
|
||||
if (event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) {
|
||||
return;
|
||||
}
|
||||
runIfContainsIllegalCharacter(event.getMessage(), event::setMessage);
|
||||
@@ -74,7 +74,7 @@ public class BukkitImageManager extends AbstractImageManager implements Listener
|
||||
Player player = event.player();
|
||||
if (player == null) return;
|
||||
if (!this.isDefaultFontInUse()) return;
|
||||
if (player.hasPermission(ImageManager.BYPASS_CHAT)) {
|
||||
if (player.hasPermission(FontManager.BYPASS_CHAT)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -101,7 +101,7 @@ public class BukkitImageManager extends AbstractImageManager implements Listener
|
||||
boolean hasIllegal = false;
|
||||
for (int i = 0; i < codepoints.length; i++) {
|
||||
int codepoint = codepoints[i];
|
||||
if (!isIllegalCharacter(codepoint)) {
|
||||
if (!isIllegalCodepoint(codepoint)) {
|
||||
newCodepoints[i] = codepoint;
|
||||
} else {
|
||||
newCodepoints[i] = '*';
|
||||
@@ -18,13 +18,17 @@ import net.momirealms.craftengine.core.item.modifier.CustomModelDataModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModelModifier;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
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.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.*;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.select.ChargeTypeSelectProperty;
|
||||
import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectProperty;
|
||||
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;
|
||||
@@ -59,6 +63,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final ItemEventListener itemEventListener;
|
||||
private final DebugStickListener debugStickListener;
|
||||
private final ItemParser itemParser;
|
||||
|
||||
public BukkitItemManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
@@ -66,6 +71,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
this.factory = BukkitItemFactory.create(plugin);
|
||||
this.itemEventListener = new ItemEventListener(plugin);
|
||||
this.debugStickListener = new DebugStickListener(plugin);
|
||||
this.itemParser = new ItemParser();
|
||||
this.registerAllVanillaItems();
|
||||
instance = this;
|
||||
}
|
||||
@@ -120,6 +126,11 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
HandlerList.unregisterAll(this.debugStickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSectionParser parser() {
|
||||
return this.itemParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildCustomItemStack(Key id, Player player) {
|
||||
return Optional.ofNullable(customItems.get(id)).map(it -> it.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1)).orElse(null);
|
||||
@@ -176,173 +187,209 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
return wrapped.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
// just register for recipes
|
||||
Holder.Reference<Key> holder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(id)
|
||||
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
|
||||
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
|
||||
public class ItemParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"};
|
||||
|
||||
String materialStringId = (String) section.get("material");
|
||||
Material material = MaterialUtils.getMaterial(materialStringId);
|
||||
if (material == null) {
|
||||
plugin.logger().warn(path, "material " + Optional.ofNullable(materialStringId).map(it -> it + " ").orElse("") + "does not exist for item " + id);
|
||||
return;
|
||||
}
|
||||
Key materialId = Key.of(material.getKey().namespace(), material.getKey().value());
|
||||
|
||||
int customModelData = MiscUtils.getAsInt(section.getOrDefault("custom-model-data", 0));
|
||||
Key itemModelKey = null;
|
||||
|
||||
CustomItem.Builder<ItemStack> itemBuilder = BukkitCustomItem.builder().id(id).material(materialId);
|
||||
itemBuilder.modifier(new IdModifier<>(id));
|
||||
|
||||
boolean hasItemModelSection = section.containsKey("item-model");
|
||||
|
||||
// To get at least one model provider
|
||||
// Sets some basic model info
|
||||
if (customModelData != 0) {
|
||||
itemBuilder.modifier(new CustomModelDataModifier<>(customModelData));
|
||||
}
|
||||
// Requires the item to have model before apply item-model
|
||||
else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
// check server version here because components require 1.21.2+
|
||||
// customize or use the id
|
||||
itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString());
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
if (hasItemModelSection) {
|
||||
itemModelKey = Key.from(section.get("item-model").toString());
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.ITEM;
|
||||
}
|
||||
|
||||
// Get item behaviors
|
||||
Object behaviorConfig = section.get("behavior");
|
||||
if (behaviorConfig instanceof List<?>) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> behavior = (List<Map<String, Object>>) behaviorConfig;
|
||||
List<ItemBehavior> behaviors = new ArrayList<>();
|
||||
for (Map<String, Object> behaviorMap : behavior) {
|
||||
behaviors.add(ItemBehaviors.fromMap(pack, path, id, behaviorMap));
|
||||
}
|
||||
itemBuilder.behavior(behaviors);
|
||||
} else if (behaviorConfig instanceof Map<?, ?>) {
|
||||
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.get("behavior"), true);
|
||||
if (behaviorSection != null) {
|
||||
itemBuilder.behavior(ItemBehaviors.fromMap(pack, path, id, behaviorSection));
|
||||
}
|
||||
}
|
||||
|
||||
// Get item data
|
||||
Map<String, Object> dataSection = MiscUtils.castToMap(section.get("data"), true);
|
||||
if (dataSection != null) {
|
||||
for (Map.Entry<String, Object> dataEntry : dataSection.entrySet()) {
|
||||
Optional.ofNullable(dataFunctions.get(dataEntry.getKey())).ifPresent(function -> {
|
||||
try {
|
||||
itemBuilder.modifier(function.apply(dataEntry.getValue()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.logger().warn("Invalid data format", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (section.containsKey("settings")) {
|
||||
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), false);
|
||||
itemBuilder.settings(ItemSettings.fromMap(settings));
|
||||
}
|
||||
|
||||
CustomItem<ItemStack> customItem = itemBuilder.build();
|
||||
this.customItems.put(id, customItem);
|
||||
this.cachedSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
|
||||
// post process
|
||||
// register tags
|
||||
Set<Key> tags = customItem.settings().tags();
|
||||
for (Key tag : tags) {
|
||||
this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(holder);
|
||||
}
|
||||
|
||||
// create trims
|
||||
EquipmentGeneration equipment = customItem.settings().equipment();
|
||||
if (equipment != null) {
|
||||
EquipmentData modern = equipment.modernData();
|
||||
// 1.21.2+
|
||||
if (modern != null) {
|
||||
this.equipmentsToGenerate.add(equipment);
|
||||
}
|
||||
// TODO 1.20
|
||||
}
|
||||
|
||||
// add it to category
|
||||
if (section.containsKey("category")) {
|
||||
this.plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList());
|
||||
}
|
||||
|
||||
// model part, can be null
|
||||
// but if it exists, either custom model data or item model should be configured
|
||||
Map<String, Object> modelSection = MiscUtils.castToMap(section.get("model"), true);
|
||||
if (modelSection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasModel = false;
|
||||
if (customModelData != 0) {
|
||||
hasModel= true;
|
||||
// use custom model data
|
||||
// check conflict
|
||||
Map<Integer, Key> conflict = this.cmdConflictChecker.computeIfAbsent(materialId, k -> new HashMap<>());
|
||||
if (conflict.containsKey(customModelData)) {
|
||||
plugin.logger().warn(path, "Failed to create model for " + id + " because custom-model-data " + customModelData + " already occupied by item " + conflict.get(customModelData).toString());
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
if (customItems.containsKey(id)) {
|
||||
TranslationManager.instance().log("warning.config.item.duplicated", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
conflict.put(customModelData, id);
|
||||
// just register for recipes
|
||||
Holder.Reference<Key> holder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(id)
|
||||
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
|
||||
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
|
||||
|
||||
// Parse models
|
||||
ItemModel model = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : model.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
String materialStringId = (String) section.get("material");
|
||||
if (materialStringId == null) {
|
||||
TranslationManager.instance().log("warning.config.item.lack_material", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigManager.packMaxVersion() > 21.39f) {
|
||||
TreeMap<Integer, ItemModel> map = this.modernOverrides.computeIfAbsent(materialId, k -> new TreeMap<>());
|
||||
map.put(customModelData, model);
|
||||
Material material = MaterialUtils.getMaterial(materialStringId);
|
||||
if (material == null) {
|
||||
TranslationManager.instance().log("warning.config.item.invalid_material", path.toString(), id.toString(), materialStringId);
|
||||
return;
|
||||
}
|
||||
|
||||
Key materialId = Key.of(material.getKey().namespace(), material.getKey().value());
|
||||
int customModelData = MiscUtils.getAsInt(section.getOrDefault("custom-model-data", 0));
|
||||
Key itemModelKey = null;
|
||||
|
||||
CustomItem.Builder<ItemStack> itemBuilder = BukkitCustomItem.builder().id(id).material(materialId);
|
||||
itemBuilder.modifier(new IdModifier<>(id));
|
||||
|
||||
boolean hasItemModelSection = section.containsKey("item-model");
|
||||
|
||||
// To get at least one model provider
|
||||
// Sets some basic model info
|
||||
if (customModelData != 0) {
|
||||
itemBuilder.modifier(new CustomModelDataModifier<>(customModelData));
|
||||
}
|
||||
// Requires the item to have model before apply item-model
|
||||
else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
// check server version here because components require 1.21.2+
|
||||
// customize or use the id
|
||||
itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString());
|
||||
if (ResourceLocation.isValid(itemModelKey.toString())) {
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
} else {
|
||||
itemModelKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigManager.packMinVersion() < 21.39f) {
|
||||
List<LegacyOverridesModel> legacyOverridesModels = new ArrayList<>();
|
||||
processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, customModelData);
|
||||
TreeSet<LegacyOverridesModel> lom = this.legacyOverrides.computeIfAbsent(materialId, k -> new TreeSet<>());
|
||||
lom.addAll(legacyOverridesModels);
|
||||
}
|
||||
}
|
||||
if (itemModelKey != null) {
|
||||
hasModel = true;
|
||||
// use components
|
||||
ItemModel model = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : model.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
if (hasItemModelSection) {
|
||||
itemModelKey = Key.from(section.get("item-model").toString());
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
}
|
||||
|
||||
if (ConfigManager.packMaxVersion() > 21.39f) {
|
||||
this.modernItemModels1_21_4.put(itemModelKey, model);
|
||||
// Get item behaviors
|
||||
Object behaviorConfig = section.get("behavior");
|
||||
if (behaviorConfig instanceof List<?>) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> behavior = (List<Map<String, Object>>) behaviorConfig;
|
||||
List<ItemBehavior> behaviors = new ArrayList<>();
|
||||
for (Map<String, Object> behaviorMap : behavior) {
|
||||
behaviors.add(ItemBehaviors.fromMap(pack, path, id, behaviorMap));
|
||||
}
|
||||
itemBuilder.behavior(behaviors);
|
||||
} else if (behaviorConfig instanceof Map<?, ?>) {
|
||||
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.get("behavior"), true);
|
||||
if (behaviorSection != null) {
|
||||
itemBuilder.behavior(ItemBehaviors.fromMap(pack, path, id, behaviorSection));
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigManager.packMaxVersion() > 21.19f && ConfigManager.packMinVersion() < 21.39f) {
|
||||
List<LegacyOverridesModel> legacyOverridesModels = new ArrayList<>();
|
||||
processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, 0);
|
||||
if (legacyOverridesModels.isEmpty()) {
|
||||
plugin.logger().warn(path, "Can't convert " + id + "'s model to legacy format.");
|
||||
// Get item data
|
||||
Map<String, Object> dataSection = MiscUtils.castToMap(section.get("data"), true);
|
||||
if (dataSection != null) {
|
||||
for (Map.Entry<String, Object> dataEntry : dataSection.entrySet()) {
|
||||
Optional.ofNullable(dataFunctions.get(dataEntry.getKey())).ifPresent(function -> {
|
||||
try {
|
||||
itemBuilder.modifier(function.apply(dataEntry.getValue()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.logger().warn("Invalid data format", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (section.containsKey("settings")) {
|
||||
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), false);
|
||||
itemBuilder.settings(ItemSettings.fromMap(settings));
|
||||
}
|
||||
|
||||
CustomItem<ItemStack> customItem = itemBuilder.build();
|
||||
customItems.put(id, customItem);
|
||||
|
||||
// cache command suggestions
|
||||
cachedSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
if (material == Material.TOTEM_OF_UNDYING)
|
||||
cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
|
||||
// post process
|
||||
// register tags
|
||||
Set<Key> tags = customItem.settings().tags();
|
||||
for (Key tag : tags) {
|
||||
customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(holder);
|
||||
}
|
||||
|
||||
// create trims
|
||||
EquipmentGeneration equipment = customItem.settings().equipment();
|
||||
if (equipment != null) {
|
||||
EquipmentData modern = equipment.modernData();
|
||||
// 1.21.2+
|
||||
if (modern != null) {
|
||||
equipmentsToGenerate.add(equipment);
|
||||
}
|
||||
// TODO 1.20
|
||||
}
|
||||
|
||||
// add it to category
|
||||
if (section.containsKey("category")) {
|
||||
plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList());
|
||||
}
|
||||
|
||||
// model part, can be null
|
||||
// but if it exists, either custom model data or item model should be configured
|
||||
Map<String, Object> modelSection = MiscUtils.castToMap(section.get("model"), true);
|
||||
if (modelSection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasModel = false;
|
||||
if (customModelData != 0) {
|
||||
hasModel= true;
|
||||
// use custom model data
|
||||
// check conflict
|
||||
Map<Integer, Key> conflict = cmdConflictChecker.computeIfAbsent(materialId, k -> new HashMap<>());
|
||||
if (conflict.containsKey(customModelData)) {
|
||||
TranslationManager.instance().log("warning.config.item.custom_model_data_conflict", path.toString(), id.toString(), String.valueOf(customModelData), conflict.get(customModelData).toString());
|
||||
return;
|
||||
}
|
||||
legacyOverridesModels.sort(LegacyOverridesModel::compareTo);
|
||||
this.modernItemModels1_21_2.put(itemModelKey, legacyOverridesModels);
|
||||
|
||||
if (customModelData > 16_777_216) {
|
||||
TranslationManager.instance().log("warning.config.item.bad_custom_model_data_value", path.toString(), id.toString(), String.valueOf(customModelData));
|
||||
}
|
||||
|
||||
conflict.put(customModelData, id);
|
||||
|
||||
// Parse models
|
||||
ItemModel model = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : model.modelsToGenerate()) {
|
||||
prepareModelGeneration(path, id, generation);
|
||||
}
|
||||
|
||||
if (Config.packMaxVersion() > 21.39f) {
|
||||
TreeMap<Integer, ItemModel> map = modernOverrides.computeIfAbsent(materialId, k -> new TreeMap<>());
|
||||
map.put(customModelData, model);
|
||||
}
|
||||
|
||||
if (Config.packMinVersion() < 21.39f) {
|
||||
List<LegacyOverridesModel> legacyOverridesModels = new ArrayList<>();
|
||||
processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, customModelData);
|
||||
TreeSet<LegacyOverridesModel> lom = legacyOverrides.computeIfAbsent(materialId, k -> new TreeSet<>());
|
||||
lom.addAll(legacyOverridesModels);
|
||||
}
|
||||
}
|
||||
if (itemModelKey != null) {
|
||||
hasModel = true;
|
||||
// use components
|
||||
ItemModel model = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : model.modelsToGenerate()) {
|
||||
prepareModelGeneration(path, id, generation);
|
||||
}
|
||||
|
||||
if (Config.packMaxVersion() > 21.39f) {
|
||||
modernItemModels1_21_4.put(itemModelKey, model);
|
||||
}
|
||||
|
||||
if (Config.packMaxVersion() > 21.19f && Config.packMinVersion() < 21.39f) {
|
||||
List<LegacyOverridesModel> legacyOverridesModels = new ArrayList<>();
|
||||
processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, 0);
|
||||
if (!legacyOverridesModels.isEmpty()) {
|
||||
legacyOverridesModels.sort(LegacyOverridesModel::compareTo);
|
||||
modernItemModels1_21_2.put(itemModelKey, legacyOverridesModels);
|
||||
} else {
|
||||
plugin.debug(() -> "Can't convert " + id + "'s model to legacy format.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasModel) {
|
||||
TranslationManager.instance().log("warning.config.item.lack_model_id", path.toString(), id.toString());
|
||||
}
|
||||
}
|
||||
if (!hasModel) {
|
||||
plugin.logger().warn(path, "No custom-model-data/item-model configured for " + id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
Optional<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().getBlock(blockBehavior.stripped());
|
||||
Optional<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().blockById(blockBehavior.stripped());
|
||||
if (optionalNewCustomBlock.isEmpty()) {
|
||||
CraftEngine.instance().logger().warn("stripped block " + blockBehavior.stripped() + " does not exist");
|
||||
return InteractionResult.FAIL;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class BlockItemBehavior extends ItemBehavior {
|
||||
if (!context.canPlace()) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
Optional<CustomBlock> optionalBlock = BukkitBlockManager.instance().getBlock(this.blockId);
|
||||
Optional<CustomBlock> optionalBlock = BukkitBlockManager.instance().blockById(this.blockId);
|
||||
if (optionalBlock.isEmpty()) {
|
||||
CraftEngine.instance().logger().warn("Failed to place unknown block " + this.blockId);
|
||||
return InteractionResult.FAIL;
|
||||
@@ -176,7 +176,7 @@ public class BlockItemBehavior extends ItemBehavior {
|
||||
throw new IllegalArgumentException("Missing required parameter 'block' for block_item behavior");
|
||||
}
|
||||
if (id instanceof Map<?, ?> map) {
|
||||
BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
return new BlockItemBehavior(key);
|
||||
} else {
|
||||
return new BlockItemBehavior(Key.of(id.toString()));
|
||||
|
||||
@@ -47,7 +47,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
}
|
||||
|
||||
public InteractionResult place(UseOnContext context) {
|
||||
Optional<CustomFurniture> optionalCustomFurniture = BukkitFurnitureManager.instance().getFurniture(this.id);
|
||||
Optional<CustomFurniture> optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id);
|
||||
if (optionalCustomFurniture.isEmpty()) {
|
||||
CraftEngine.instance().logger().warn("Furniture " + this.id + " not found");
|
||||
return InteractionResult.FAIL;
|
||||
@@ -139,7 +139,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
throw new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior");
|
||||
}
|
||||
if (id instanceof Map<?,?> map) {
|
||||
BukkitFurnitureManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
return new FurnitureItemBehavior(key);
|
||||
} else {
|
||||
return new FurnitureItemBehavior(Key.of(id.toString()));
|
||||
|
||||
@@ -67,7 +67,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
}
|
||||
int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1));
|
||||
if (id instanceof Map<?, ?> map) {
|
||||
BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
return new LiquidCollisionBlockItemBehavior(key, offset);
|
||||
} else {
|
||||
return new LiquidCollisionBlockItemBehavior(Key.of(id.toString()), offset);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.momirealms.craftengine.bukkit.item.recipe;
|
||||
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface BukkitRecipeConvertor<T extends Recipe<ItemStack>> {
|
||||
|
||||
Runnable convert(Key id, T recipe);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -39,7 +39,7 @@ public class CrafterEventListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onCrafting(CrafterCraftEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
CraftingRecipe recipe = event.getRecipe();
|
||||
if (!(event.getBlock().getState() instanceof Crafter crafter)) {
|
||||
return;
|
||||
@@ -82,12 +82,12 @@ public class CrafterEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPELESS, input);
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input);
|
||||
if (ceRecipe != null) {
|
||||
event.setResult(ceRecipe.result(ItemBuildContext.EMPTY));
|
||||
return;
|
||||
}
|
||||
ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPED, input);
|
||||
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input);
|
||||
if (ceRecipe != null) {
|
||||
event.setResult(ceRecipe.result(ItemBuildContext.EMPTY));
|
||||
return;
|
||||
|
||||
@@ -17,7 +17,7 @@ import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
@@ -85,7 +85,7 @@ public class RecipeEventListener implements Listener {
|
||||
recipeType = RecipeTypes.SMOKING;
|
||||
}
|
||||
|
||||
Recipe<ItemStack> ceRecipe = recipeManager.getRecipe(recipeType, input);
|
||||
Recipe<ItemStack> ceRecipe = recipeManager.recipeByInput(recipeType, input);
|
||||
// The item is an ingredient, we should never consider it as fuel firstly
|
||||
if (ceRecipe != null) return;
|
||||
|
||||
@@ -261,7 +261,7 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onFurnaceInventoryOpen(InventoryOpenEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!(event.getInventory() instanceof FurnaceInventory furnaceInventory)) {
|
||||
return;
|
||||
}
|
||||
@@ -277,7 +277,7 @@ public class RecipeEventListener implements Listener {
|
||||
// for 1.20.1-1.21.1
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onBlockIgnite(BlockIgniteEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (VersionHelper.isVersionNewerThan1_21_2()) return;
|
||||
Block block = event.getBlock();
|
||||
Material material = block.getType();
|
||||
@@ -295,7 +295,7 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlaceBlock(BlockPlaceEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
Block block = event.getBlock();
|
||||
Material material = block.getType();
|
||||
if (material == Material.FURNACE || material == Material.BLAST_FURNACE || material == Material.SMOKER) {
|
||||
@@ -322,7 +322,7 @@ public class RecipeEventListener implements Listener {
|
||||
// for 1.21.2+
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPutItemOnCampfire(PlayerInteractEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isVersionNewerThan1_21_2()) return;
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
|
||||
Block clicked = event.getClickedBlock();
|
||||
@@ -345,7 +345,7 @@ public class RecipeEventListener implements Listener {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalMCRecipe = (Optional<Object>) Reflections.method$RecipeManager$getRecipeFor1.invoke(
|
||||
BukkitRecipeManager.minecraftRecipeManager(),
|
||||
BukkitRecipeManager.nmsRecipeManager(),
|
||||
Reflections.instance$RecipeType$CAMPFIRE_COOKING,
|
||||
Reflections.constructor$SingleRecipeInput.newInstance(Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack)),
|
||||
FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()),
|
||||
@@ -360,7 +360,7 @@ public class RecipeEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
@@ -373,7 +373,7 @@ public class RecipeEventListener implements Listener {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCampfireCook(CampfireStartEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isVersionNewerThan1_21_2()) return;
|
||||
CampfireRecipe recipe = event.getRecipe();
|
||||
Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
@@ -392,7 +392,7 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setTotalCookTime(Integer.MAX_VALUE);
|
||||
return;
|
||||
@@ -404,7 +404,7 @@ public class RecipeEventListener implements Listener {
|
||||
// for 1.21.2+
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCampfireCook(BlockCookEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isVersionNewerThan1_21_2()) return;
|
||||
Material type = event.getBlock().getType();
|
||||
if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return;
|
||||
@@ -427,7 +427,7 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
@@ -743,7 +743,7 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCraftingRecipe(PrepareItemCraftEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (recipe == null)
|
||||
return;
|
||||
@@ -802,14 +802,14 @@ public class RecipeEventListener implements Listener {
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
Key lastRecipe = serverPlayer.lastUsedRecipe();
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPELESS, input, lastRecipe);
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input, lastRecipe);
|
||||
if (ceRecipe != null) {
|
||||
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
|
||||
serverPlayer.setLastUsedRecipe(ceRecipe.id());
|
||||
correctCraftingRecipeUsed(inventory, ceRecipe);
|
||||
return;
|
||||
}
|
||||
ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPED, input, lastRecipe);
|
||||
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe);
|
||||
if (ceRecipe != null) {
|
||||
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
|
||||
serverPlayer.setLastUsedRecipe(ceRecipe.id());
|
||||
@@ -821,7 +821,7 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) {
|
||||
Object holderOrRecipe = recipeManager.getRecipeHolderByRecipe(recipe);
|
||||
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe);
|
||||
if (holderOrRecipe == null) {
|
||||
// it's a vanilla recipe but not injected
|
||||
return;
|
||||
@@ -836,7 +836,7 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onSmithingTransform(PrepareSmithingEvent event) {
|
||||
if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
SmithingInventory inventory = event.getInventory();
|
||||
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return;
|
||||
|
||||
@@ -857,7 +857,7 @@ public class RecipeEventListener implements Listener {
|
||||
getOptimizedIDItem(addition)
|
||||
);
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SMITHING_TRANSFORM, input);
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SMITHING_TRANSFORM, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
@@ -878,7 +878,7 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
|
||||
Object holderOrRecipe = recipeManager.getRecipeHolderByRecipe(recipe);
|
||||
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe);
|
||||
if (holderOrRecipe == null) {
|
||||
// it's a vanilla recipe but not injected
|
||||
return;
|
||||
|
||||
@@ -8,14 +8,16 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.loot.AbstractVanillaLootManager;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLoot;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLootManager;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.PreConditions;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
@@ -30,18 +32,19 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
// note: block listeners are in BlockEventListener to reduce performance cost
|
||||
public class BukkitVanillaLootManager implements VanillaLootManager, Listener {
|
||||
public class BukkitVanillaLootManager extends AbstractVanillaLootManager implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final Map<Integer, VanillaLoot> blockLoots;
|
||||
private final Map<Key, VanillaLoot> entityLoots;
|
||||
private final VanillaLootParser vanillaLootParser;
|
||||
|
||||
public BukkitVanillaLootManager(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
this.blockLoots = new HashMap<>();
|
||||
this.entityLoots = new HashMap<>();
|
||||
this.vanillaLootParser = new VanillaLootParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,17 +57,6 @@ public class BukkitVanillaLootManager implements VanillaLootManager, Listener {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.blockLoots.clear();
|
||||
this.entityLoots.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VanillaLoot> getBlockLoot(int vanillaBlockState) {
|
||||
return Optional.ofNullable(this.blockLoots.get(vanillaBlockState));
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onEntityDeath(EntityDeathEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
@@ -97,45 +89,65 @@ public class BukkitVanillaLootManager implements VanillaLootManager, Listener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
String type = (String) section.get("type");
|
||||
if (PreConditions.isNull(type, () -> this.plugin.logger().warn(path, "`type` option is required for vanilla-loot " + id))) {
|
||||
return;
|
||||
public ConfigSectionParser parser() {
|
||||
return this.vanillaLootParser;
|
||||
}
|
||||
|
||||
public class VanillaLootParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot", "loots", "loot"};
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.VANILLA_LOOTS;
|
||||
}
|
||||
VanillaLoot.Type typeEnum = VanillaLoot.Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
||||
boolean override = (boolean) section.getOrDefault("override", false);
|
||||
List<String> targets = MiscUtils.getAsStringList(section.getOrDefault("target", List.of()));
|
||||
LootTable<?> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), false));
|
||||
switch (typeEnum) {
|
||||
case BLOCK -> {
|
||||
for (String target : targets) {
|
||||
if (target.endsWith("]") && target.contains("[")) {
|
||||
java.lang.Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(target));
|
||||
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target);
|
||||
return;
|
||||
}
|
||||
VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
} else {
|
||||
for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) {
|
||||
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
String type = (String) section.get("type");
|
||||
if (type == null) {
|
||||
TranslationManager.instance().log("warning.config.vanilla_loot.type_not_exist", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
VanillaLoot.Type typeEnum = VanillaLoot.Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
||||
boolean override = (boolean) section.getOrDefault("override", false);
|
||||
List<String> targets = MiscUtils.getAsStringList(section.getOrDefault("target", List.of()));
|
||||
LootTable<?> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), false));
|
||||
switch (typeEnum) {
|
||||
case BLOCK -> {
|
||||
for (String target : targets) {
|
||||
if (target.endsWith("]") && target.contains("[")) {
|
||||
java.lang.Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(target));
|
||||
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target);
|
||||
TranslationManager.instance().log("warning.config.vanilla_loot.block.invalid_target", path.toString(), id.toString(), target);
|
||||
return;
|
||||
}
|
||||
VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
if (override) vanillaLoot.override(true);
|
||||
VanillaLoot vanillaLoot = blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
} else {
|
||||
for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) {
|
||||
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
TranslationManager.instance().log("warning.config.vanilla_loot.block.invalid_target", path.toString(), id.toString(), target);
|
||||
return;
|
||||
}
|
||||
VanillaLoot vanillaLoot = blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
if (override) vanillaLoot.override(true);
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
for (String target : targets) {
|
||||
Key key = Key.of(target);
|
||||
VanillaLoot vanillaLoot = this.entityLoots.computeIfAbsent(key, k -> new VanillaLoot(VanillaLoot.Type.ENTITY));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
if (override) vanillaLoot.override(true);
|
||||
case ENTITY -> {
|
||||
for (String target : targets) {
|
||||
Key key = Key.of(target);
|
||||
VanillaLoot vanillaLoot = entityLoots.computeIfAbsent(key, k -> new VanillaLoot(VanillaLoot.Type.ENTITY));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
if (override) vanillaLoot.override(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.pack.host.HostMode;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
@@ -52,7 +52,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (ConfigManager.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.sendResourcePack(plugin.networkManager().getUser(event.getPlayer()), null);
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onResourcePackStatus(PlayerResourcePackStatusEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (ConfigManager.sendPackOnJoin() && ConfigManager.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) {
|
||||
event.getPlayer().kick();
|
||||
}
|
||||
@@ -73,34 +73,34 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
if (Files.exists(resourcePackPath())) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, ConfigManager.kickOnDeclined(), ConfigManager.resourcePackPrompt());
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
} else if (ConfigManager.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackSettings(ConfigManager.externalPackUUID(), ConfigManager.externalPackUrl(), ConfigManager.externalPackSha1(), ConfigManager.kickOnDeclined(), ConfigManager.resourcePackPrompt());
|
||||
} else if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackSettings(Config.externalPackUUID(), Config.externalPackUrl(), Config.externalPackSha1(), Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigManager.sendPackOnReload()) {
|
||||
if (Config.sendPackOnReload()) {
|
||||
if (this.previousHostMode == HostMode.SELF_HOST) {
|
||||
this.previousHostUUID = super.packUUID;
|
||||
}
|
||||
// unload packs if user changed to none host
|
||||
if (ConfigManager.hostMode() == HostMode.NONE && this.previousHostMode != HostMode.NONE) {
|
||||
if (Config.hostMode() == HostMode.NONE && this.previousHostMode != HostMode.NONE) {
|
||||
unloadResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// load new external resource pack on reload
|
||||
if (ConfigManager.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
updateResourcePackForOnlinePlayers(null);
|
||||
} else {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// record previous host uuid here
|
||||
this.previousHostUUID = ConfigManager.externalPackUUID();
|
||||
this.previousHostUUID = Config.externalPackUUID();
|
||||
}
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST && this.previousHostMode != HostMode.SELF_HOST) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && this.previousHostMode != HostMode.SELF_HOST) {
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
ReloadCommand.RELOAD_PACK_FLAG = false;
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
@@ -111,7 +111,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.previousHostMode = ConfigManager.hostMode();
|
||||
this.previousHostMode = Config.hostMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,12 +134,12 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
super.generateResourcePack();
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, ConfigManager.kickOnDeclined(), ConfigManager.resourcePackPrompt());
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
// resend packs
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST && ConfigManager.sendPackOnReload()) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && Config.sendPackOnReload()) {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
}
|
||||
|
||||
private void updateResourcePackSettings(UUID uuid, String url, String sha1, boolean required, Component prompt) {
|
||||
if (!ConfigManager.sendPackOnJoin()) {
|
||||
if (!Config.sendPackOnJoin()) {
|
||||
resetResourcePackSettings();
|
||||
return;
|
||||
}
|
||||
@@ -182,36 +182,36 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
}
|
||||
|
||||
public void sendResourcePack(NetWorkUser user, @Nullable UUID previousPack) {
|
||||
if (ConfigManager.hostMode() == HostMode.NONE) return;
|
||||
if (Config.hostMode() == HostMode.NONE) return;
|
||||
String url;
|
||||
String sha1;
|
||||
UUID uuid;
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
url = ResourcePackHost.instance().url();
|
||||
sha1 = super.packHash;
|
||||
uuid = super.packUUID;
|
||||
if (!Files.exists(resourcePackPath())) return;
|
||||
} else {
|
||||
url = ConfigManager.externalPackUrl();
|
||||
sha1 = ConfigManager.externalPackSha1();
|
||||
uuid = ConfigManager.externalPackUUID();
|
||||
url = Config.externalPackUrl();
|
||||
sha1 = Config.externalPackSha1();
|
||||
uuid = Config.externalPackUUID();
|
||||
if (uuid.equals(previousPack)) return;
|
||||
}
|
||||
|
||||
Object packPrompt = ComponentUtils.adventureToMinecraft(ConfigManager.resourcePackPrompt());
|
||||
Object packPrompt = ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt());
|
||||
try {
|
||||
Object packPacket;
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, ConfigManager.kickOnDeclined(), Optional.of(packPrompt)
|
||||
uuid, url, sha1, Config.kickOnDeclined(), Optional.of(packPrompt)
|
||||
);
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, ConfigManager.kickOnDeclined(), packPrompt
|
||||
uuid, url, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
} else {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
url + uuid, sha1, ConfigManager.kickOnDeclined(), packPrompt
|
||||
url + uuid, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
}
|
||||
if (user.decoderState() == ConnectionState.PLAY) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes;
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitImageManager;
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
|
||||
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
|
||||
@@ -29,7 +29,7 @@ import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.ReflectionClassPathAppender;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.dependency.Dependencies;
|
||||
import net.momirealms.craftengine.core.plugin.dependency.Dependency;
|
||||
import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManagerImpl;
|
||||
@@ -65,17 +65,20 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
private boolean hasPlaceholderAPI;
|
||||
|
||||
public BukkitCraftEngine(JavaPlugin bootstrap) {
|
||||
VersionHelper.init(serverVersion());
|
||||
super((p) -> {
|
||||
CraftEngineReloadEvent event = new CraftEngineReloadEvent((BukkitCraftEngine) p);
|
||||
EventUtils.fireAndForget(event);
|
||||
});
|
||||
instance = this;
|
||||
this.bootstrap = bootstrap;
|
||||
super.classPathAppender = new ReflectionClassPathAppender(this);
|
||||
super.scheduler = new BukkitSchedulerAdapter(this);
|
||||
super.logger = new JavaPluginLogger(bootstrap.getLogger());
|
||||
// find mod class if present
|
||||
Class<?> modClass = ReflectionUtils.getClazz(MOD_CLASS);
|
||||
if (modClass != null) {
|
||||
Field isSuccessfullyRegistered = ReflectionUtils.getDeclaredField(modClass, "isSuccessfullyRegistered");
|
||||
try {
|
||||
assert isSuccessfullyRegistered != null;
|
||||
requiresRestart = !(boolean) isSuccessfullyRegistered.get(null);
|
||||
hasMod = true;
|
||||
} catch (Exception ignore) {
|
||||
@@ -84,13 +87,11 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
public void onPluginLoad() {
|
||||
super.onPluginLoad();
|
||||
Reflections.init();
|
||||
BukkitInjector.init();
|
||||
super.networkManager = new BukkitNetworkManager(this);
|
||||
super.networkManager.init();
|
||||
// load mappings and inject blocks
|
||||
super.blockManager = new BukkitBlockManager(this);
|
||||
super.furnitureManager = new BukkitFurnitureManager(this);
|
||||
this.successfullyLoaded = true;
|
||||
@@ -105,8 +106,8 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
if (successfullyEnabled) {
|
||||
public void onPluginEnable() {
|
||||
if (this.successfullyEnabled) {
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
@@ -154,8 +155,37 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.worldManager = new BukkitWorldManager(this);
|
||||
super.soundManager = new BukkitSoundManager(this);
|
||||
super.vanillaLootManager = new BukkitVanillaLootManager(this);
|
||||
this.imageManager = new BukkitImageManager(this);
|
||||
super.enable();
|
||||
super.fontManager = new BukkitFontManager(this);
|
||||
super.onPluginEnable();
|
||||
// compatibility
|
||||
// register expansion
|
||||
if (this.isPluginEnabled("PlaceholderAPI")) {
|
||||
PlaceholderAPIUtils.registerExpansions(this);
|
||||
this.hasPlaceholderAPI = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisable() {
|
||||
super.onPluginDisable();
|
||||
if (this.tickTask != null) this.tickTask.cancel();
|
||||
if (!Bukkit.getServer().isStopping()) {
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe("Please do not disable plugins at runtime.");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
Bukkit.getServer().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void platformDelayedEnable() {
|
||||
if (Config.metrics()) {
|
||||
new Metrics(this.bootstrap(), 24333);
|
||||
}
|
||||
// tick task
|
||||
if (VersionHelper.isFolia()) {
|
||||
this.tickTask = this.scheduler().sync().runRepeating(() -> {
|
||||
@@ -172,69 +202,6 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
}
|
||||
}, 1, 1);
|
||||
}
|
||||
|
||||
// compatibility
|
||||
// register expansion
|
||||
if (this.isPluginEnabled("PlaceholderAPI")) {
|
||||
PlaceholderAPIUtils.registerExpansions(this);
|
||||
this.hasPlaceholderAPI = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
if (this.tickTask != null) this.tickTask.cancel();
|
||||
if (!Bukkit.getServer().isStopping()) {
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe("Please do not disable plugins at runtime.");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
Bukkit.getServer().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
super.reload();
|
||||
CraftEngineReloadEvent event = new CraftEngineReloadEvent(this);
|
||||
EventUtils.fireAndForget(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedEnable() {
|
||||
if (ConfigManager.metrics()) {
|
||||
new Metrics(this.bootstrap(), 24333);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerParsers() {
|
||||
// register template parser
|
||||
this.packManager.registerConfigSectionParser(this.templateManager);
|
||||
// register font parser
|
||||
this.packManager.registerConfigSectionParser(this.imageManager);
|
||||
// register item parser
|
||||
this.packManager.registerConfigSectionParser(this.itemManager);
|
||||
// register furniture parser
|
||||
this.packManager.registerConfigSectionParser(this.furnitureManager);
|
||||
// register block parser
|
||||
this.packManager.registerConfigSectionParser(this.blockManager);
|
||||
// register recipe parser
|
||||
this.packManager.registerConfigSectionParser(this.recipeManager);
|
||||
// register category parser
|
||||
this.packManager.registerConfigSectionParser(this.itemBrowserManager);
|
||||
// register translation parser
|
||||
this.packManager.registerConfigSectionParser(this.translationManager);
|
||||
this.packManager.registerConfigSectionParser(this.translationManager.clientLangManager());
|
||||
// register sound parser
|
||||
this.packManager.registerConfigSectionParser(this.soundManager);
|
||||
this.packManager.registerConfigSectionParser(this.soundManager.jukeboxSongManager());
|
||||
// register vanilla loot parser
|
||||
this.packManager.registerConfigSectionParser(this.vanillaLootManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,7 +45,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new DebugItemDataCommand(this, plugin),
|
||||
new DebugSetBlockCommand(this, plugin),
|
||||
new DebugSpawnFurnitureCommand(this, plugin),
|
||||
new DebugTargetBlockCommand(this, plugin)
|
||||
new DebugTargetBlockCommand(this, plugin),
|
||||
new TotemAnimationCommand(this, plugin)
|
||||
));
|
||||
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
|
||||
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
|
||||
import net.momirealms.craftengine.core.util.Tristate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.RemoteConsoleCommandSender;
|
||||
@@ -79,6 +80,12 @@ public class BukkitSenderFactory extends SenderFactory<BukkitCraftEngine, Comman
|
||||
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <C extends CommandSender> C consoleCommandSender() {
|
||||
return (C) Bukkit.getConsoleSender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
|
||||
@@ -47,7 +47,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
|
||||
NamespacedKey namespacedKey = context.get("id");
|
||||
Key id = KeyUtils.namespacedKey2Key(namespacedKey);
|
||||
BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance();
|
||||
Optional<CustomFurniture> optionalCustomFurniture = furnitureManager.getFurniture(id);
|
||||
Optional<CustomFurniture> optionalCustomFurniture = furnitureManager.furnitureById(id);
|
||||
if (optionalCustomFurniture.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,27 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
if (argument == ReloadArgument.CONFIG) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().reload();
|
||||
long time2 = System.currentTimeMillis();
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS, Component.text(time2 - time1));
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), false).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime())
|
||||
);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
|
||||
plugin().logger().warn("Failed to reload config", e);
|
||||
}
|
||||
} else if (argument == ReloadArgument.RECIPE) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime())
|
||||
);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
|
||||
plugin().logger().warn("Failed to reload config", e);
|
||||
@@ -50,29 +67,30 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_SUCCESS, Component.text(time2 - time1));
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_SUCCESS, Component.text(packTime));
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE);
|
||||
plugin().logger().warn("Failed to generate resource pack", e);
|
||||
}
|
||||
});
|
||||
} else if (argument == ReloadArgument.ALL) {
|
||||
long time1 = System.currentTimeMillis();
|
||||
try {
|
||||
plugin().reload();
|
||||
plugin().scheduler().executeAsync(() -> {
|
||||
try {
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS, Component.text(time2 - time1));
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
|
||||
plugin().logger().warn("Failed to generate resource pack", e);
|
||||
}
|
||||
});
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAcceptAsync(reloadResult -> {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime() + packTime),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime()),
|
||||
Component.text(packTime)
|
||||
);
|
||||
}, plugin().scheduler().async());
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
|
||||
plugin().logger().warn("Failed to reload config", e);
|
||||
plugin().logger().warn("Failed to generate resource pack", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -85,6 +103,7 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public enum ReloadArgument {
|
||||
CONFIG,
|
||||
RECIPE,
|
||||
PACK,
|
||||
ALL
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class SearchRecipeAdminCommand extends BukkitCommandFeature<CommandSender
|
||||
for (Player player : players) {
|
||||
BukkitServerPlayer serverPlayer = plugin().adapt(player);
|
||||
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().getRecipeByResult(itemId);
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().recipeByResult(itemId);
|
||||
if (!inRecipes.isEmpty()) {
|
||||
plugin().itemBrowserManager().openRecipePage(serverPlayer, null, inRecipes, 0, 0, false);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class SearchRecipePlayerCommand extends BukkitCommandFeature<CommandSende
|
||||
return;
|
||||
}
|
||||
Key itemId = item.id();
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().getRecipeByResult(itemId);
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().recipeByResult(itemId);
|
||||
if (!inRecipes.isEmpty()) {
|
||||
plugin().itemBrowserManager().openRecipePage(serverPlayer, null, inRecipes, 0, 0, false);
|
||||
} else {
|
||||
|
||||
@@ -47,7 +47,7 @@ public class SearchUsageAdminCommand extends BukkitCommandFeature<CommandSender>
|
||||
for (Player player : players) {
|
||||
BukkitServerPlayer serverPlayer = plugin().adapt(player);
|
||||
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().getRecipeByIngredient(itemId);
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().recipeByIngredient(itemId);
|
||||
if (!inRecipes.isEmpty()) {
|
||||
plugin().itemBrowserManager().openRecipePage(serverPlayer, null, inRecipes, 0, 0, false);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class SearchUsagePlayerCommand extends BukkitCommandFeature<CommandSender
|
||||
return;
|
||||
}
|
||||
Key itemId = item.id();
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().getRecipeByIngredient(itemId);
|
||||
List<Recipe<Object>> inRecipes = plugin().recipeManager().recipeByIngredient(itemId);
|
||||
if (!inRecipes.isEmpty()) {
|
||||
plugin().itemBrowserManager().openRecipePage(serverPlayer, null, inRecipes, 0, 0, false);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.util.MaterialUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
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.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.bukkit.data.MultiplePlayerSelector;
|
||||
import org.incendo.cloud.bukkit.parser.NamespacedKeyParser;
|
||||
import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class TotemAnimationCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public TotemAnimationCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(FlagKeys.SILENT_FLAG)
|
||||
.required("players", MultiplePlayerSelectorParser.multiplePlayerSelectorParser())
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedTotemSuggestions());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
NamespacedKey namespacedKey = context.get("id");
|
||||
Key key = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
CustomItem<ItemStack> item = plugin().itemManager().getCustomItem(key).orElse(null);
|
||||
if (item == null || MaterialUtils.getMaterial(item.material()) != Material.TOTEM_OF_UNDYING) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_TOTEM_NOT_TOTEM, Component.text(key.toString()));
|
||||
return;
|
||||
}
|
||||
ItemStack totem = item.buildItemStack();
|
||||
MultiplePlayerSelector selector = context.get("players");
|
||||
for (Player player : selector.values()) {
|
||||
PlayerUtils.sendTotemAnimation(player, totem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "totem_animation";
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
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.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -357,7 +357,7 @@ public class BukkitInjector {
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
|
||||
Object mcRecipeManager = BukkitRecipeManager.minecraftRecipeManager();
|
||||
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
Object type = injectedCacheCheck.recipeType();
|
||||
Object lastRecipe = injectedCacheCheck.lastRecipe();
|
||||
@@ -394,13 +394,13 @@ public class BukkitInjector {
|
||||
CustomCookingRecipe<ItemStack> ceRecipe;
|
||||
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
|
||||
if (type == Reflections.instance$RecipeType$SMELTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$BLASTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$SMOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$CAMPFIRE_COOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -412,7 +412,7 @@ public class BukkitInjector {
|
||||
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
|
||||
// It doesn't matter at all
|
||||
injectedCacheCheck.lastRecipe(resourceLocation);
|
||||
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
|
||||
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -425,7 +425,7 @@ public class BukkitInjector {
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
|
||||
Object mcRecipeManager = BukkitRecipeManager.minecraftRecipeManager();
|
||||
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
Object type = injectedCacheCheck.recipeType();
|
||||
Object lastRecipe = injectedCacheCheck.lastRecipe();
|
||||
@@ -462,13 +462,13 @@ public class BukkitInjector {
|
||||
CustomCookingRecipe<ItemStack> ceRecipe;
|
||||
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
|
||||
if (type == Reflections.instance$RecipeType$SMELTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$BLASTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$SMOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$CAMPFIRE_COOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -480,7 +480,7 @@ public class BukkitInjector {
|
||||
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
|
||||
// It doesn't matter at all
|
||||
injectedCacheCheck.lastRecipe(id);
|
||||
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -493,7 +493,7 @@ public class BukkitInjector {
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
|
||||
Object mcRecipeManager = BukkitRecipeManager.minecraftRecipeManager();
|
||||
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
Object type = injectedCacheCheck.recipeType();
|
||||
Object lastRecipe = injectedCacheCheck.lastRecipe();
|
||||
@@ -522,13 +522,13 @@ public class BukkitInjector {
|
||||
CustomCookingRecipe<ItemStack> ceRecipe;
|
||||
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
|
||||
if (type == Reflections.instance$RecipeType$SMELTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$BLASTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$SMOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$CAMPFIRE_COOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -540,7 +540,7 @@ public class BukkitInjector {
|
||||
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
|
||||
// It doesn't matter at all
|
||||
injectedCacheCheck.lastRecipe(id);
|
||||
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -553,7 +553,7 @@ public class BukkitInjector {
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
|
||||
Object mcRecipeManager = BukkitRecipeManager.minecraftRecipeManager();
|
||||
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
Object type = injectedCacheCheck.recipeType();
|
||||
Object lastRecipe = injectedCacheCheck.lastRecipe();
|
||||
@@ -583,11 +583,11 @@ public class BukkitInjector {
|
||||
CustomCookingRecipe<ItemStack> ceRecipe;
|
||||
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
|
||||
if (type == Reflections.instance$RecipeType$SMELTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$BLASTING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
|
||||
} else if (type == Reflections.instance$RecipeType$SMOKING) {
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -599,7 +599,7 @@ public class BukkitInjector {
|
||||
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
|
||||
// It doesn't matter at all
|
||||
injectedCacheCheck.lastRecipe(id);
|
||||
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -623,14 +623,14 @@ public class BukkitInjector {
|
||||
CESection section = holder.ceSection();
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
section.setBlockState(x, y, z, EmptyBlock.INSTANCE.defaultState());
|
||||
if (ConfigManager.enableLightSystem() && ConfigManager.forceUpdateLight()) {
|
||||
if (Config.enableLightSystem() && Config.forceUpdateLight()) {
|
||||
updateLightIfChanged(holder, previousState, newState, null, y, z, x);
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
section.setBlockState(x, y, z, immutableBlockState);
|
||||
if (!immutableBlockState.isEmpty()) {
|
||||
if (ConfigManager.enableLightSystem()) {
|
||||
if (Config.enableLightSystem()) {
|
||||
updateLightIfChanged(holder, previousState, newState, immutableBlockState.vanillaBlockState().handle(), y, z, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,20 +37,21 @@ import java.util.function.BiConsumer;
|
||||
|
||||
public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener {
|
||||
private static BukkitNetworkManager instance;
|
||||
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> nmsPacketFunctions = new HashMap<>();
|
||||
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> byteBufPacketFunctions = new HashMap<>();
|
||||
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> NMS_PACKET_HANDLERS = new HashMap<>();
|
||||
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> BYTE_BUFFER_PACKET_HANDLERS = new HashMap<>();
|
||||
|
||||
private static void registerNMSPacketConsumer(final TriConsumer<NetWorkUser, NMSPacketEvent, Object> function, @Nullable Class<?> packet) {
|
||||
if (packet == null) return;
|
||||
nmsPacketFunctions.put(packet, function);
|
||||
NMS_PACKET_HANDLERS.put(packet, function);
|
||||
}
|
||||
|
||||
private static void registerByteBufPacketConsumer(final BiConsumer<NetWorkUser, ByteBufPacketEvent> function, int id) {
|
||||
byteBufPacketFunctions.put(id, function);
|
||||
BYTE_BUFFER_PACKET_HANDLERS.put(id, function);
|
||||
}
|
||||
|
||||
private final BiConsumer<Object, List<Object>> packetsConsumer;
|
||||
private final BiConsumer<Object, Object> delayedPacketConsumer;
|
||||
private final BiConsumer<Object, List<Object>> immediatePacketsConsumer;
|
||||
private final BiConsumer<Object, Object> packetConsumer;
|
||||
private final BiConsumer<Object, Object> immediatePacketConsumer;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
@@ -67,35 +68,22 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
private static final String PACKET_ENCODER = "craftengine_encoder";
|
||||
private static final String PACKET_DECODER = "craftengine_decoder";
|
||||
|
||||
private boolean active;
|
||||
private boolean init;
|
||||
private static boolean hasModelEngine;
|
||||
|
||||
public static boolean hasModelEngine() {
|
||||
return hasModelEngine;
|
||||
}
|
||||
|
||||
public BukkitNetworkManager(BukkitCraftEngine plugin) {
|
||||
instance = this;
|
||||
hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null;
|
||||
this.plugin = plugin;
|
||||
if (VersionHelper.isVersionNewerThan1_21_5()) {
|
||||
this.packetIds = new PacketIds1_21_5();
|
||||
} else if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
this.packetIds = new PacketIds1_21_2();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
this.packetIds = new PacketIds1_20_5();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
this.packetIds = new PacketIds1_20_3();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.packetIds = new PacketIds1_20_2();
|
||||
} else {
|
||||
this.packetIds = new PacketIds1_20();
|
||||
}
|
||||
this.registerConsumers();
|
||||
// set up packet id
|
||||
this.packetIds = setupPacketIds();
|
||||
// register packet handlers
|
||||
this.registerPacketHandlers();
|
||||
// set up packet senders
|
||||
this.packetConsumer = FastNMS.INSTANCE::sendPacket;
|
||||
this.packetsConsumer = ((serverPlayer, packets) -> {
|
||||
Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
FastNMS.INSTANCE.sendPacket(serverPlayer, bundle);
|
||||
this.packetConsumer.accept(serverPlayer, bundle);
|
||||
});
|
||||
this.delayedPacketConsumer = FastNMS.INSTANCE::sendPacket;
|
||||
this.immediatePacketConsumer = (serverPlayer, packet) -> {
|
||||
try {
|
||||
Reflections.method$Connection$sendPacketImmediate.invoke(FastNMS.INSTANCE.field$Player$connection$connection(serverPlayer), packet, null, true);
|
||||
@@ -103,12 +91,45 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
plugin.logger().warn("Failed to invoke send packet", e);
|
||||
}
|
||||
};
|
||||
this.active = true;
|
||||
hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null;
|
||||
instance = this;
|
||||
this.immediatePacketsConsumer = (serverPlayer, packets) -> {
|
||||
Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
this.immediatePacketConsumer.accept(serverPlayer, bundle);
|
||||
};
|
||||
// set up mod channel
|
||||
this.plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL, this);
|
||||
this.plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL);
|
||||
// Inject server channel
|
||||
try {
|
||||
Object server = Reflections.method$MinecraftServer$getServer.invoke(null);
|
||||
Object serverConnection = Reflections.field$MinecraftServer$connection.get(server);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ChannelFuture> channels = (List<ChannelFuture>) Reflections.field$ServerConnectionListener$channels.get(serverConnection);
|
||||
ListMonitor<ChannelFuture> monitor = new ListMonitor<>(channels, (future) -> {
|
||||
Channel channel = future.channel();
|
||||
injectServerChannel(channel);
|
||||
this.injectedChannels.add(channel);
|
||||
}, (object) -> {});
|
||||
Reflections.field$ServerConnectionListener$channels.set(serverConnection, monitor);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to init server connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerConsumers() {
|
||||
private PacketIds setupPacketIds() {
|
||||
if (VersionHelper.isVersionNewerThan1_21()) {
|
||||
return new PacketIds1_21();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
return new PacketIds1_20_5();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
return new PacketIds1_20_3();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
return new PacketIds1_20_2();
|
||||
} else {
|
||||
return new PacketIds1_20();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPacketHandlers() {
|
||||
registerNMSPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, Reflections.clazz$ClientboundLevelChunkWithLightPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.PLAYER_ACTION, Reflections.clazz$ServerboundPlayerActionPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SWING_HAND, Reflections.clazz$ServerboundSwingPacket);
|
||||
@@ -128,6 +149,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_DATA, Reflections.clazz$ClientboundSetEntityDataPacket);
|
||||
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
||||
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
|
||||
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
|
||||
@@ -149,7 +171,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Channel channel = getChannel(player);
|
||||
@@ -169,40 +191,17 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
return this.onlineUserArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
// 保留仅注册入频道用
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (init) return;
|
||||
try {
|
||||
this.plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL, this);
|
||||
this.plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL);
|
||||
Object server = Reflections.method$MinecraftServer$getServer.invoke(null);
|
||||
Object serverConnection = Reflections.field$MinecraftServer$connection.get(server);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ChannelFuture> channels = (List<ChannelFuture>) Reflections.field$ServerConnectionListener$channels.get(serverConnection);
|
||||
ListMonitor<ChannelFuture> monitor = new ListMonitor<>(channels, (future) -> {
|
||||
if (!this.active) return;
|
||||
Channel channel = future.channel();
|
||||
injectServerChannel(channel);
|
||||
this.injectedChannels.add(channel);
|
||||
}, (object) -> {});
|
||||
Reflections.field$ServerConnectionListener$channels.set(serverConnection, monitor);
|
||||
this.init = true;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to init server connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this);
|
||||
for (Channel channel : injectedChannels) {
|
||||
uninjectServerChannel(channel);
|
||||
@@ -211,7 +210,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
handleDisconnection(getChannel(player));
|
||||
}
|
||||
injectedChannels.clear();
|
||||
active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -258,16 +256,26 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately) {
|
||||
if (immediately) {
|
||||
this.immediatePacketConsumer.accept(player.serverPlayer(), packet);
|
||||
} else {
|
||||
this.delayedPacketConsumer.accept(player.serverPlayer(), packet);
|
||||
this.packetConsumer.accept(player.serverPlayer(), packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPackets(@NotNull NetWorkUser player, List<Object> packet) {
|
||||
this.packetsConsumer.accept(player.serverPlayer(), packet);
|
||||
@Override
|
||||
public void sendPackets(@NotNull NetWorkUser player, List<Object> packet, boolean immediately) {
|
||||
if (immediately) {
|
||||
this.immediatePacketsConsumer.accept(player.serverPlayer(), packet);
|
||||
} else {
|
||||
this.packetsConsumer.accept(player.serverPlayer(), packet);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasModelEngine() {
|
||||
return hasModelEngine;
|
||||
}
|
||||
|
||||
public void receivePacket(@NotNull NetWorkUser player, Object packet) {
|
||||
@@ -542,13 +550,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
}
|
||||
|
||||
protected void handleNMSPacket(NetWorkUser user, NMSPacketEvent event, Object packet) {
|
||||
Optional.ofNullable(nmsPacketFunctions.get(packet.getClass()))
|
||||
Optional.ofNullable(NMS_PACKET_HANDLERS.get(packet.getClass()))
|
||||
.ifPresent(function -> function.accept(user, event, packet));
|
||||
}
|
||||
|
||||
protected void handleByteBufPacket(NetWorkUser user, ByteBufPacketEvent event) {
|
||||
int packetID = event.packetID();
|
||||
Optional.ofNullable(byteBufPacketFunctions.get(packetID))
|
||||
Optional.ofNullable(BYTE_BUFFER_PACKET_HANDLERS.get(packetID))
|
||||
.ifPresent(function -> function.accept(user, event));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.font.ImageManager;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
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.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||
@@ -61,6 +61,11 @@ public class PacketConsumers {
|
||||
mappingsMOD[entry.getKey()] = entry.getValue();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mappingsMOD.length; i++) {
|
||||
if (BlockStateUtils.isVanillaBlock(i)) {
|
||||
mappingsMOD[i] = remap(i);
|
||||
}
|
||||
}
|
||||
BLOCK_LIST = new IntIdentityList(registrySize);
|
||||
BIOME_LIST = new IntIdentityList(RegistryUtils.currentBiomeRegistrySize());
|
||||
}
|
||||
@@ -70,8 +75,7 @@ public class PacketConsumers {
|
||||
}
|
||||
|
||||
public static int remapMOD(int stateId) {
|
||||
int modStateId = mappingsMOD[stateId];
|
||||
return BlockStateUtils.isVanillaBlock(modStateId) ? remap(modStateId) : modStateId;
|
||||
return mappingsMOD[stateId];
|
||||
}
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> LEVEL_CHUNK_WITH_LIGHT = (user, event, packet) -> {
|
||||
@@ -296,7 +300,7 @@ public class PacketConsumers {
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
// not a custom block
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (ConfigManager.enableSoundSystem()) {
|
||||
if (Config.enableSoundSystem()) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(blockState);
|
||||
if (BukkitBlockManager.instance().isBlockSoundRemoved(blockOwner)) {
|
||||
player.startMiningBlock(world, pos, blockState, false, null);
|
||||
@@ -559,7 +563,7 @@ public class PacketConsumers {
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> PICK_ITEM_FROM_ENTITY = (user, event, packet) -> {
|
||||
try {
|
||||
int entityId = (int) Reflections.field$ServerboundPickItemFromEntityPacket$id.get(packet);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByEntityId(entityId);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
|
||||
if (furniture == null) return;
|
||||
Player player = (Player) user.platformPlayer();
|
||||
if (player == null) return;
|
||||
@@ -618,18 +622,18 @@ public class PacketConsumers {
|
||||
} else if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) {
|
||||
// Furniture
|
||||
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
|
||||
if (furniture != null) {
|
||||
user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
|
||||
user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false);
|
||||
if (ConfigManager.hideBaseEntity() && !furniture.hasExternalModel()) {
|
||||
if (Config.hideBaseEntity() && !furniture.hasExternalModel()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
} else if (entityType == Reflections.instance$EntityType$SHULKER) {
|
||||
// Cancel collider entity packet
|
||||
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
|
||||
if (furniture != null) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
@@ -690,7 +694,7 @@ public class PacketConsumers {
|
||||
Object action = Reflections.field$ServerboundInteractPacket$action.get(packet);
|
||||
Object actionType = Reflections.method$ServerboundInteractPacket$Action$getType.invoke(action);
|
||||
if (actionType == null) return;
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByEntityId(entityId);
|
||||
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
|
||||
if (furniture == null) return;
|
||||
Location location = furniture.baseEntity().getLocation();
|
||||
BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user;
|
||||
@@ -770,13 +774,13 @@ public class PacketConsumers {
|
||||
// we handle it on packet level to prevent it from being captured by plugins
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RENAME_ITEM = (user, event, packet) -> {
|
||||
try {
|
||||
if (!ConfigManager.filterAnvil()) return;
|
||||
if (!Config.filterAnvil()) return;
|
||||
String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
ImageManager manager = CraftEngine.instance().imageManager();
|
||||
FontManager manager = CraftEngine.instance().imageManager();
|
||||
if (!manager.isDefaultFontInUse()) return;
|
||||
// check bypass
|
||||
if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_ANVIL)) {
|
||||
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) {
|
||||
return;
|
||||
}
|
||||
runIfContainsIllegalCharacter(message, manager, (s) -> {
|
||||
@@ -795,12 +799,12 @@ public class PacketConsumers {
|
||||
// we handle it on packet level to prevent it from being captured by plugins
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SIGN_UPDATE = (user, event, packet) -> {
|
||||
try {
|
||||
if (!ConfigManager.filterSign()) return;
|
||||
if (!Config.filterSign()) return;
|
||||
String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet);
|
||||
ImageManager manager = CraftEngine.instance().imageManager();
|
||||
FontManager manager = CraftEngine.instance().imageManager();
|
||||
if (!manager.isDefaultFontInUse()) return;
|
||||
// check bypass
|
||||
if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_SIGN)) {
|
||||
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
@@ -822,11 +826,11 @@ public class PacketConsumers {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> EDIT_BOOK = (user, event, packet) -> {
|
||||
try {
|
||||
if (!ConfigManager.filterBook()) return;
|
||||
ImageManager manager = CraftEngine.instance().imageManager();
|
||||
if (!Config.filterBook()) return;
|
||||
FontManager manager = CraftEngine.instance().imageManager();
|
||||
if (!manager.isDefaultFontInUse()) return;
|
||||
// check bypass
|
||||
if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_BOOK)) {
|
||||
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_BOOK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -875,7 +879,7 @@ public class PacketConsumers {
|
||||
}
|
||||
};
|
||||
|
||||
private static Pair<Boolean, String> processClientString(String original, ImageManager manager) {
|
||||
private static Pair<Boolean, String> processClientString(String original, FontManager manager) {
|
||||
if (original.isEmpty()) {
|
||||
return Pair.of(false, original);
|
||||
}
|
||||
@@ -884,7 +888,7 @@ public class PacketConsumers {
|
||||
boolean hasIllegal = false;
|
||||
for (int i = 0; i < codepoints.length; i++) {
|
||||
int codepoint = codepoints[i];
|
||||
if (manager.isIllegalCharacter(codepoint)) {
|
||||
if (manager.isIllegalCodepoint(codepoint)) {
|
||||
newCodepoints[i] = '*';
|
||||
hasIllegal = true;
|
||||
} else {
|
||||
@@ -894,14 +898,14 @@ public class PacketConsumers {
|
||||
return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original);
|
||||
}
|
||||
|
||||
private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer<String> callback) {
|
||||
private static void runIfContainsIllegalCharacter(String string, FontManager manager, Consumer<String> callback) {
|
||||
if (string.isEmpty()) return;
|
||||
int[] codepoints = CharacterUtils.charsToCodePoints(string.toCharArray());
|
||||
int[] newCodepoints = new int[codepoints.length];
|
||||
boolean hasIllegal = false;
|
||||
for (int i = 0; i < codepoints.length; i++) {
|
||||
int codepoint = codepoints[i];
|
||||
if (!manager.isIllegalCharacter(codepoint)) {
|
||||
if (!manager.isIllegalCodepoint(codepoint)) {
|
||||
newCodepoints[i] = codepoint;
|
||||
} else {
|
||||
newCodepoints[i] = '*';
|
||||
@@ -957,4 +961,44 @@ public class PacketConsumers {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SET_ENTITY_DATA = (user, event, packet) -> {
|
||||
try {
|
||||
int id = (int) Reflections.field$ClientboundSetEntityDataPacket$id.get(packet);
|
||||
Object player = user.serverPlayer();
|
||||
Object level = Reflections.method$Entity$level.invoke(player);
|
||||
Object entityLookup = Reflections.method$Level$moonrise$getEntityLookup.invoke(level);
|
||||
Object entity = Reflections.method$EntityLookup$get.invoke(entityLookup, id);
|
||||
if (entity == null) return;
|
||||
Object entityType = Reflections.method$Entity$getType.invoke(entity);
|
||||
if (entityType == Reflections.instance$EntityType$BLOCK_DISPLAY) {
|
||||
List<Object> packedItems = (List<Object>) Reflections.field$ClientboundSetEntityDataPacket$packedItems.get(packet);
|
||||
for (int i = 0; i < packedItems.size(); i++) {
|
||||
Object packedItem = packedItems.get(i);
|
||||
int entityDataId = (int) Reflections.field$SynchedEntityData$DataValue$id.get(packedItem);
|
||||
if ((VersionHelper.isVersionNewerThan1_20_2() && entityDataId != 23)
|
||||
|| (!VersionHelper.isVersionNewerThan1_20_2() && entityDataId != 22)) {
|
||||
continue;
|
||||
}
|
||||
Object blockState = Reflections.field$SynchedEntityData$DataValue$value.get(packedItem);
|
||||
Object serializer = Reflections.field$SynchedEntityData$DataValue$serializer.get(packedItem);
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
int newStateId;
|
||||
if (!user.clientModEnabled()) {
|
||||
newStateId = remap(stateId);
|
||||
} else {
|
||||
newStateId = remapMOD(stateId);
|
||||
}
|
||||
packedItems.set(i, Reflections.constructor$SynchedEntityData$DataValue.newInstance(
|
||||
entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId)
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// todo修改其他实体的物品的方块谓词
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityDataPacket", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.network.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PacketIdFinder {
|
||||
private static final Map<String, Map<String, Integer>> gamePacketIds = new HashMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
Object packetReport = Reflections.constructor$PacketReport.newInstance((Object) null);
|
||||
JsonElement jsonElement = (JsonElement) Reflections.method$PacketReport$serializePackets.invoke(packetReport);
|
||||
var play = jsonElement.getAsJsonObject().get("play");
|
||||
for (var entry : play.getAsJsonObject().entrySet()) {
|
||||
Map<String, Integer> ids = new HashMap<>();
|
||||
gamePacketIds.put(entry.getKey(), ids);
|
||||
for (var entry2 : entry.getValue().getAsJsonObject().entrySet()) {
|
||||
ids.put(entry2.getKey(), entry2.getValue().getAsJsonObject().get("protocol_id").getAsInt());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to get packets", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int clientboundByName(String packetName) {
|
||||
return gamePacketIds.get("clientbound").get(packetName);
|
||||
}
|
||||
}
|
||||
@@ -2,30 +2,30 @@ package net.momirealms.craftengine.bukkit.plugin.network.impl;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketIds;
|
||||
|
||||
public class PacketIds1_21_5 implements PacketIds {
|
||||
public class PacketIds1_21 implements PacketIds {
|
||||
|
||||
@Override
|
||||
public int clientboundBlockUpdatePacket() {
|
||||
return 8;
|
||||
return PacketIdFinder.clientboundByName("minecraft:block_update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundSectionBlocksUpdatePacket() {
|
||||
return 72;
|
||||
return PacketIdFinder.clientboundByName("minecraft:section_blocks_update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundLevelParticlesPacket() {
|
||||
return 40;
|
||||
return PacketIdFinder.clientboundByName("minecraft:level_particles");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundLevelEventPacket() {
|
||||
return 39;
|
||||
return PacketIdFinder.clientboundByName("minecraft:level_event");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundAddEntityPacket() {
|
||||
return 1;
|
||||
return PacketIdFinder.clientboundByName("minecraft:add_entity");
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.network.impl;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketIds;
|
||||
|
||||
@Deprecated
|
||||
public class PacketIds1_21_2 implements PacketIds {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.user;
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.channel.Channel;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
@@ -13,6 +14,7 @@ import net.momirealms.craftengine.core.block.PackedBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
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.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
@@ -437,16 +439,26 @@ public class BukkitServerPlayer extends Player {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.miningProgress = (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(this.destroyedState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos) + miningProgress;
|
||||
|
||||
float progressToAdd = (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(this.destroyedState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
int id = BlockStateUtils.blockStateToId(this.destroyedState);
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (customState != null && !customState.isEmpty()
|
||||
&& !customState.settings().isCorrectTool(item == null ? ItemKeys.AIR : item.id())) {
|
||||
progressToAdd *= customState.settings().incorrectToolSpeed();
|
||||
}
|
||||
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
|
||||
if (this.miningProgress >= 1f) {
|
||||
//Reflections.method$ServerLevel$levelEvent.invoke(Reflections.field$CraftWorld$ServerLevel.get(player.getWorld()), null, 2001, blockPos, BlockStateUtils.blockStateToId(this.destroyedState));
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, blockPos, BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, blockPos, id, false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
this.stopMiningBlock();
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package net.momirealms.craftengine.bukkit.sound;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.sound.song.AbstractJukeboxSongManager;
|
||||
import net.momirealms.craftengine.core.sound.song.JukeboxSong;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class BukkitJukeboxSongManager extends AbstractJukeboxSongManager {
|
||||
|
||||
public BukkitJukeboxSongManager(CraftEngine plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerSongs(Map<Key, JukeboxSong> songs) {
|
||||
if (songs.isEmpty()) return;
|
||||
try {
|
||||
unfreezeRegistry();
|
||||
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
|
||||
Key id = entry.getKey();
|
||||
JukeboxSong jukeboxSong = entry.getValue();
|
||||
Object resourceLocation = Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, id.namespace(), id.value());
|
||||
Object soundId = Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, jukeboxSong.sound().namespace(), jukeboxSong.sound().value());
|
||||
Object song = Reflections.method$Registry$get.invoke(Reflections.instance$InternalRegistries$JUKEBOX_SONG, resourceLocation);
|
||||
|
||||
Object soundEvent = VersionHelper.isVersionNewerThan1_21_2() ?
|
||||
Reflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) :
|
||||
Reflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false);
|
||||
Object soundHolder = Reflections.method$Holder$direct.invoke(null, soundEvent);
|
||||
|
||||
if (song == null) {
|
||||
song = Reflections.constructor$JukeboxSong.newInstance(soundHolder, ComponentUtils.adventureToMinecraft(jukeboxSong.description()), jukeboxSong.lengthInSeconds(), jukeboxSong.comparatorOutput());
|
||||
Object holder = Reflections.method$Registry$registerForHolder.invoke(null, Reflections.instance$InternalRegistries$JUKEBOX_SONG, resourceLocation, song);
|
||||
Reflections.method$Holder$Reference$bindValue.invoke(holder, song);
|
||||
Reflections.field$Holder$Reference$tags.set(holder, Set.of());
|
||||
}
|
||||
}
|
||||
freezeRegistry();
|
||||
} catch (Exception e) {
|
||||
plugin.logger().warn("Failed to register jukebox songs.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void unfreezeRegistry() throws IllegalAccessException {
|
||||
Reflections.field$MappedRegistry$frozen.set(Reflections.instance$InternalRegistries$JUKEBOX_SONG, false);
|
||||
}
|
||||
|
||||
private void freezeRegistry() throws IllegalAccessException {
|
||||
Reflections.field$MappedRegistry$frozen.set(Reflections.instance$InternalRegistries$JUKEBOX_SONG, true);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
package net.momirealms.craftengine.bukkit.sound;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.sound.AbstractSoundManager;
|
||||
import net.momirealms.craftengine.core.sound.song.JukeboxSongManager;
|
||||
import net.momirealms.craftengine.core.sound.JukeboxSong;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class BukkitSoundManager extends AbstractSoundManager {
|
||||
|
||||
@@ -11,7 +19,40 @@ public class BukkitSoundManager extends AbstractSoundManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JukeboxSongManager createJukeboxSongManager() {
|
||||
return new BukkitJukeboxSongManager(super.plugin);
|
||||
protected void registerSongs(Map<Key, JukeboxSong> songs) {
|
||||
if (songs.isEmpty()) return;
|
||||
try {
|
||||
unfreezeRegistry();
|
||||
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
|
||||
Key id = entry.getKey();
|
||||
JukeboxSong jukeboxSong = entry.getValue();
|
||||
Object resourceLocation = Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, id.namespace(), id.value());
|
||||
Object soundId = Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, jukeboxSong.sound().namespace(), jukeboxSong.sound().value());
|
||||
Object song = Reflections.method$Registry$get.invoke(Reflections.instance$InternalRegistries$JUKEBOX_SONG, resourceLocation);
|
||||
|
||||
Object soundEvent = VersionHelper.isVersionNewerThan1_21_2() ?
|
||||
Reflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) :
|
||||
Reflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false);
|
||||
Object soundHolder = Reflections.method$Holder$direct.invoke(null, soundEvent);
|
||||
|
||||
if (song == null) {
|
||||
song = Reflections.constructor$JukeboxSong.newInstance(soundHolder, ComponentUtils.adventureToMinecraft(jukeboxSong.description()), jukeboxSong.lengthInSeconds(), jukeboxSong.comparatorOutput());
|
||||
Object holder = Reflections.method$Registry$registerForHolder.invoke(null, Reflections.instance$InternalRegistries$JUKEBOX_SONG, resourceLocation, song);
|
||||
Reflections.method$Holder$Reference$bindValue.invoke(holder, song);
|
||||
Reflections.field$Holder$Reference$tags.set(holder, Set.of());
|
||||
}
|
||||
}
|
||||
freezeRegistry();
|
||||
} catch (Exception e) {
|
||||
plugin.logger().warn("Failed to register jukebox songs.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void unfreezeRegistry() throws IllegalAccessException {
|
||||
Reflections.field$MappedRegistry$frozen.set(Reflections.instance$InternalRegistries$JUKEBOX_SONG, false);
|
||||
}
|
||||
|
||||
private void freezeRegistry() throws IllegalAccessException {
|
||||
Reflections.field$MappedRegistry$frozen.set(Reflections.instance$InternalRegistries$JUKEBOX_SONG, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BlockStateUtils {
|
||||
} else {
|
||||
String blockTypeString = blockState.substring(0, index);
|
||||
Key block = Key.of(blockTypeString);
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().getBlock(block);
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().blockById(block);
|
||||
if (optionalCustomBlock.isPresent()) {
|
||||
ImmutableBlockState state = BlockStateParser.deserialize(blockState);
|
||||
if (state == null) {
|
||||
@@ -55,7 +55,7 @@ public class BlockStateUtils {
|
||||
}
|
||||
|
||||
public static List<Object> getAllBlockStates(Key block) {
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().getBlock(block);
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().blockById(block);
|
||||
return optionalCustomBlock.map(customBlock -> customBlock.variantProvider().states().stream().map(it -> it.customBlockState().handle()).toList())
|
||||
.orElseGet(() -> getAllVanillaBlockStates(block));
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class BlockStateUtils {
|
||||
}
|
||||
|
||||
public static BlockData fromBlockData(Object blockState) {
|
||||
return (BlockData) FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState);
|
||||
return FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState);
|
||||
}
|
||||
|
||||
public static int blockDataToId(BlockData blockData) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
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.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
@@ -77,16 +77,16 @@ public class InteractUtils {
|
||||
return false;
|
||||
});
|
||||
register(BlockKeys.SOUL_CAMPFIRE, (player, item, blockState, result) -> {
|
||||
if (!ConfigManager.enableRecipeSystem()) return false;
|
||||
if (!Config.enableRecipeSystem()) return false;
|
||||
Optional<Holder.Reference<Key>> optional = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(item.id());
|
||||
return optional.filter(keyReference -> BukkitRecipeManager.instance().getRecipe(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new OptimizedIDItem<>(
|
||||
return optional.filter(keyReference -> BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new OptimizedIDItem<>(
|
||||
keyReference, item.getItem()
|
||||
))) != null).isPresent();
|
||||
});
|
||||
register(BlockKeys.CAMPFIRE, (player, item, blockState, result) -> {
|
||||
if (!ConfigManager.enableRecipeSystem()) return false;
|
||||
if (!Config.enableRecipeSystem()) return false;
|
||||
Optional<Holder.Reference<Key>> optional = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(item.id());
|
||||
return optional.filter(keyReference -> BukkitRecipeManager.instance().getRecipe(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new OptimizedIDItem<>(
|
||||
return optional.filter(keyReference -> BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new OptimizedIDItem<>(
|
||||
keyReference, item.getItem()
|
||||
))) != null).isPresent();
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
public class NoteBlockChainUpdateUtils {
|
||||
|
||||
private NoteBlockChainUpdateUtils() {}
|
||||
|
||||
public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) throws ReflectiveOperationException {
|
||||
if (times >= ConfigManager.maxChainUpdate()) return;
|
||||
if (times >= Config.maxChainUpdate()) return;
|
||||
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
|
||||
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
|
||||
if (BlockStateUtils.isClientSideNoteBlock(state)) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Item;
|
||||
@@ -11,6 +15,9 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class PlayerUtils {
|
||||
@@ -140,4 +147,28 @@ public class PlayerUtils {
|
||||
|
||||
return actualAmount;
|
||||
}
|
||||
|
||||
public static void sendTotemAnimation(Player player, ItemStack totem) {
|
||||
ItemStack offhandItem = player.getInventory().getItemInOffHand();
|
||||
List<Object> packets = new ArrayList<>();
|
||||
try {
|
||||
Object previousItem = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, offhandItem);
|
||||
Object totemItem = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, totem);
|
||||
|
||||
Object packet1 = Reflections.constructor$ClientboundSetEquipmentPacket
|
||||
.newInstance(player.getEntityId(), List.of(Pair.of(Reflections.instance$EquipmentSlot$OFFHAND, totemItem)));
|
||||
Object packet2 = Reflections.constructor$ClientboundEntityEventPacket
|
||||
.newInstance(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player), (byte) 35);
|
||||
Object packet3 = Reflections.constructor$ClientboundSetEquipmentPacket
|
||||
.newInstance(player.getEntityId(), List.of(Pair.of(Reflections.instance$EquipmentSlot$OFFHAND, previousItem)));
|
||||
packets.add(packet1);
|
||||
packets.add(packet2);
|
||||
packets.add(packet3);
|
||||
|
||||
Object bundlePacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
BukkitNetworkManager.instance().sendPacket(player, bundlePacket);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
BukkitCraftEngine.instance().logger().warn("Failed to send totem animation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.JsonElement;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@@ -809,6 +810,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$SynchedEntityData$DataValue$serializer = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$SynchedEntityData$DataValue, 1
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$SynchedEntityData$DataValue$value = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$SynchedEntityData$DataValue, 2
|
||||
@@ -3027,6 +3034,32 @@ public class Reflections {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Class<?> clazz$ClientboundSetEquipmentPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSetEquipmentPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityEquipment")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundSetEquipmentPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundSetEquipmentPacket, int.class, List.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ClientboundEntityEventPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundEntityEventPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityStatus")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundEntityEventPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundEntityEventPacket, clazz$Entity, byte.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$Block$defaultBlockState = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$Block, clazz$BlockState
|
||||
@@ -3666,6 +3699,7 @@ public class Reflections {
|
||||
|
||||
public static final Object instance$EntityType$TEXT_DISPLAY;
|
||||
public static final Object instance$EntityType$ITEM_DISPLAY;
|
||||
public static final Object instance$EntityType$BLOCK_DISPLAY;
|
||||
public static final Object instance$EntityType$FALLING_BLOCK;
|
||||
public static final Object instance$EntityType$INTERACTION;
|
||||
public static final Object instance$EntityType$SHULKER;
|
||||
@@ -3676,6 +3710,8 @@ public class Reflections {
|
||||
instance$EntityType$TEXT_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, textDisplay);
|
||||
Object itemDisplay = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "item_display");
|
||||
instance$EntityType$ITEM_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, itemDisplay);
|
||||
Object blockDisplay = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "block_display");
|
||||
instance$EntityType$BLOCK_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, blockDisplay);
|
||||
Object fallingBlock = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "falling_block");
|
||||
instance$EntityType$FALLING_BLOCK = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, fallingBlock);
|
||||
Object interaction = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "interaction");
|
||||
@@ -6050,4 +6086,58 @@ public class Reflections {
|
||||
clazz$BlockStateBase, clazz$BlockState, clazz$Mirror
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundMoveEntityPacket$Pos = requireNonNull(
|
||||
ReflectionUtils.getDeclaredConstructor(
|
||||
clazz$ClientboundMoveEntityPacket$Pos, int.class, short.class, short.class, short.class, boolean.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$Entity$getType = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$Entity, clazz$EntityType
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$SynchedEntityData$DataValue = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$SynchedEntityData$DataValue, int.class, clazz$EntityDataSerializer, Object.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$EntityLookup = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
"ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup",
|
||||
"io.papermc.paper.chunk.system.entity.EntityLookup"
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$Level$moonrise$getEntityLookup = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
VersionHelper.isVersionNewerThan1_21() ? clazz$Level : clazz$ServerLevel,
|
||||
clazz$EntityLookup
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$EntityLookup$get = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$EntityLookup, clazz$Entity, int.class
|
||||
)
|
||||
);
|
||||
|
||||
// 1.21+
|
||||
public static final Class<?> clazz$PacketReport =
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("data.info.PacketReport")
|
||||
);
|
||||
|
||||
// 1.21+
|
||||
public static final Constructor<?> constructor$PacketReport = Optional.ofNullable(clazz$PacketReport)
|
||||
.map(it -> ReflectionUtils.getConstructor(it, 0))
|
||||
.orElse(null);
|
||||
|
||||
// 1.21+
|
||||
public static final Method method$PacketReport$serializePackets = Optional.ofNullable(clazz$PacketReport)
|
||||
.map(it -> ReflectionUtils.getDeclaredMethod(it, JsonElement.class))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.world;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.util.LightUtils;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.SectionPosUtils;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
@@ -20,7 +20,7 @@ public class BukkitCEWorld extends CEWorld {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (ConfigManager.enableLightSystem()) {
|
||||
if (Config.enableLightSystem()) {
|
||||
LightUtils.updateChunkLight((org.bukkit.World) world.platformWorld(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1));
|
||||
super.updatedSectionPositions.clear();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
@@ -265,7 +265,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e);
|
||||
return;
|
||||
} finally {
|
||||
if (ConfigManager.restoreVanillaBlocks()) {
|
||||
if (Config.restoreVanillaBlocks()) {
|
||||
CESection[] ceSections = ceChunk.sections();
|
||||
Object worldServer = FastNMS.INSTANCE.field$CraftChunk$worldServer(chunk);
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
|
||||
@@ -313,7 +313,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
for (int i = 0; i < ceSections.length; i++) {
|
||||
CESection ceSection = ceSections[i];
|
||||
Object section = sections[i];
|
||||
if (ConfigManager.syncCustomBlocks()) {
|
||||
if (Config.syncCustomBlocks()) {
|
||||
Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section);
|
||||
Object data = Reflections.varHandle$PalettedContainer$data.get(statesContainer);
|
||||
Object palette = Reflections.field$PalettedContainer$Data$palette.get(data);
|
||||
@@ -362,7 +362,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ConfigManager.restoreCustomBlocks()) {
|
||||
if (Config.restoreCustomBlocks()) {
|
||||
if (!ceSection.statesContainer().isEmpty()) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
@@ -378,7 +378,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
}
|
||||
BukkitInjector.injectLevelChunkSection(section, ceSection, ceWorld, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z));
|
||||
}
|
||||
if (ConfigManager.enableRecipeSystem()) {
|
||||
if (Config.enableRecipeSystem()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> blockEntities = (Map<Object, Object>) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk);
|
||||
for (Object blockEntity : blockEntities.values()) {
|
||||
|
||||
@@ -40,7 +40,7 @@ tasks.remapJar {
|
||||
inputFile.set(tasks.shadowJar.get().archiveFile)
|
||||
|
||||
destinationDirectory.set(file("$rootDir/target"))
|
||||
archiveFileName.set("${base.archivesName.get()}-${project.version}.jar")
|
||||
archiveFileName.set("${base.archivesName.get()}-${project.version}+${rootProject.properties["latest_minecraft_version"]}.jar")
|
||||
}
|
||||
|
||||
loom {
|
||||
|
||||
@@ -49,6 +49,8 @@ dependencies {
|
||||
compileOnly("commons-io:commons-io:${rootProject.properties["commons_io_version"]}")
|
||||
// Data Fixer Upper
|
||||
compileOnly("com.mojang:datafixerupper:${rootProject.properties["datafixerupper_version"]}")
|
||||
// Aho-Corasick java implementation
|
||||
compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}")
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -75,6 +77,7 @@ tasks {
|
||||
relocate("com.saicone.rtag", "net.momirealms.craftengine.libraries.rtag")
|
||||
relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml")
|
||||
relocate("net.kyori", "net.momirealms.craftengine.libraries")
|
||||
relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
|
||||
relocate("net.momirealms.sparrow.nbt", "net.momirealms.craftengine.libraries.nbt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,62 @@ package net.momirealms.craftengine.core.block;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractBlockManager extends AbstractModelGenerator implements BlockManager {
|
||||
// CraftEngine objects
|
||||
protected final Map<Key, CustomBlock> byId = new HashMap<>();
|
||||
// Cached command suggestions
|
||||
protected final List<Suggestion> cachedSuggestions = new ArrayList<>();
|
||||
// Cached Namespace
|
||||
protected final Set<String> namespacesInUse = new HashSet<>();
|
||||
|
||||
public AbstractBlockManager(CraftEngine plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key, CustomBlock> blocks() {
|
||||
return Collections.unmodifiableMap(this.byId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CustomBlock> blockById(Key id) {
|
||||
return Optional.ofNullable(this.byId.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.clearModelsToGenerate();
|
||||
this.cachedSuggestions.clear();
|
||||
this.byId.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Suggestion> cachedSuggestions() {
|
||||
return Collections.unmodifiableCollection(this.cachedSuggestions);
|
||||
}
|
||||
|
||||
public Set<String> namespacesInUse() {
|
||||
return Collections.unmodifiableSet(this.namespacesInUse);
|
||||
}
|
||||
|
||||
protected void initSuggestions() {
|
||||
this.cachedSuggestions.clear();
|
||||
this.namespacesInUse.clear();
|
||||
Set<String> states = new HashSet<>();
|
||||
for (CustomBlock block : this.byId.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package net.momirealms.craftengine.core.block;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
@@ -13,12 +12,9 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BlockManager extends Reloadable, ModelGenerator, ConfigSectionParser {
|
||||
String CONFIG_SECTION_NAME = "blocks";
|
||||
public interface BlockManager extends Manageable, ModelGenerator {
|
||||
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
ConfigSectionParser parser();
|
||||
|
||||
Collection<ModelGeneration> modelsToGenerate();
|
||||
|
||||
@@ -28,19 +24,9 @@ public interface BlockManager extends Reloadable, ModelGenerator, ConfigSectionP
|
||||
|
||||
Map<Key, CustomBlock> blocks();
|
||||
|
||||
Optional<CustomBlock> getBlock(Key key);
|
||||
Optional<CustomBlock> blockById(Key key);
|
||||
|
||||
Collection<Suggestion> cachedSuggestions();
|
||||
|
||||
Map<Key, Key> soundMapper();
|
||||
|
||||
void initSuggestions();
|
||||
|
||||
void delayedLoad();
|
||||
|
||||
void delayedInit();
|
||||
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class BlockSettings {
|
||||
@Nullable
|
||||
Key itemId;
|
||||
Set<Key> tags = Set.of();
|
||||
float incorrectToolSpeed = 0.3f;
|
||||
Set<Key> correctTools = Set.of();
|
||||
String name;
|
||||
|
||||
@@ -81,6 +82,7 @@ public class BlockSettings {
|
||||
newSettings.fluidState = settings.fluidState;
|
||||
newSettings.blockLight = settings.blockLight;
|
||||
newSettings.name = settings.name;
|
||||
newSettings.incorrectToolSpeed = settings.incorrectToolSpeed;
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
@@ -124,6 +126,10 @@ public class BlockSettings {
|
||||
return canOcclude;
|
||||
}
|
||||
|
||||
public float incorrectToolSpeed() {
|
||||
return incorrectToolSpeed;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
@@ -243,6 +249,11 @@ public class BlockSettings {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockSettings incorrectToolSpeed(float incorrectToolSpeed) {
|
||||
this.incorrectToolSpeed = incorrectToolSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockSettings isRandomlyTicking(boolean isRandomlyTicking) {
|
||||
this.isRandomlyTicking = isRandomlyTicking;
|
||||
return this;
|
||||
@@ -381,6 +392,10 @@ public class BlockSettings {
|
||||
List<String> tools = MiscUtils.getAsStringList(value);
|
||||
return settings -> settings.correctTools(tools.stream().map(Key::of).collect(Collectors.toSet()));
|
||||
}));
|
||||
registerFactory("incorrect-tool-dig-speed", (value -> {
|
||||
float floatValue = MiscUtils.getAsFloat(value);
|
||||
return settings -> settings.incorrectToolSpeed(floatValue);
|
||||
}));
|
||||
registerFactory("name", (value -> {
|
||||
String name = value.toString();
|
||||
return settings -> settings.name(name);
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Properties {
|
||||
public static Property<?> fromMap(String name, Map<String, Object> map) {
|
||||
String type = (String) map.getOrDefault("type", "empty");
|
||||
if (type == null) {
|
||||
throw new NullPointerException("behavior type cannot be null");
|
||||
throw new NullPointerException("Property type cannot be null");
|
||||
}
|
||||
Key key = Key.withDefaultNamespace(type, "craftengine");
|
||||
PropertyFactory factory = BuiltInRegistries.PROPERTY_FACTORY.getValue(key);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.momirealms.craftengine.core.entity.furniture;
|
||||
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractFurnitureManager implements FurnitureManager {
|
||||
protected final Map<Key, CustomFurniture> byId = new HashMap<>();
|
||||
// Cached command suggestions
|
||||
private final List<Suggestion> cachedSuggestions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void delayedLoad() {
|
||||
this.initSuggestions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSuggestions() {
|
||||
this.cachedSuggestions.clear();
|
||||
for (Key key : this.byId.keySet()) {
|
||||
this.cachedSuggestions.add(Suggestion.suggestion(key.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Suggestion> cachedSuggestions() {
|
||||
return Collections.unmodifiableCollection(this.cachedSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CustomFurniture> furnitureById(Key id) {
|
||||
return Optional.ofNullable(this.byId.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.byId.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.momirealms.craftengine.core.entity.furniture;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Furniture {
|
||||
void initializeColliders();
|
||||
|
||||
Vec3d position();
|
||||
|
||||
World world();
|
||||
|
||||
boolean isValid();
|
||||
|
||||
void destroy();
|
||||
|
||||
void destroySeats();
|
||||
|
||||
Optional<Seat> findFirstAvailableSeat(int targetEntityId);
|
||||
|
||||
boolean removeOccupiedSeat(Vector3f seat);
|
||||
|
||||
default boolean removeOccupiedSeat(Seat seat) {
|
||||
return this.removeOccupiedSeat(seat.offset());
|
||||
}
|
||||
|
||||
boolean tryOccupySeat(Seat seat);
|
||||
|
||||
UUID uuid();
|
||||
|
||||
int baseEntityId();
|
||||
|
||||
@NotNull AnchorType anchorType();
|
||||
|
||||
@NotNull Key id();
|
||||
|
||||
@NotNull CustomFurniture config();
|
||||
|
||||
boolean hasExternalModel();
|
||||
|
||||
void spawnSeatEntityForPlayer(Player player, Seat seat);
|
||||
}
|
||||
@@ -1,37 +1,40 @@
|
||||
package net.momirealms.craftengine.core.entity.furniture;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.entity.Entity;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FurnitureManager extends Reloadable, ConfigSectionParser {
|
||||
String CONFIG_SECTION_NAME = "furniture";
|
||||
public interface FurnitureManager extends Manageable {
|
||||
String FURNITURE_ADMIN_NODE = "craftengine.furniture.admin";
|
||||
|
||||
void delayedLoad();
|
||||
ConfigSectionParser parser();
|
||||
|
||||
void initSuggestions();
|
||||
|
||||
Collection<Suggestion> cachedSuggestions();
|
||||
|
||||
void delayedInit();
|
||||
Furniture place(CustomFurniture furniture, Vec3d vec3d, World world, AnchorType anchorType, boolean playSound);
|
||||
|
||||
@Override
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.FURNITURE;
|
||||
}
|
||||
|
||||
Optional<CustomFurniture> getFurniture(Key id);
|
||||
Optional<CustomFurniture> furnitureById(Key id);
|
||||
|
||||
boolean isFurnitureRealEntity(int entityId);
|
||||
|
||||
@Nullable
|
||||
Furniture loadedFurnitureByRealEntityId(int entityId);
|
||||
|
||||
@Nullable
|
||||
default Furniture loadedFurnitureByRealEntity(Entity entity) {
|
||||
return loadedFurnitureByRealEntityId(entity.entityID());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Furniture loadedFurnitureByEntityId(int entityId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
package net.momirealms.craftengine.core.font;
|
||||
|
||||
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.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractFontManager implements FontManager {
|
||||
private final CraftEngine plugin;
|
||||
// namespace:font font
|
||||
private final Map<Key, Font> fonts = new HashMap<>();
|
||||
// namespace:id image
|
||||
private final Map<Key, BitmapImage> images = new HashMap<>();
|
||||
private final Set<Integer> illegalChars = new HashSet<>();
|
||||
private final ImageParser imageParser;
|
||||
private final EmojiParser emojiParser;
|
||||
|
||||
private OffsetFont offsetFont;
|
||||
|
||||
public AbstractFontManager(CraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
this.imageParser = new ImageParser();
|
||||
this.emojiParser = new EmojiParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("offset-characters"))
|
||||
.map(OffsetFont::new)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.fonts.clear();
|
||||
this.images.clear();
|
||||
this.illegalChars.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSectionParser[] parsers() {
|
||||
return new ConfigSectionParser[] {this.imageParser, this.emojiParser};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedLoad() {
|
||||
Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultFontInUse() {
|
||||
return !this.illegalChars.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIllegalCodepoint(int codepoint) {
|
||||
return this.illegalChars.contains(codepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Font> fonts() {
|
||||
return Collections.unmodifiableCollection(this.fonts.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BitmapImage> bitmapImageByCodepoint(Key font, int codepoint) {
|
||||
return fontById(font).map(f -> f.bitmapImageByCodepoint(codepoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BitmapImage> bitmapImageByImageId(Key id) {
|
||||
return Optional.ofNullable(this.images.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int codepointByImageId(Key key, int x, int y) {
|
||||
BitmapImage image = this.images.get(key);
|
||||
if (image == null) return -1;
|
||||
return image.codepointAt(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createOffsets(int offset, FontTagFormatter tagFormatter) {
|
||||
return Optional.ofNullable(this.offsetFont).map(it -> it.createOffset(offset, tagFormatter)).orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Font> fontById(Key id) {
|
||||
return Optional.ofNullable(this.fonts.get(id));
|
||||
}
|
||||
|
||||
private Font getOrCreateFont(Key key) {
|
||||
return this.fonts.computeIfAbsent(key, Font::new);
|
||||
}
|
||||
|
||||
public class EmojiParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"emoji", "emojis"};
|
||||
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.EMOJI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"images", "image"};
|
||||
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.IMAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
if (images.containsKey(id)) {
|
||||
TranslationManager.instance().log("warning.config.image.duplicated", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
Object heightObj = section.get("height");
|
||||
if (heightObj == null) {
|
||||
TranslationManager.instance().log("warning.config.image.lack_height", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
int height = MiscUtils.getAsInt(heightObj);
|
||||
int ascent = MiscUtils.getAsInt(section.getOrDefault("ascent", height - 1));
|
||||
if (height < ascent) {
|
||||
TranslationManager.instance().log("warning.config.image.height_smaller_than_ascent", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
Object file = section.get("file");
|
||||
if (file == null) {
|
||||
TranslationManager.instance().log("warning.config.image.no_file", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
String resourceLocation = file.toString().replace("\\", "/");
|
||||
if (!ResourceLocation.isValid(resourceLocation)) {
|
||||
TranslationManager.instance().log("warning.config.image.invalid_resource_location", path.toString(), id.toString(), resourceLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
String fontName = (String) section.getOrDefault("font", "minecraft:default");
|
||||
if (!ResourceLocation.isValid(fontName)) {
|
||||
TranslationManager.instance().log("warning.config.image.invalid_font_name", path.toString(), id.toString(), fontName);
|
||||
return;
|
||||
}
|
||||
|
||||
Key fontKey = Key.withDefaultNamespace(fontName, id.namespace());
|
||||
Font font = getOrCreateFont(fontKey);
|
||||
List<char[]> chars;
|
||||
if (section.containsKey("chars")) {
|
||||
chars = MiscUtils.getAsStringList(section.get("chars")).stream().map(it -> {
|
||||
if (it.startsWith("\\u")) {
|
||||
return CharacterUtils.decodeUnicodeToChars(it);
|
||||
} else {
|
||||
return it.toCharArray();
|
||||
}
|
||||
}).toList();
|
||||
} else {
|
||||
String character = (String) section.get("char");
|
||||
if (character == null) {
|
||||
TranslationManager.instance().log("warning.config.image.lack_char", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
if (character.length() == 1) {
|
||||
chars = List.of(character.toCharArray());
|
||||
} else {
|
||||
chars = List.of(CharacterUtils.decodeUnicodeToChars(character));
|
||||
}
|
||||
}
|
||||
|
||||
int size = -1;
|
||||
int[][] codepointGrid = new int[chars.size()][];
|
||||
for (int i = 0; i < chars.size(); ++i) {
|
||||
int[] codepoints = CharacterUtils.charsToCodePoints(chars.get(i));
|
||||
for (int codepoint : codepoints) {
|
||||
if (font.isCodepointInUse(codepoint)) {
|
||||
BitmapImage image = font.bitmapImageByCodepoint(codepoint);
|
||||
TranslationManager.instance().log("warning.config.image.codepoint_in_use",
|
||||
path.toString(),
|
||||
id.toString(),
|
||||
fontKey.toString(),
|
||||
CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)),
|
||||
new String(Character.toChars(codepoint)),
|
||||
image.id().toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
codepointGrid[i] = codepoints;
|
||||
if (size == -1) size = codepoints.length;
|
||||
if (size != codepoints.length) {
|
||||
TranslationManager.instance().log("warning.config.image.invalid_codepoint_grid", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resourceLocation.endsWith(".png")) resourceLocation += ".png";
|
||||
Key namespacedPath = Key.of(resourceLocation);
|
||||
Path targetImagePath = pack.resourcePackFolder()
|
||||
.resolve("assets")
|
||||
.resolve(namespacedPath.namespace())
|
||||
.resolve("textures")
|
||||
.resolve(namespacedPath.value());
|
||||
|
||||
if (!Files.exists(targetImagePath)) {
|
||||
TranslationManager.instance().log("warning.config.image.file_not_exist", path.toString(), id.toString(), targetImagePath.toString());
|
||||
// DO NOT RETURN, JUST GIVE WARNINGS
|
||||
}
|
||||
|
||||
BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid);
|
||||
for (int[] y : codepointGrid) {
|
||||
for (int x : y) {
|
||||
font.addBitMapImage(x, bitmapImage);
|
||||
}
|
||||
}
|
||||
|
||||
AbstractFontManager.this.images.put(id, bitmapImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package net.momirealms.craftengine.core.font;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.PreConditions;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public abstract class AbstractImageManager implements ImageManager {
|
||||
private final CraftEngine plugin;
|
||||
// namespace:font font
|
||||
private final HashMap<Key, Font> fonts = new HashMap<>();
|
||||
// namespace:id image
|
||||
private final HashMap<Key, BitmapImage> images = new HashMap<>();
|
||||
private final Set<Integer> illegalChars = new HashSet<>();
|
||||
|
||||
private OffsetFont offsetFont;
|
||||
|
||||
public AbstractImageManager(CraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.offsetFont = Optional.ofNullable(plugin.configManager().settings().getSection("offset-characters"))
|
||||
.map(OffsetFont::new)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.fonts.clear();
|
||||
this.images.clear();
|
||||
this.illegalChars.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedLoad() {
|
||||
Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> {
|
||||
this.illegalChars.addAll(font.codepointsInUse());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultFontInUse() {
|
||||
return !this.illegalChars.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIllegalCharacter(int codepoint) {
|
||||
return this.illegalChars.contains(codepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
int height = MiscUtils.getAsInt(section.get("height"));
|
||||
int ascent = MiscUtils.getAsInt(section.get("ascent"));
|
||||
if (PreConditions.runIfTrue(height < ascent,
|
||||
() -> this.plugin.logger().warn(path, "Illegal ascent found at " + id + ". Height should be no lower than ascent"))) return;
|
||||
|
||||
String file = (String) section.get("file");
|
||||
if (PreConditions.isNull(file,
|
||||
() -> this.plugin.logger().warn(path, "`file` option is not set in image " + id))) return;
|
||||
|
||||
String fontName = (String) section.getOrDefault("font", "minecraft:default");
|
||||
if (PreConditions.isNull(fontName,
|
||||
() -> this.plugin.logger().warn(path, "`font` option is not set in image " + id))) return;
|
||||
|
||||
Key fontKey = Key.withDefaultNamespace(fontName, id.namespace());
|
||||
// get the font
|
||||
Font font = this.getOrCreateFont(fontKey);
|
||||
List<char[]> chars;
|
||||
if (section.containsKey("chars")) {
|
||||
chars = MiscUtils.getAsStringList(section.get("chars")).stream().map(it -> {
|
||||
if (it.startsWith("\\u")) {
|
||||
return CharacterUtils.decodeUnicodeToChars(it);
|
||||
} else {
|
||||
return it.toCharArray();
|
||||
}
|
||||
}).toList();
|
||||
} else {
|
||||
String character = (String) section.get("char");
|
||||
if (PreConditions.isNull(character,
|
||||
() -> this.plugin.logger().warn(path, "`char` option is not set in image " + id))) return;
|
||||
if (character.length() == 1) {
|
||||
chars = List.of(character.toCharArray());
|
||||
} else {
|
||||
chars = List.of(CharacterUtils.decodeUnicodeToChars(character));
|
||||
}
|
||||
}
|
||||
|
||||
int size = -1;
|
||||
int[][] codepointGrid = new int[chars.size()][];
|
||||
for (int i = 0; i < chars.size(); ++i) {
|
||||
int[] codepoints = CharacterUtils.charsToCodePoints(chars.get(i));
|
||||
for (int codepoint : codepoints) {
|
||||
if (PreConditions.runIfTrue(font.isCodepointInUse(codepoint),
|
||||
() -> this.plugin.logger().warn(path, String.format("Codepoint [%s (%s)] is already used in font [%s]", CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), new String(Character.toChars(codepoint)), font.key().toString())))) return;
|
||||
}
|
||||
codepointGrid[i] = codepoints;
|
||||
if (size == -1) size = codepoints.length;
|
||||
if (PreConditions.runIfTrue(size != codepoints.length,
|
||||
() -> this.plugin.logger().warn(path, "Illegal chars format found at " + id))) return;
|
||||
}
|
||||
|
||||
if (PreConditions.runIfTrue(size == -1,
|
||||
() -> this.plugin.logger().warn(path, "Illegal chars format found at " + id))) return;
|
||||
|
||||
if (!file.endsWith(".png")) file += ".png";
|
||||
file = file.replace("\\", "/");
|
||||
Key namespacedPath = Key.of(file);
|
||||
Path targetImageFile = pack.resourcePackFolder()
|
||||
.resolve("assets")
|
||||
.resolve(namespacedPath.namespace())
|
||||
.resolve("textures")
|
||||
.resolve(namespacedPath.value());
|
||||
|
||||
if (PreConditions.runIfTrue(!Files.exists(targetImageFile),
|
||||
() -> this.plugin.logger().warn(targetImageFile, "PNG file not found for image " + id))) return;
|
||||
|
||||
BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, file, codepointGrid);
|
||||
for (int[] y : codepointGrid) {
|
||||
for (int x : y) {
|
||||
font.registerCodepoint(x, bitmapImage);
|
||||
}
|
||||
}
|
||||
|
||||
this.images.put(id, bitmapImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Font> fontsInUse() {
|
||||
return new ArrayList<>(this.fonts.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BitmapImage> bitmapImageByCodepoint(Key font, int codepoint) {
|
||||
return getFontInUse(font).map(f -> f.getImageByCodepoint(codepoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BitmapImage> bitmapImageByImageId(Key id) {
|
||||
return Optional.ofNullable(this.images.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int codepointByImageId(Key key, int x, int y) {
|
||||
BitmapImage image = this.images.get(key);
|
||||
if (image == null) return -1;
|
||||
return image.codepointAt(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createOffsets(int offset, BiFunction<String, String, String> tagFormatter) {
|
||||
return Optional.ofNullable(this.offsetFont).map(it -> it.createOffset(offset, tagFormatter)).orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Font> getFontInUse(Key key) {
|
||||
return Optional.ofNullable(fonts.get(key));
|
||||
}
|
||||
|
||||
private Font getOrCreateFont(Key key) {
|
||||
return this.fonts.computeIfAbsent(key, Font::new);
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,15 @@ import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class BitmapImage implements FontProvider {
|
||||
private final Key imageId;
|
||||
private final Key id;
|
||||
private final Key font;
|
||||
private final int height;
|
||||
private final int ascent;
|
||||
private final String file;
|
||||
private final int[][] codepointGrid;
|
||||
|
||||
public BitmapImage(Key imageId, Key font, int height, int ascent, String file, int[][] codepointGrid) {
|
||||
this.imageId = imageId;
|
||||
public BitmapImage(Key id, Key font, int height, int ascent, String file, int[][] codepointGrid) {
|
||||
this.id = id;
|
||||
this.font = font;
|
||||
this.height = height;
|
||||
this.ascent = ascent;
|
||||
@@ -39,16 +39,16 @@ public class BitmapImage implements FontProvider {
|
||||
return font;
|
||||
}
|
||||
|
||||
public Key imageId() {
|
||||
return imageId;
|
||||
public Key id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int[][] codepointGrid() {
|
||||
return codepointGrid;
|
||||
return codepointGrid.clone();
|
||||
}
|
||||
|
||||
public int codepointAt(int row, int column) {
|
||||
if (row < 0 || row >= codepointGrid.length || column < 0 || column >= codepointGrid[row].length) {
|
||||
if (!isValidCoordinate(row, column)) {
|
||||
throw new IndexOutOfBoundsException("Invalid index: (" + row + ", " + column + ")");
|
||||
}
|
||||
return codepointGrid[row][column];
|
||||
@@ -69,16 +69,16 @@ public class BitmapImage implements FontProvider {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
BitmapImage image = (BitmapImage) object;
|
||||
return imageId.equals(image.imageId);
|
||||
return id.equals(image.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return imageId.hashCode();
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getJson() {
|
||||
public JsonObject get() {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("type", "bitmap");
|
||||
jsonObject.addProperty("height", height);
|
||||
|
||||
@@ -4,12 +4,12 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Font {
|
||||
private final Key key;
|
||||
private final HashMap<Integer, BitmapImage> idToCodepoint = new LinkedHashMap<>();
|
||||
private final Map<Integer, BitmapImage> idToCodepoint = new LinkedHashMap<>();
|
||||
|
||||
public Font(Key key) {
|
||||
this.key = key;
|
||||
@@ -24,11 +24,11 @@ public class Font {
|
||||
return Collections.unmodifiableCollection(this.idToCodepoint.keySet());
|
||||
}
|
||||
|
||||
public BitmapImage getImageByCodepoint(int codepoint) {
|
||||
public BitmapImage bitmapImageByCodepoint(int codepoint) {
|
||||
return this.idToCodepoint.get(codepoint);
|
||||
}
|
||||
|
||||
public void registerCodepoint(int codepoint, BitmapImage image) {
|
||||
public void addBitMapImage(int codepoint, BitmapImage image) {
|
||||
this.idToCodepoint.put(codepoint, image);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class Font {
|
||||
}
|
||||
|
||||
public Collection<BitmapImage> bitmapImages() {
|
||||
return idToCodepoint.values().stream().distinct().toList();
|
||||
return this.idToCodepoint.values().stream().distinct().toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.momirealms.craftengine.core.font;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import net.momirealms.craftengine.core.util.FormatUtils;
|
||||
@@ -9,10 +8,8 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public interface ImageManager extends Reloadable, ConfigSectionParser {
|
||||
String CONFIG_SECTION_NAME = "images";
|
||||
public interface FontManager extends Manageable {
|
||||
Key DEFAULT_FONT = Key.of("minecraft:default");
|
||||
String BYPASS_BOOK = "craftengine.filter.bypass.book";
|
||||
String BYPASS_SIGN = "craftengine.filter.bypass.sign";
|
||||
@@ -20,27 +17,23 @@ public interface ImageManager extends Reloadable, ConfigSectionParser {
|
||||
String BYPASS_COMMAND = "craftengine.filter.bypass.command";
|
||||
String BYPASS_ANVIL = "craftengine.filter.bypass.anvil";
|
||||
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
void delayedLoad();
|
||||
ConfigSectionParser[] parsers();
|
||||
|
||||
boolean isDefaultFontInUse();
|
||||
|
||||
boolean isIllegalCharacter(int codepoint);
|
||||
boolean isIllegalCodepoint(int codepoint);
|
||||
|
||||
Collection<Font> fontsInUse();
|
||||
Collection<Font> fonts();
|
||||
|
||||
Optional<BitmapImage> bitmapImageByCodepoint(Key font, int codepoint);
|
||||
|
||||
default Optional<BitmapImage> getBitmapImageByChars(Key font, char[] chars) {
|
||||
default Optional<BitmapImage> bitmapImageByChars(Key font, char[] chars) {
|
||||
return bitmapImageByCodepoint(font, CharacterUtils.charsToCodePoint(chars));
|
||||
}
|
||||
|
||||
Optional<BitmapImage> bitmapImageByImageId(Key imageId);
|
||||
|
||||
Optional<Font> getFontInUse(Key font);
|
||||
Optional<Font> fontById(Key font);
|
||||
|
||||
int codepointByImageId(Key imageId, int x, int y);
|
||||
|
||||
@@ -48,15 +41,15 @@ public interface ImageManager extends Reloadable, ConfigSectionParser {
|
||||
return this.codepointByImageId(imageId, 0, 0);
|
||||
}
|
||||
|
||||
default char[] getCharsByImageId(Key imageId) {
|
||||
return getCharsByImageId(imageId, 0, 0);
|
||||
default char[] charsByImageId(Key imageId) {
|
||||
return charsByImageId(imageId, 0, 0);
|
||||
}
|
||||
|
||||
default char[] getCharsByImageId(Key imageId, int x, int y) {
|
||||
default char[] charsByImageId(Key imageId, int x, int y) {
|
||||
return Character.toChars(this.codepointByImageId(imageId, x, y));
|
||||
}
|
||||
|
||||
String createOffsets(int offset, BiFunction<String, String, String> tagFormatter);
|
||||
String createOffsets(int offset, FontTagFormatter tagFormatter);
|
||||
|
||||
default String createMiniMessageOffsets(int offset) {
|
||||
return createOffsets(offset, FormatUtils::miniMessageFont);
|
||||
@@ -69,10 +62,4 @@ public interface ImageManager extends Reloadable, ConfigSectionParser {
|
||||
default String createRawOffsets(int offset) {
|
||||
return createOffsets(offset, (raw, font) -> raw);
|
||||
}
|
||||
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.FONT;
|
||||
}
|
||||
|
||||
void delayedInit();
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.font;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface FontProvider {
|
||||
import java.util.function.Supplier;
|
||||
|
||||
JsonObject getJson();
|
||||
public interface FontProvider extends Supplier<JsonObject> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.momirealms.craftengine.core.font;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public interface FontTagFormatter extends BiFunction<String, String, String> {
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
protected final Set<EquipmentGeneration> equipmentsToGenerate;
|
||||
// Cached command suggestions
|
||||
protected final List<Suggestion> cachedSuggestions = new ArrayList<>();
|
||||
protected final List<Suggestion> cachedTotemSuggestions = new ArrayList<>();
|
||||
|
||||
protected void registerDataFunction(Function<Object, ItemModifier<I>> function, String... alias) {
|
||||
for (String a : alias) {
|
||||
@@ -65,6 +66,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
super.clearModelsToGenerate();
|
||||
this.customItems.clear();
|
||||
this.cachedSuggestions.clear();
|
||||
this.cachedTotemSuggestions.clear();
|
||||
this.legacyOverrides.clear();
|
||||
this.modernOverrides.clear();
|
||||
this.customItemTags.clear();
|
||||
@@ -120,6 +122,11 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
return Collections.unmodifiableCollection(this.cachedSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Suggestion> cachedTotemSuggestions() {
|
||||
return Collections.unmodifiableCollection(this.cachedTotemSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<ItemBehavior>> getItemBehavior(Key key) {
|
||||
Optional<CustomItem<I>> customItemOptional = getCustomItem(key);
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.plugin.minimessage.*;
|
||||
import net.momirealms.craftengine.core.plugin.text.minimessage.*;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -3,11 +3,10 @@ package net.momirealms.craftengine.core.item;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModel;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -16,12 +15,9 @@ import org.incendo.cloud.suggestion.Suggestion;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
public interface ItemManager<T> extends Reloadable, ModelGenerator, ConfigSectionParser {
|
||||
String CONFIG_SECTION_NAME = "items";
|
||||
public interface ItemManager<T> extends Manageable, ModelGenerator {
|
||||
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
ConfigSectionParser parser();
|
||||
|
||||
Map<Key, TreeSet<LegacyOverridesModel>> legacyItemOverrides();
|
||||
|
||||
@@ -79,13 +75,9 @@ public interface ItemManager<T> extends Reloadable, ModelGenerator, ConfigSectio
|
||||
|
||||
int fuelTime(Key id);
|
||||
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.ITEM;
|
||||
}
|
||||
|
||||
Collection<Suggestion> cachedSuggestions();
|
||||
|
||||
void delayedInit();
|
||||
Collection<Suggestion> cachedTotemSuggestions();
|
||||
|
||||
Object encodeJava(Key componentType, @Nullable Object component);
|
||||
}
|
||||
@@ -2,14 +2,14 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
public class DisplayNameModifier<I> implements ItemModifier<I> {
|
||||
private final String argument;
|
||||
|
||||
public DisplayNameModifier(String argument) {
|
||||
this.argument = ConfigManager.nonItalic() ? "<!i>" + argument : argument;
|
||||
this.argument = Config.nonItalic() ? "<!i>" + argument : argument;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,14 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
public class ItemNameModifier<I> implements ItemModifier<I> {
|
||||
private final String argument;
|
||||
|
||||
public ItemNameModifier(String argument) {
|
||||
this.argument = ConfigManager.nonItalic() ? "<!i>" + argument : argument;
|
||||
this.argument = Config.nonItalic() ? "<!i>" + argument : argument;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
import java.util.List;
|
||||
@@ -11,7 +11,7 @@ public class LoreModifier<I> implements ItemModifier<I> {
|
||||
private final List<String> argument;
|
||||
|
||||
public LoreModifier(List<String> argument) {
|
||||
this.argument = ConfigManager.nonItalic() ? argument.stream().map(it -> "<!i>" + it).toList() : argument;
|
||||
this.argument = Config.nonItalic() ? argument.stream().map(it -> "<!i>" + it).toList() : argument;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package net.momirealms.craftengine.core.item.recipe;
|
||||
|
||||
import net.momirealms.craftengine.core.item.recipe.input.RecipeInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20_5;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_21_2;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
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.Holder;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractRecipeManager<T> implements RecipeManager<T> {
|
||||
protected final VanillaRecipeReader recipeReader;
|
||||
protected final Map<Key, List<Recipe<T>>> byType = new HashMap<>();
|
||||
protected final Map<Key, Recipe<T>> byId = new HashMap<>();
|
||||
protected final Map<Key, List<Recipe<T>>> byResult = new HashMap<>();
|
||||
protected final Map<Key, List<Recipe<T>>> byIngredient = new HashMap<>();
|
||||
protected final Set<Key> dataPackRecipes = new HashSet<>();
|
||||
protected final Set<Key> customRecipes = new HashSet<>();
|
||||
private final RecipeParser recipeParser;
|
||||
|
||||
public AbstractRecipeManager() {
|
||||
this.recipeReader = initVanillaRecipeReader();
|
||||
this.recipeParser = new RecipeParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSectionParser parser() {
|
||||
return this.recipeParser;
|
||||
}
|
||||
|
||||
private VanillaRecipeReader initVanillaRecipeReader() {
|
||||
if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
return new VanillaRecipeReader1_21_2();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
return new VanillaRecipeReader1_20_5();
|
||||
} else {
|
||||
return new VanillaRecipeReader1_20();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.dataPackRecipes.clear();
|
||||
this.byType.clear();
|
||||
this.byId.clear();
|
||||
this.byResult.clear();
|
||||
this.byIngredient.clear();
|
||||
for (Key key : this.customRecipes) {
|
||||
unregisterPlatformRecipe(key);
|
||||
}
|
||||
this.customRecipes.clear();
|
||||
}
|
||||
|
||||
protected void markAsDataPackRecipe(Key key) {
|
||||
this.dataPackRecipes.add(key);
|
||||
}
|
||||
|
||||
protected void markAsCustomRecipe(Key key) {
|
||||
this.customRecipes.add(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataPackRecipe(Key key) {
|
||||
return this.dataPackRecipes.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRecipe(Key key) {
|
||||
return this.byId.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Recipe<T>> recipeById(Key key) {
|
||||
return Optional.ofNullable(this.byId.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe<T>> recipesByType(Key type) {
|
||||
return this.byType.getOrDefault(type, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe<T>> recipeByResult(Key result) {
|
||||
return this.byResult.getOrDefault(result, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe<T>> recipeByIngredient(Key ingredient) {
|
||||
return this.byIngredient.getOrDefault(ingredient, List.of());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Recipe<T> recipeByInput(Key type, RecipeInput input) {
|
||||
List<Recipe<T>> recipes = this.byType.get(type);
|
||||
if (recipes == null) return null;
|
||||
for (Recipe<T> recipe : recipes) {
|
||||
if (recipe.matches(input)) {
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Recipe<T> recipeByInput(Key type, RecipeInput input, Key lastRecipe) {
|
||||
if (lastRecipe != null) {
|
||||
Recipe<T> last = byId.get(lastRecipe);
|
||||
if (last != null && last.matches(input)) {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
return recipeByInput(type, input);
|
||||
}
|
||||
|
||||
protected void registerInternalRecipe(Key id, Recipe<T> recipe) {
|
||||
this.byType.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe);
|
||||
this.byId.put(id, recipe);
|
||||
this.byResult.computeIfAbsent(recipe.result().item().id(), k -> new ArrayList<>()).add(recipe);
|
||||
HashSet<Key> usedKeys = new HashSet<>();
|
||||
for (Ingredient<T> ingredient : recipe.ingredientsInUse()) {
|
||||
for (Holder<Key> holder : ingredient.items()) {
|
||||
Key key = holder.value();
|
||||
if (usedKeys.add(key)) {
|
||||
this.byIngredient.computeIfAbsent(key, k -> new ArrayList<>()).add(recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RecipeParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"recipes", "recipe"};
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.RECIPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (AbstractRecipeManager.this.byId.containsKey(id)) {
|
||||
TranslationManager.instance().log("warning.config.recipe.duplicated", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
Recipe<T> recipe;
|
||||
try {
|
||||
recipe = RecipeTypes.fromMap(id, section);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn(path, "Failed to create recipe: " + id, e);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
markAsCustomRecipe(id);
|
||||
registerInternalRecipe(id, recipe);
|
||||
registerPlatformRecipe(id, recipe);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to register custom recipe " + id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void unregisterPlatformRecipe(Key key);
|
||||
|
||||
protected abstract void registerPlatformRecipe(Key key, Recipe<T> recipe);
|
||||
}
|
||||
@@ -1,45 +1,33 @@
|
||||
package net.momirealms.craftengine.core.item.recipe;
|
||||
|
||||
import net.momirealms.craftengine.core.item.recipe.input.RecipeInput;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface RecipeManager<T> extends Reloadable, ConfigSectionParser {
|
||||
String CONFIG_SECTION_NAME = "recipes";
|
||||
public interface RecipeManager<T> extends Manageable {
|
||||
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
ConfigSectionParser parser();
|
||||
|
||||
boolean isDataPackRecipe(Key key);
|
||||
|
||||
boolean isCustomRecipe(Key key);
|
||||
|
||||
Optional<Recipe<T>> getRecipeById(Key id);
|
||||
Optional<Recipe<T>> recipeById(Key id);
|
||||
|
||||
List<Recipe<T>> getRecipes(Key type);
|
||||
List<Recipe<T>> recipesByType(Key type);
|
||||
|
||||
List<Recipe<T>> getRecipeByResult(Key result);
|
||||
List<Recipe<T>> recipeByResult(Key result);
|
||||
|
||||
List<Recipe<T>> getRecipeByIngredient(Key ingredient);
|
||||
List<Recipe<T>> recipeByIngredient(Key ingredient);
|
||||
|
||||
@Nullable
|
||||
Recipe<T> getRecipe(Key type, RecipeInput input);
|
||||
Recipe<T> recipeByInput(Key type, RecipeInput input);
|
||||
|
||||
@Nullable Recipe<T> getRecipe(Key type, RecipeInput input, @Nullable Key lastRecipe);
|
||||
|
||||
CompletableFuture<Void> delayedLoad();
|
||||
|
||||
void delayedInit();
|
||||
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.RECIPE;
|
||||
}
|
||||
@Nullable
|
||||
Recipe<T> recipeByInput(Key type, RecipeInput input, @Nullable Key lastRecipe);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.momirealms.craftengine.core.loot;
|
||||
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class AbstractVanillaLootManager implements VanillaLootManager {
|
||||
protected final Map<Integer, VanillaLoot> blockLoots = new HashMap<>();
|
||||
// TODO More entity NBT
|
||||
protected final Map<Key, VanillaLoot> entityLoots = new HashMap<>();
|
||||
|
||||
public AbstractVanillaLootManager() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.blockLoots.clear();
|
||||
this.entityLoots.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VanillaLoot> getBlockLoot(int vanillaBlockState) {
|
||||
return Optional.ofNullable(this.blockLoots.get(vanillaBlockState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VanillaLoot> getEntityLoot(Key entity) {
|
||||
return Optional.ofNullable(this.entityLoots.get(entity));
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
package net.momirealms.craftengine.core.loot;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface VanillaLootManager extends ConfigSectionParser, Reloadable {
|
||||
String CONFIG_SECTION_NAME = "vanilla-loots";
|
||||
public interface VanillaLootManager extends Manageable {
|
||||
|
||||
@Override
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.VANILLA_LOOTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
default String sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
void delayedInit();
|
||||
ConfigSectionParser parser();
|
||||
|
||||
Optional<VanillaLoot> getBlockLoot(int blockState);
|
||||
|
||||
Optional<VanillaLoot> getEntityLoot(Key entity);
|
||||
}
|
||||
|
||||
@@ -15,10 +15,9 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
|
||||
import net.momirealms.craftengine.core.pack.obfuscation.ObfA;
|
||||
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.config.StringKeyConstructor;
|
||||
import net.momirealms.craftengine.core.plugin.config.template.TemplateManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.I18NData;
|
||||
import net.momirealms.craftengine.core.sound.AbstractSoundManager;
|
||||
import net.momirealms.craftengine.core.sound.SoundEvent;
|
||||
@@ -39,6 +38,7 @@ import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static net.momirealms.craftengine.core.util.MiscUtils.castToMap;
|
||||
|
||||
@@ -50,7 +50,6 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
public static final Set<Key> VANILLA_ITEM_TEXTURES = new HashSet<>();
|
||||
public static final Set<Key> VANILLA_BLOCK_TEXTURES = new HashSet<>();
|
||||
public static final Set<Key> VANILLA_FONT_TEXTURES = new HashSet<>();
|
||||
public static final List<byte[]> SHULKER_PNG = new ArrayList<>(1);
|
||||
|
||||
private final CraftEngine plugin;
|
||||
private final BiConsumer<Path, Path> eventDispatcher;
|
||||
@@ -86,8 +85,6 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
loadInternalList("internal/textures/block/_list.json", VANILLA_BLOCK_TEXTURES::add);
|
||||
loadInternalList("internal/textures/item/_list.json", VANILLA_ITEM_TEXTURES::add);
|
||||
loadInternalList("internal/textures/font/_list.json", VANILLA_FONT_TEXTURES::add);
|
||||
|
||||
loadInternalPng("internal/textures/entity/shulker/shulker.png", SHULKER_PNG::add);
|
||||
}
|
||||
|
||||
private void loadInternalData(String path, BiConsumer<Key, JsonObject> callback) {
|
||||
@@ -140,18 +137,22 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.loadPacks();
|
||||
this.loadConfigs();
|
||||
this.calculateHash();
|
||||
if (ConfigManager.hostMode() == HostMode.SELF_HOST) {
|
||||
Path path = ConfigManager.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(ConfigManager.hostResourcePackPath()) : Path.of(ConfigManager.hostResourcePackPath());
|
||||
ResourcePackHost.instance().enable(ConfigManager.hostIP(), ConfigManager.hostPort(), path);
|
||||
ResourcePackHost.instance().setRateLimit(ConfigManager.requestRate(), ConfigManager.requestInterval(), TimeUnit.SECONDS);
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
Path path = Config.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(Config.hostResourcePackPath()) : Path.of(Config.hostResourcePackPath());
|
||||
ResourcePackHost.instance().enable(Config.hostIP(), Config.hostPort(), path);
|
||||
ResourcePackHost.instance().setRateLimit(Config.requestRate(), Config.requestInterval(), TimeUnit.SECONDS);
|
||||
} else {
|
||||
ResourcePackHost.instance().disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadResources(boolean recipe) {
|
||||
this.loadPacks();
|
||||
this.loadResourceConfigs(recipe ? (p) -> true : (p) -> p.loadingSequence() != LoadingSequence.RECIPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.loadedPacks.clear();
|
||||
@@ -192,8 +193,12 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
|
||||
@Override
|
||||
public boolean registerConfigSectionParser(ConfigSectionParser parser) {
|
||||
if (this.sectionParsers.containsKey(parser.sectionId())) return false;
|
||||
this.sectionParsers.put(parser.sectionId(), parser);
|
||||
for (String id : parser.sectionId()) {
|
||||
if (this.sectionParsers.containsKey(id)) return false;
|
||||
}
|
||||
for (String id : parser.sectionId()) {
|
||||
this.sectionParsers.put(id, parser);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -205,7 +210,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
public Path selfHostPackPath() {
|
||||
return ConfigManager.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(ConfigManager.hostResourcePackPath()) : Path.of(ConfigManager.hostResourcePackPath());
|
||||
return Config.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(Config.hostResourcePackPath()) : Path.of(Config.hostResourcePackPath());
|
||||
}
|
||||
|
||||
private void loadPacks() {
|
||||
@@ -228,7 +233,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
String version = null;
|
||||
String author = null;
|
||||
if (Files.exists(metaFile) && Files.isRegularFile(metaFile)) {
|
||||
YamlDocument metaYML = ConfigManager.instance().loadYamlData(metaFile.toFile());
|
||||
YamlDocument metaYML = Config.instance().loadYamlData(metaFile.toFile());
|
||||
namespace = metaYML.getString("namespace", namespace);
|
||||
description = metaYML.getString("description");
|
||||
version = metaYML.getString("version");
|
||||
@@ -372,7 +377,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_frame.png.mcmeta");
|
||||
}
|
||||
|
||||
private void loadConfigs() {
|
||||
private void loadResourceConfigs(Predicate<ConfigSectionParser> predicate) {
|
||||
long o1 = System.nanoTime();
|
||||
for (Pack pack : loadedPacks()) {
|
||||
Pair<List<Path>, List<Path>> files = FileUtils.getConfigsDeeply(pack.configurationFolder());
|
||||
@@ -404,32 +409,31 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
this.plugin.logger().info("Loaded packs. Took " + String.format("%.2f", ((o2 - o1) / 1_000_000.0)) + " ms");
|
||||
for (Map.Entry<ConfigSectionParser, List<CachedConfig>> entry : this.cachedConfigs.entrySet()) {
|
||||
ConfigSectionParser parser = entry.getKey();
|
||||
boolean isTemplate = parser.sectionId().equals(TemplateManager.CONFIG_SECTION_NAME);
|
||||
long t1 = System.nanoTime();
|
||||
for (CachedConfig cached : entry.getValue()) {
|
||||
for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) {
|
||||
String key = configEntry.getKey();
|
||||
try {
|
||||
Key id = Key.withDefaultNamespace(key, cached.pack().namespace());
|
||||
if (isTemplate) {
|
||||
((TemplateManager) parser).addTemplate(cached.pack(), cached.filePath(), id, configEntry.getValue());
|
||||
} else {
|
||||
if (parser.isTemplate()) {
|
||||
this.plugin.templateManager().addTemplate(cached.pack(), cached.filePath(), id, configEntry.getValue());
|
||||
} else if (predicate.test(parser)) {
|
||||
if (configEntry.getValue() instanceof Map<?, ?> configSection0) {
|
||||
Map<String, Object> configSection1 = castToMap(configSection0, false);
|
||||
if ((boolean) configSection1.getOrDefault("enable", true)) {
|
||||
parser.parseSection(cached.pack(), cached.filePath(), id, plugin.templateManager().applyTemplates(configSection1));
|
||||
}
|
||||
} else {
|
||||
this.plugin.logger().warn(cached.filePath(), "Configuration section is required for " + parser.sectionId() + "." + configEntry.getKey() + " - ");
|
||||
this.plugin.logger().warn(cached.filePath(), "Configuration section is required for " + parser.sectionId()[0] + "." + configEntry.getKey() + " - ");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn(cached.filePath(), "Error loading " + parser.sectionId() + "." + key, e);
|
||||
this.plugin.logger().warn(cached.filePath(), "Error loading " + parser.sectionId()[0] + "." + key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
long t2 = System.nanoTime();
|
||||
this.plugin.logger().info("Loaded " + parser.sectionId() + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms");
|
||||
this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms");
|
||||
}
|
||||
this.cachedConfigs.clear();
|
||||
}
|
||||
@@ -440,10 +444,8 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
int hashIndex = key.indexOf('#');
|
||||
String configType = hashIndex != -1 ? key.substring(0, hashIndex) : key;
|
||||
Optional.ofNullable(this.sectionParsers.get(configType))
|
||||
.ifPresent(parser -> {
|
||||
this.cachedConfigs.computeIfAbsent(parser, k -> new ArrayList<>())
|
||||
.add(new CachedConfig(castToMap(typeSections0, false), path, pack));
|
||||
});
|
||||
.ifPresent(parser -> this.cachedConfigs.computeIfAbsent(parser, k -> new ArrayList<>())
|
||||
.add(new CachedConfig(castToMap(typeSections0, false), path, pack)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +468,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
try {
|
||||
List<Path> folders = new ArrayList<>();
|
||||
folders.addAll(loadedPacks().stream().map(Pack::resourcePackFolder).toList());
|
||||
folders.addAll(ConfigManager.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList());
|
||||
folders.addAll(Config.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList());
|
||||
|
||||
List<Pair<Path, List<Path>>> duplicated = mergeFolder(folders, generatedPackPath);
|
||||
if (!duplicated.isEmpty()) {
|
||||
@@ -528,7 +530,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
private void generateEquipments(Path generatedPackPath) {
|
||||
for (EquipmentGeneration generator : this.plugin.itemManager().equipmentsToGenerate()) {
|
||||
EquipmentData equipmentData = generator.modernData();
|
||||
if (equipmentData != null && ConfigManager.packMaxVersion() >= 21.4f) {
|
||||
if (equipmentData != null && Config.packMaxVersion() >= 21.4f) {
|
||||
Path equipmentPath = generatedPackPath
|
||||
.resolve("assets")
|
||||
.resolve(equipmentData.assetId().namespace())
|
||||
@@ -561,7 +563,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
this.plugin.logger().severe("Error writing equipment file", e);
|
||||
}
|
||||
}
|
||||
if (equipmentData != null && ConfigManager.packMaxVersion() >= 21.2f && ConfigManager.packMinVersion() < 21.4f) {
|
||||
if (equipmentData != null && Config.packMaxVersion() >= 21.2f && Config.packMinVersion() < 21.4f) {
|
||||
Path equipmentPath = generatedPackPath
|
||||
.resolve("assets")
|
||||
.resolve(equipmentData.assetId().namespace())
|
||||
@@ -599,7 +601,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void generateClientLang(Path generatedPackPath) {
|
||||
for (Map.Entry<String, I18NData> entry : this.plugin.translationManager().clientLangManager().langData().entrySet()) {
|
||||
for (Map.Entry<String, I18NData> entry : this.plugin.translationManager().clientLangData().entrySet()) {
|
||||
JsonObject json = new JsonObject();
|
||||
for (Map.Entry<String, String> pair : entry.getValue().translations.entrySet()) {
|
||||
json.addProperty(pair.getKey(), pair.getValue());
|
||||
@@ -663,7 +665,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void generateOverrideSounds(Path generatedPackPath) {
|
||||
if (!ConfigManager.enableSoundSystem()) return;
|
||||
if (!Config.enableSoundSystem()) return;
|
||||
|
||||
Path soundPath = generatedPackPath
|
||||
.resolve("assets")
|
||||
@@ -753,7 +755,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
private void generateBlockOverrides(Path generatedPackPath) {
|
||||
File blockStatesFile = new File(plugin.dataFolderFile(), "blockstates.yml");
|
||||
if (!blockStatesFile.exists()) plugin.saveResource("blockstates.yml");
|
||||
YamlDocument preset = ConfigManager.instance().loadYamlData(blockStatesFile);
|
||||
YamlDocument preset = Config.instance().loadYamlData(blockStatesFile);
|
||||
for (Map.Entry<Key, Map<String, JsonElement>> entry : plugin.blockManager().blockOverrides().entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
Path overridedBlockPath = generatedPackPath
|
||||
@@ -788,7 +790,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (!ConfigManager.generateModAssets()) return;
|
||||
if (!Config.generateModAssets()) return;
|
||||
for (Map.Entry<Key, JsonElement> entry : plugin.blockManager().modBlockStates().entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
Path overridedBlockPath = generatedPackPath
|
||||
@@ -815,8 +817,8 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void generateModernItemModels1_21_2(Path generatedPackPath) {
|
||||
if (ConfigManager.packMaxVersion() < 21.19f) return;
|
||||
if (ConfigManager.packMinVersion() > 21.39f) return;
|
||||
if (Config.packMaxVersion() < 21.19f) return;
|
||||
if (Config.packMinVersion() > 21.39f) return;
|
||||
|
||||
boolean has = false;
|
||||
for (Map.Entry<Key, List<LegacyOverridesModel>> entry : plugin.itemManager().modernItemModels1_21_2().entrySet()) {
|
||||
@@ -868,13 +870,13 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigManager.packMinVersion() < 21.19f && has) {
|
||||
plugin.logger().warn("You are using item-model component for models which requires 1.21.2+. But the min supported version is " + "1." + ConfigManager.packMinVersion());
|
||||
if (Config.packMinVersion() < 21.19f && has) {
|
||||
plugin.logger().warn("You are using item-model component for models which requires 1.21.2+. But the min supported version is " + "1." + Config.packMinVersion());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateModernItemModels1_21_4(Path generatedPackPath) {
|
||||
if (ConfigManager.packMaxVersion() < 21.39f) return;
|
||||
if (Config.packMaxVersion() < 21.39f) return;
|
||||
for (Map.Entry<Key, ItemModel> entry : plugin.itemManager().modernItemModels1_21_4().entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
Path itemPath = generatedPackPath
|
||||
@@ -907,7 +909,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void generateModernItemOverrides(Path generatedPackPath) {
|
||||
if (ConfigManager.packMaxVersion() < 21.39f) return;
|
||||
if (Config.packMaxVersion() < 21.39f) return;
|
||||
for (Map.Entry<Key, TreeMap<Integer, ItemModel>> entry : plugin.itemManager().modernItemOverrides().entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
Path overridedItemPath = generatedPackPath
|
||||
@@ -967,7 +969,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void generateLegacyItemOverrides(Path generatedPackPath) {
|
||||
if (ConfigManager.packMinVersion() > 21.39f) return;
|
||||
if (Config.packMinVersion() > 21.39f) return;
|
||||
for (Map.Entry<Key, TreeSet<LegacyOverridesModel>> entry : plugin.itemManager().legacyItemOverrides().entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
Path overridedItemPath = generatedPackPath
|
||||
@@ -1020,7 +1022,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
|
||||
private void generateFonts(Path generatedPackPath) {
|
||||
// generate image font json
|
||||
for (Font font : plugin.imageManager().fontsInUse()) {
|
||||
for (Font font : plugin.imageManager().fonts()) {
|
||||
Key namespacedKey = font.key();
|
||||
Path fontPath = generatedPackPath.resolve("assets")
|
||||
.resolve(namespacedKey.namespace())
|
||||
@@ -1054,7 +1056,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
for (BitmapImage image : font.bitmapImages()) {
|
||||
providers.add(image.getJson());
|
||||
providers.add(image.get());
|
||||
}
|
||||
|
||||
try (FileWriter fileWriter = new FileWriter(fontPath.toFile())) {
|
||||
@@ -1064,7 +1066,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigManager.resourcePack$overrideUniform()) {
|
||||
if (Config.resourcePack$overrideUniform()) {
|
||||
Path fontPath = generatedPackPath.resolve("assets")
|
||||
.resolve("minecraft")
|
||||
.resolve("font")
|
||||
@@ -1115,7 +1117,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
conflicts.add(file);
|
||||
} else {
|
||||
for (ConditionalResolution resolution : ConfigManager.resolutions()) {
|
||||
for (ConditionalResolution resolution : Config.resolutions()) {
|
||||
if (resolution.matcher().test(relative)) {
|
||||
resolution.resolution().run(targetPath, file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
|
||||
@@ -7,10 +7,11 @@ public class LoadingSequence {
|
||||
public static final int BLOCK = 30;
|
||||
public static final int ITEM = 40;
|
||||
public static final int FURNITURE = 50;
|
||||
public static final int FONT = 60;
|
||||
public static final int IMAGE = 60;
|
||||
public static final int RECIPE = 70;
|
||||
public static final int CATEGORY = 80;
|
||||
public static final int SOUND = 90;
|
||||
public static final int JUKEBOX_SONG = 100;
|
||||
public static final int VANILLA_LOOTS = 110;
|
||||
public static final int EMOJI = 120;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
package net.momirealms.craftengine.core.pack;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface PackManager extends Reloadable {
|
||||
public interface PackManager extends Manageable {
|
||||
|
||||
void loadResources(boolean recipe);
|
||||
|
||||
@NotNull
|
||||
Collection<Pack> loadedPacks();
|
||||
|
||||
boolean registerConfigSectionParser(ConfigSectionParser parser);
|
||||
|
||||
boolean unregisterConfigSectionParser(String id);
|
||||
|
||||
default boolean unregisterConfigSectionParser(ConfigSectionParser parser) {
|
||||
return this.unregisterConfigSectionParser(parser.sectionId());
|
||||
default void registerConfigSectionParsers(ConfigSectionParser[] parsers) {
|
||||
for (ConfigSectionParser parser : parsers) {
|
||||
registerConfigSectionParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
void delayedInit();
|
||||
boolean unregisterConfigSectionParser(String id);
|
||||
|
||||
default void unregisterConfigSectionParser(ConfigSectionParser parser) {
|
||||
for (String id : parser.sectionId()) {
|
||||
unregisterConfigSectionParser(id);
|
||||
}
|
||||
}
|
||||
|
||||
void generateResourcePack();
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.momirealms.craftengine.core.pack;
|
||||
|
||||
public class ResourceLocation {
|
||||
|
||||
public static boolean isValid(final String resourceLocation) {
|
||||
int index = resourceLocation.indexOf(":");
|
||||
if (index == -1) {
|
||||
return isValidPath(resourceLocation);
|
||||
} else {
|
||||
return isValidNamespace(resourceLocation.substring(0, index)) && isValidPath(resourceLocation.substring(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean validPathChar(char character) {
|
||||
return character == '_' || character == '-' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '/' || character == '.';
|
||||
}
|
||||
|
||||
private static boolean validNamespaceChar(char character) {
|
||||
return character == '_' || character == '-' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '.';
|
||||
}
|
||||
|
||||
public static boolean isValidNamespace(String namespace) {
|
||||
for(int i = 0; i < namespace.length(); ++i) {
|
||||
if (!validNamespaceChar(namespace.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isValidPath(String path) {
|
||||
for(int i = 0; i < path.length(); ++i) {
|
||||
if (!validPathChar(path.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -26,7 +26,7 @@ public class ResourcePackHost {
|
||||
private long rateLimitInterval = 1000;
|
||||
|
||||
public String url() {
|
||||
return ConfigManager.hostProtocol() + "://" + ip + ":" + port + "/";
|
||||
return Config.hostProtocol() + "://" + ip + ":" + port + "/";
|
||||
}
|
||||
|
||||
public void enable(String ip, int port, Path resourcePackPath) {
|
||||
@@ -77,7 +77,7 @@ public class ResourcePackHost {
|
||||
private class ResourcePackHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
if (ConfigManager.denyNonMinecraftRequest()) {
|
||||
if (Config.denyNonMinecraftRequest()) {
|
||||
String userAgent = exchange.getRequestHeaders().getFirst("User-Agent");
|
||||
if (userAgent == null || !userAgent.startsWith("Minecraft Java/")) {
|
||||
CraftEngine.instance().debug(() -> "Blocked non-Minecraft Java client. User-Agent: " + userAgent);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package net.momirealms.craftengine.core.pack.model.generation;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.ResourceLocation;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -25,16 +28,23 @@ public abstract class AbstractModelGenerator implements ModelGenerator {
|
||||
this.modelsToGenerate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareModelGeneration(ModelGeneration model) {
|
||||
ModelGeneration generation = this.modelsToGenerate.get(model.path());
|
||||
if (generation != null) {
|
||||
if (generation.equals(model)) {
|
||||
public void prepareModelGeneration(Path path, Key id, ModelGeneration model) {
|
||||
ModelGeneration conflict = this.modelsToGenerate.get(model.path());
|
||||
if (conflict != null) {
|
||||
if (conflict.equals(model)) {
|
||||
return;
|
||||
}
|
||||
this.plugin.logger().severe("Two or more configurations attempt to generate different json models with the same path: " + model.path());
|
||||
TranslationManager.instance().log("warning.config.model.generation.conflict", path.toString(), id.toString(), model.path().toString());
|
||||
return;
|
||||
}
|
||||
if (!ResourceLocation.isValid(model.parentModelPath())) {
|
||||
TranslationManager.instance().log("warning.config.model.generation.parent.invalid_resource_location", path.toString(), id.toString(), model.parentModelPath());
|
||||
}
|
||||
for (Map.Entry<String, String> texture : model.texturesOverride().entrySet()) {
|
||||
if (!ResourceLocation.isValid(texture.getValue())) {
|
||||
TranslationManager.instance().log("warning.config.model.generation.texture.invalid_resource_location", path.toString(), id.toString(), texture.getKey(), texture.getValue());
|
||||
}
|
||||
}
|
||||
this.modelsToGenerate.put(model.path(), model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,4 @@ public interface ModelGenerator {
|
||||
Collection<ModelGeneration> modelsToGenerate();
|
||||
|
||||
void clearModelsToGenerate();
|
||||
|
||||
void prepareModelGeneration(ModelGeneration model);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ObfC {
|
||||
}
|
||||
|
||||
private static String normalizeCharset(String input) {
|
||||
return input.toLowerCase(Locale.ROOT);
|
||||
return input.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private static String generateDefaultCharset() {
|
||||
|
||||
@@ -101,7 +101,7 @@ public final class ObfF {
|
||||
}
|
||||
|
||||
private static Section 九转大肠() {
|
||||
return CraftEngine.instance().configManager().settings()
|
||||
return CraftEngine.instance().config().settings()
|
||||
.getSection("resource-pack.protection");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin;
|
||||
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
|
||||
import net.momirealms.craftengine.core.font.ImageManager;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeManager;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLootManager;
|
||||
@@ -11,7 +11,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.config.template.TemplateManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.template.TemplateManagerImpl;
|
||||
import net.momirealms.craftengine.core.plugin.dependency.Dependencies;
|
||||
@@ -29,11 +29,14 @@ import net.momirealms.craftengine.core.plugin.logger.filter.LogFilter;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter;
|
||||
import net.momirealms.craftengine.core.sound.SoundManager;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.WorldManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -41,13 +44,15 @@ public abstract class CraftEngine implements Plugin {
|
||||
public static final String MOD_CLASS = "net.momirealms.craftengine.mod.CraftEnginePlugin";
|
||||
public static final String NAMESPACE = "craftengine";
|
||||
private static CraftEngine instance;
|
||||
protected PluginLogger logger;
|
||||
protected Consumer<Supplier<String>> debugger = (s) -> {};
|
||||
protected Config config;
|
||||
protected ClassPathAppender classPathAppender;
|
||||
protected DependencyManager dependencyManager;
|
||||
protected SchedulerAdapter<?> scheduler;
|
||||
protected NetworkManager networkManager;
|
||||
protected ClassPathAppender classPathAppender;
|
||||
protected ImageManager imageManager;
|
||||
protected FontManager fontManager;
|
||||
protected PackManager packManager;
|
||||
protected ConfigManager configManager;
|
||||
protected ItemManager<?> itemManager;
|
||||
protected RecipeManager<?> recipeManager;
|
||||
protected BlockManager blockManager;
|
||||
@@ -62,8 +67,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
protected SoundManager soundManager;
|
||||
protected VanillaLootManager vanillaLootManager;
|
||||
|
||||
protected PluginLogger logger;
|
||||
protected Consumer<Supplier<String>> debugger = (s) -> {};
|
||||
private final Consumer<CraftEngine> reloadEventDispatcher;
|
||||
private boolean isReloading;
|
||||
|
||||
private String buildByBit = "%%__BUILTBYBIT__%%";
|
||||
@@ -72,8 +76,10 @@ public abstract class CraftEngine implements Plugin {
|
||||
private String user = "%%__USER__%%";
|
||||
private String username = "%%__USERNAME__%%";
|
||||
|
||||
protected CraftEngine() {
|
||||
protected CraftEngine(Consumer<CraftEngine> reloadEventDispatcher) {
|
||||
instance = this;
|
||||
this.reloadEventDispatcher = reloadEventDispatcher;
|
||||
VersionHelper.init(serverVersion());
|
||||
}
|
||||
|
||||
public static CraftEngine instance() {
|
||||
@@ -83,8 +89,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
public void onPluginLoad() {
|
||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new LogFilter());
|
||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter());
|
||||
this.dependencyManager = new DependencyManagerImpl(this);
|
||||
@@ -93,78 +98,124 @@ public abstract class CraftEngine implements Plugin {
|
||||
dependenciesToLoad.addAll(platformDependencies());
|
||||
this.dependencyManager.loadDependencies(dependenciesToLoad);
|
||||
this.translationManager = new TranslationManagerImpl(this);
|
||||
this.configManager = new ConfigManager(this);
|
||||
this.config = new Config(this);
|
||||
}
|
||||
|
||||
// TODO Make most things async
|
||||
@Override
|
||||
public void reload() {
|
||||
if (this.isReloading) return;
|
||||
this.isReloading = true;
|
||||
try {
|
||||
this.configManager.reload();
|
||||
public record ReloadResult(boolean success, long asyncTime, long syncTime) {
|
||||
|
||||
static ReloadResult failure() {
|
||||
return new ReloadResult(false, -1L, -1L);
|
||||
}
|
||||
|
||||
static ReloadResult success(long asyncTime, long syncTime) {
|
||||
return new ReloadResult(true, asyncTime, syncTime);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<ReloadResult> reloadPlugin(Executor asyncExecutor, Executor syncExecutor, boolean reloadRecipe) {
|
||||
CompletableFuture<ReloadResult> future = new CompletableFuture<>();
|
||||
asyncExecutor.execute(() -> {
|
||||
if (this.isReloading) {
|
||||
future.complete(ReloadResult.failure());
|
||||
return;
|
||||
}
|
||||
this.isReloading = true;
|
||||
long time1 = System.currentTimeMillis();
|
||||
// firstly reload main config
|
||||
this.config.load();
|
||||
// reset debugger
|
||||
this.debugger = Config.debug() ? (s) -> logger.info("[Debug] " + s.get()) : (s) -> {};
|
||||
// now we reload the translations
|
||||
this.translationManager.reload();
|
||||
// clear the outdated cache by reloading the managers
|
||||
this.templateManager.reload();
|
||||
this.furnitureManager.reload();
|
||||
this.imageManager.reload();
|
||||
this.fontManager.reload();
|
||||
this.itemManager.reload();
|
||||
this.soundManager.reload();
|
||||
this.recipeManager.reload();
|
||||
this.itemBrowserManager.reload();
|
||||
this.blockManager.reload();
|
||||
this.worldManager.reload();
|
||||
this.vanillaLootManager.reload();
|
||||
// load configs here
|
||||
this.packManager.reload();
|
||||
// load at last
|
||||
this.guiManager.reload();
|
||||
// delayed load
|
||||
this.translationManager.delayedLoad();
|
||||
this.blockManager.delayedLoad();
|
||||
this.furnitureManager.delayedLoad();
|
||||
this.itemBrowserManager.delayedLoad();
|
||||
this.soundManager.delayedLoad();
|
||||
this.imageManager.delayedLoad();
|
||||
// reset debugger
|
||||
if (ConfigManager.debug()) {
|
||||
this.debugger = (s) -> logger.info("[Debug] " + s.get());
|
||||
} else {
|
||||
this.debugger = (s) -> {};
|
||||
this.packManager.reload();
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.reload();
|
||||
}
|
||||
} finally {
|
||||
this.recipeManager.delayedLoad().thenRun(() -> this.isReloading = false);
|
||||
}
|
||||
// now we load resources
|
||||
this.packManager.loadResources(reloadRecipe);
|
||||
// handle some special client lang for instance block_name
|
||||
this.translationManager.delayedLoad();
|
||||
// init suggestions and packet mapper
|
||||
this.blockManager.delayedLoad();
|
||||
// init suggestions
|
||||
this.furnitureManager.delayedLoad();
|
||||
// sort the categories
|
||||
this.itemBrowserManager.delayedLoad();
|
||||
// collect illegal characters from minecraft:default font
|
||||
this.fontManager.delayedLoad();
|
||||
if (reloadRecipe) {
|
||||
// convert data pack recipes
|
||||
this.recipeManager.delayedLoad();
|
||||
}
|
||||
long time2 = System.currentTimeMillis();
|
||||
long asyncTime = time2 - time1;
|
||||
syncExecutor.execute(() -> {
|
||||
try {
|
||||
long time3 = System.currentTimeMillis();
|
||||
// register songs
|
||||
this.soundManager.runDelayedSyncTasks();
|
||||
// register recipes
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.runDelayedSyncTasks();
|
||||
}
|
||||
long time4 = System.currentTimeMillis();
|
||||
long syncTime = time4 - time3;
|
||||
this.reloadEventDispatcher.accept(this);
|
||||
future.complete(ReloadResult.success(asyncTime, syncTime));
|
||||
} finally {
|
||||
this.isReloading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.networkManager.enable();
|
||||
this.templateManager = new TemplateManagerImpl(this);
|
||||
public void onPluginEnable() {
|
||||
this.networkManager.init();
|
||||
this.templateManager = new TemplateManagerImpl();
|
||||
this.itemBrowserManager = new ItemBrowserManagerImpl(this);
|
||||
this.commandManager.registerDefaultFeatures();
|
||||
// delay the reload so other plugins can register some parsers
|
||||
// delay the reload so other plugins can register some custom parsers
|
||||
this.scheduler.sync().runDelayed(() -> {
|
||||
this.registerParsers();
|
||||
this.registerDefaultParsers();
|
||||
// hook external item plugins
|
||||
this.itemManager.delayedInit();
|
||||
this.reload();
|
||||
// hook worldedit
|
||||
this.blockManager.delayedInit();
|
||||
// register listeners and tasks
|
||||
this.guiManager.delayedInit();
|
||||
this.recipeManager.delayedInit();
|
||||
this.blockManager.delayedInit();
|
||||
this.worldManager.delayedInit();
|
||||
this.packManager.delayedInit();
|
||||
this.furnitureManager.delayedInit();
|
||||
this.imageManager.delayedInit();
|
||||
this.fontManager.delayedInit();
|
||||
this.vanillaLootManager.delayedInit();
|
||||
this.delayedEnable();
|
||||
// reload the plugin
|
||||
try {
|
||||
this.reloadPlugin(Runnable::run, Runnable::run, true);
|
||||
} catch (Exception e) {
|
||||
this.logger.warn("Failed to reload plugin on enable stage", e);
|
||||
}
|
||||
// must be after reloading because this process loads furniture
|
||||
this.worldManager.delayedInit();
|
||||
this.furnitureManager.delayedInit();
|
||||
// set up some platform extra tasks
|
||||
this.platformDelayedEnable();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if (this.senderFactory != null) this.senderFactory.close();
|
||||
if (this.commandManager != null) this.commandManager.unregisterFeatures();
|
||||
if (this.networkManager != null) this.networkManager.shutdown();
|
||||
if (this.imageManager != null) this.imageManager.disable();
|
||||
public void onPluginDisable() {
|
||||
if (this.networkManager != null) this.networkManager.disable();
|
||||
if (this.fontManager != null) this.fontManager.disable();
|
||||
if (this.packManager != null) this.packManager.disable();
|
||||
if (this.itemManager != null) this.itemManager.disable();
|
||||
if (this.blockManager != null) this.blockManager.disable();
|
||||
@@ -176,14 +227,38 @@ public abstract class CraftEngine implements Plugin {
|
||||
if (this.guiManager != null) this.guiManager.disable();
|
||||
if (this.soundManager != null) this.soundManager.disable();
|
||||
if (this.vanillaLootManager != null) this.vanillaLootManager.disable();
|
||||
if (this.translationManager != null) this.translationManager.disable();
|
||||
if (this.scheduler != null) this.scheduler.shutdownScheduler();
|
||||
if (this.scheduler != null) this.scheduler.shutdownExecutor();
|
||||
if (this.commandManager != null) this.commandManager.unregisterFeatures();
|
||||
if (this.senderFactory != null) this.senderFactory.close();
|
||||
ResourcePackHost.instance().disable();
|
||||
}
|
||||
|
||||
protected abstract void registerParsers();
|
||||
protected void registerDefaultParsers() {
|
||||
// register template parser
|
||||
this.packManager.registerConfigSectionParser(this.templateManager.parser());
|
||||
// register font parser
|
||||
this.packManager.registerConfigSectionParsers(this.fontManager.parsers());
|
||||
// register item parser
|
||||
this.packManager.registerConfigSectionParser(this.itemManager.parser());
|
||||
// register furniture parser
|
||||
this.packManager.registerConfigSectionParser(this.furnitureManager.parser());
|
||||
// register block parser
|
||||
this.packManager.registerConfigSectionParser(this.blockManager.parser());
|
||||
// register recipe parser
|
||||
this.packManager.registerConfigSectionParser(this.recipeManager.parser());
|
||||
// register category parser
|
||||
this.packManager.registerConfigSectionParser(this.itemBrowserManager.parser());
|
||||
// register translation parser
|
||||
this.packManager.registerConfigSectionParsers(this.translationManager.parsers());
|
||||
// register sound parser
|
||||
this.packManager.registerConfigSectionParsers(this.soundManager.parsers());
|
||||
// register vanilla loot parser
|
||||
this.packManager.registerConfigSectionParser(this.vanillaLootManager.parser());
|
||||
}
|
||||
|
||||
protected abstract void delayedEnable();
|
||||
protected abstract void platformDelayedEnable();
|
||||
|
||||
protected abstract List<Dependency> platformDependencies();
|
||||
|
||||
@@ -203,15 +278,11 @@ public abstract class CraftEngine implements Plugin {
|
||||
Dependencies.BOOSTED_YAML,
|
||||
Dependencies.MINIMESSAGE,
|
||||
Dependencies.TEXT_SERIALIZER_GSON,
|
||||
Dependencies.TEXT_SERIALIZER_JSON
|
||||
Dependencies.TEXT_SERIALIZER_JSON,
|
||||
Dependencies.AHO_CORASICK
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyManager dependencyManager() {
|
||||
return dependencyManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <W> SchedulerAdapter<W> scheduler() {
|
||||
@@ -223,6 +294,33 @@ public abstract class CraftEngine implements Plugin {
|
||||
return classPathAppender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger logger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Supplier<String> message) {
|
||||
debugger.accept(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReloading() {
|
||||
return isReloading;
|
||||
}
|
||||
|
||||
public abstract boolean hasPlaceholderAPI();
|
||||
|
||||
@Override
|
||||
public DependencyManager dependencyManager() {
|
||||
return dependencyManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> ItemManager<T> itemManager() {
|
||||
@@ -240,18 +338,8 @@ public abstract class CraftEngine implements Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageManager imageManager() {
|
||||
return imageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigManager configManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger logger() {
|
||||
return logger;
|
||||
public FontManager imageManager() {
|
||||
return fontManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,9 +368,10 @@ public abstract class CraftEngine implements Plugin {
|
||||
return (RecipeManager<T>) recipeManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public SenderFactory<? extends Plugin, ?> senderFactory() {
|
||||
return senderFactory;
|
||||
public <P extends Plugin, C> SenderFactory<P, C> senderFactory() {
|
||||
return (SenderFactory<P, C>) senderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -309,15 +398,4 @@ public abstract class CraftEngine implements Plugin {
|
||||
public VanillaLootManager vanillaLootManager() {
|
||||
return vanillaLootManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Supplier<String> message) {
|
||||
debugger.accept(message);
|
||||
}
|
||||
|
||||
public boolean isReloading() {
|
||||
return isReloading;
|
||||
}
|
||||
|
||||
public abstract boolean hasPlaceholderAPI();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.momirealms.craftengine.core.plugin;
|
||||
|
||||
public interface Manageable {
|
||||
|
||||
// on plugin enable
|
||||
default void init() {
|
||||
}
|
||||
|
||||
// after all plugins enabled
|
||||
default void delayedInit() {
|
||||
}
|
||||
|
||||
// async reload
|
||||
default void reload() {
|
||||
unload();
|
||||
load();
|
||||
}
|
||||
|
||||
// async unload
|
||||
default void unload() {
|
||||
}
|
||||
|
||||
// async load
|
||||
default void load() {
|
||||
}
|
||||
|
||||
// on plugin disable
|
||||
default void disable() {
|
||||
unload();
|
||||
}
|
||||
|
||||
// after all modules reloaded
|
||||
default void delayedLoad() {
|
||||
}
|
||||
|
||||
// delayed tasks on main thread
|
||||
default void runDelayedSyncTasks() {
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,14 @@ package net.momirealms.craftengine.core.plugin;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.font.ImageManager;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeManager;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLootManager;
|
||||
import net.momirealms.craftengine.core.pack.PackManager;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.config.template.TemplateManager;
|
||||
import net.momirealms.craftengine.core.plugin.dependency.DependencyManager;
|
||||
import net.momirealms.craftengine.core.plugin.gui.GuiManager;
|
||||
@@ -27,7 +27,7 @@ import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface Plugin extends Reloadable {
|
||||
public interface Plugin {
|
||||
|
||||
InputStream resourceStream(String filePath);
|
||||
|
||||
@@ -39,6 +39,8 @@ public interface Plugin extends Reloadable {
|
||||
|
||||
Path dataFolderPath();
|
||||
|
||||
boolean isReloading();
|
||||
|
||||
DependencyManager dependencyManager();
|
||||
|
||||
<W> SchedulerAdapter<W> scheduler();
|
||||
@@ -55,9 +57,9 @@ public interface Plugin extends Reloadable {
|
||||
|
||||
NetworkManager networkManager();
|
||||
|
||||
ImageManager imageManager();
|
||||
FontManager imageManager();
|
||||
|
||||
ConfigManager configManager();
|
||||
Config config();
|
||||
|
||||
TranslationManager translationManager();
|
||||
|
||||
@@ -69,7 +71,7 @@ public interface Plugin extends Reloadable {
|
||||
|
||||
<T> RecipeManager<T> recipeManager();
|
||||
|
||||
SenderFactory<? extends Plugin, ?> senderFactory();
|
||||
<P extends Plugin, C> SenderFactory<P, C> senderFactory();
|
||||
|
||||
WorldManager worldManager();
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package net.momirealms.craftengine.core.plugin;
|
||||
|
||||
public interface Reloadable {
|
||||
|
||||
default void reload() {
|
||||
unload();
|
||||
load();
|
||||
}
|
||||
|
||||
default void enable() {
|
||||
}
|
||||
|
||||
default void unload() {
|
||||
}
|
||||
|
||||
default void load() {
|
||||
}
|
||||
|
||||
default void disable() {
|
||||
unload();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import net.momirealms.craftengine.core.plugin.Plugin;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.locale.CraftEngineCaptionFormatter;
|
||||
import net.momirealms.craftengine.core.plugin.locale.CraftEngineCaptionProvider;
|
||||
import net.momirealms.craftengine.core.util.ArrayUtils;
|
||||
@@ -75,7 +75,7 @@ public abstract class AbstractCommandManager<C> implements CraftEngineCommandMan
|
||||
injectExceptionHandler(CommandExecutionException.class, MinecraftExceptionHandler.createDefaultCommandExecutionHandler(), StandardCaptionKeys.EXCEPTION_UNEXPECTED);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void injectExceptionHandler(Class<? extends Throwable> type, MinecraftExceptionHandler.MessageFactory<C, ?> factory, Caption key) {
|
||||
getCommandManager().exceptionController().registerHandler(type, ctx -> {
|
||||
final @Nullable ComponentLike message = factory.message(captionFormatter, (ExceptionContext) ctx);
|
||||
@@ -126,7 +126,7 @@ public abstract class AbstractCommandManager<C> implements CraftEngineCommandMan
|
||||
|
||||
@Override
|
||||
public void registerDefaultFeatures() {
|
||||
YamlDocument document = ConfigManager.instance().loadYamlConfig(commandsFile,
|
||||
YamlDocument document = Config.instance().loadYamlConfig(commandsFile,
|
||||
GeneralSettings.DEFAULT,
|
||||
LoaderSettings
|
||||
.builder()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user