diff --git a/README.md b/README.md index f0b0d8a09..fefff6da4 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.49") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.49") + compileOnly("net.momirealms:craft-engine-core:0.0.52") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.52") } ``` \ No newline at end of file diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java index 68bcdb48b..8c37c2a04 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java @@ -22,6 +22,7 @@ public class EvtCustomClick extends SkriptEvent { private final static int RIGHT = 1, LEFT = 2, ANY = RIGHT | LEFT; public final static ClickEventTracker interactTracker = new ClickEventTracker(Skript.getInstance()); + @SuppressWarnings("unchecked") public static void register() { Skript.registerEvent("Interact Custom Block Furniture", EvtCustomClick.class, new Class[]{CustomBlockInteractEvent.class, FurnitureInteractEvent.class}, "[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-unsafeblockstatematchers/strings%] [(with|using|holding) %-itemtype%]", diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index eda56d932..da8cf0aa0 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -351,6 +351,8 @@ chunk-system: # 4 = LZ4 | Blazing-Fast Blazing-Fast Low Low | # 5 = ZSTD | Medium-Fast Fast High Medium | compression-method: 4 + # This might not work for some server forks that modify how Minecraft saves chunks. + fast-palette-injection: false # Auto-convert custom blocks -> vanilla blocks when unloading chunks # # - When ENABLED (true): diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml index 4476e5c87..b2f321624 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml @@ -314,7 +314,7 @@ templates#models#crossbow: threshold: 0.58 - model: type: minecraft:model - path: "{pulling_2_path}" + path: "{pulling_2_model}" threshold: 1.0 fallback: type: minecraft:model diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index 6ac0e36c9..7b9afee86 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -1,6 +1,7 @@ # Don't change this lang-version: "${lang_version}" +# Commands exception.invalid_syntax: "Invalid syntax. Correct syntax: " exception.invalid_argument: "Invalid argument. Reason: " exception.invalid_sender: " is not allowed to execute that command. Must be of type " @@ -63,79 +64,213 @@ command.upload.failure.not_supported: "Current hosting method '' doe command.upload.on_progress: "Started uploading progress. Check the console for more information." command.send_resource_pack.success.single: "Sent resource pack to ." command.send_resource_pack.success.multiple: "Send resource packs to players." -warning.config.image.duplicated: "Issue found in file - Duplicated image ''." -warning.config.image.lack_height: "Issue found in file - The image '' is missing the required 'height' argument." -warning.config.image.height_smaller_than_ascent: "Issue found in file - The image '' violates the bitmap image rule: 'height' should be no lower than 'ascent'." -warning.config.image.no_file: "Issue found in file - The image '' is missing the required 'file' argument." -warning.config.image.invalid_resource_location: "Issue found in file - The image '' has a 'file' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.image.invalid_font_name: "Issue found in file - The image '' has a 'font' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.image.lack_char: "Issue found in file - The image '' is missing the required 'char' argument." -warning.config.image.codepoint_in_use: "Issue found in file - The image '' is using a character[()] in font that has been used by another image ''." -warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grind." -warning.config.image.file_not_exist: "Issue found in file - PNG file '' not found for image ''." -warning.config.recipe.duplicated: "Issue found in file - Duplicated recipe ''." +warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." +warning.config.type.int: "Issue found in file - Failed to load '': Cannot cast '' to integer type for option ''." +warning.config.type.float: "Issue found in file - Failed to load '': Cannot cast '' to float type for option ''." +warning.config.type.double: "Issue found in file - Failed to load '': Cannot cast '' to double type for option ''." +warning.config.type.quaternionf: "Issue found in file - Failed to load '': Cannot cast '' to Quaternionf type for option ''." +warning.config.type.vector3f: "Issue found in file - Failed to load '': Cannot cast '' to Vector3f type for option ''." +warning.config.structure.not_section: "Issue found in file - The config '' is expected to be a config section while it's actually a(n) ''." +warning.config.image.duplicate: "Issue found in file - Duplicated image ''. Please check if there is the same configuration in other files." +warning.config.image.missing_height: "Issue found in file - The image '' is missing the required 'height' argument." +warning.config.image.height_ascent_conflict: "Issue found in file - The image '' violates the bitmap image rule: 'height' argument '' should be no lower than 'ascent' argument ''." +warning.config.image.missing_file: "Issue found in file - The image '' is missing the required 'file' argument." +warning.config.image.invalid_file_chars: "Issue found in file - The image '' has a 'file' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.image.invalid_font_chars: "Issue found in file - The image '' has a 'font' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.image.missing_char: "Issue found in file - The image '' is missing the required 'char' argument." +warning.config.image.codepoint_conflict: "Issue found in file - The image '' is using a character '()' in font that has been used by another image ''." +warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grid." +warning.config.image.file_not_found: "Issue found in file - PNG file '' not found for image ''." +warning.config.image.invalid_hex_value: "Issue found in file - The image '' is using a unicode character '' that is not a valid hexadecimal (radix 16) value." +warning.config.recipe.duplicate: "Issue found in file - Duplicated recipe ''. Please check if there is the same configuration in other files." +warning.config.recipe.missing_type: "Issue found in file - The recipe '' is missing the required 'type' argument." +warning.config.recipe.invalid_type: "Issue found in file - The recipe '' is using an invalid recipe type ''." +warning.config.recipe.invalid_item: "Issue found in file - The recipe '' is using an invalid item ''." +warning.config.recipe.missing_ingredient: "Issue found in file - The cooking recipe '' is missing the required 'ingredient' argument." +warning.config.recipe.missing_result: "Issue found in file - The recipe '' is missing the required 'result' argument." +warning.config.recipe.result.missing_id: "Issue found in file - The recipe '' is missing the required argument 'id' for recipe result." +warning.config.recipe.crafting.invalid_category: "Issue found in file - The crafting recipe '' is using an invalid category ''. Allowed categories: []." +warning.config.recipe.cooking.invalid_category: "Issue found in file - The cooking recipe '' is using an invalid category ''. Allowed categories: []." +warning.config.recipe.shaped.missing_pattern: "Issue found in file - The shaped recipe '' is missing the required argument 'pattern'." +warning.config.recipe.shaped.invalid_pattern: "Issue found in file - The shaped recipe '' is using an invalid pattern ''." +warning.config.recipe.shaped.invalid_symbol: "Issue found in file - The shaped recipe '' is using an invalid symbol '' in pattern." +warning.config.recipe.smithing_transform.post_processor.missing_type: "Issue found in file - The smithing transform recipe '' is missing the required argument 'type' for one of the post-processors." +warning.config.recipe.smithing_transform.post_processor.invalid_type: "Issue found in file - The smithing transform recipe '' is using an invalid post processor type ''." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Issue found in file - The smithing transform recipe '' is missing the required argument 'components' for post-processors 'keep_components'." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Issue found in file - The smithing transform recipe '' is missing the required argument 'tags' for post-processors 'keep_tags'." warning.config.i18n.unknown_locale: "Issue found in file - Unknown locale ''." -warning.config.template.duplicated: "Issue found in file - Duplicated template ''." -warning.config.vanilla_loot.type_not_exist: "Issue found in file - 'type' not set for vanilla loot ''." -warning.config.vanilla_loot.block.invalid_target: "Issue found in file - Invalid block target [] in vanilla loot ''." -warning.config.sound.duplicated: "Issue found in file - Duplicated sound ''." -warning.config.jukebox_song.duplicated: "Issue found in file - Duplicated jukebox song ''." -warning.config.furniture.duplicated: "Issue found in file - Duplicated furniture ''." -warning.config.furniture.lack_placement: "Issue found in file - The furniture '' is missing the required 'placement' argument." -warning.config.furniture.element.lack_item: "Issue found in file - The furniture '' is missing the required 'item' argument for one of its elements." -warning.config.item.duplicated: "Issue found in file - Duplicated item ''." -warning.config.item.lack_material: "Issue found in file - The item '' is missing the required 'material' argument." +warning.config.template.duplicate: "Issue found in file - Duplicated template ''. Please check if there is the same configuration in other files." +warning.config.template.argument.self_increase_int.invalid_range: "Issue found in file - The template '' is using a 'from' '' larger than 'to' '' in 'self_increase_int' argument." +warning.config.template.argument.list.invalid_type: "Issue found in file - The template '' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) ''." +warning.config.vanilla_loot.missing_type: "Issue found in file - The vanilla loot '' is missing the required 'type' argument." +warning.config.vanilla_loot.invalid_type: "Issue found in file - The vanilla loot '' is using an invalid type ''. Allowed types: []." +warning.config.vanilla_loot.block.invalid_target: "Issue found in file - Invalid block target '' in vanilla loot ''." +warning.config.sound.duplicate: "Issue found in file - Duplicated sound ''. Please check if there is the same configuration in other files." +warning.config.sound.missing_sounds: "Issue found in file - The sound '' is missing the required 'sounds' argument." +warning.config.sound.missing_name: "Issue found in file - The sound '' is missing the required 'name' argument." +warning.config.jukebox_song.duplicate: "Issue found in file - Duplicated jukebox song ''. Please check if there is the same configuration in other files." +warning.config.jukebox_song.missing_sound: "Issue found in file - The jukebox song '' is missing the required 'sound' argument." +warning.config.furniture.duplicate: "Issue found in file - Duplicated furniture ''. Please check if there is the same configuration in other files." +warning.config.furniture.missing_placement: "Issue found in file - The furniture '' is missing the required 'placement' argument." +warning.config.furniture.element.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for one of its elements." +warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." +warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." +warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." +warning.config.item.duplicate: "Issue found in file - Duplicated item ''. Please check if there is the same configuration in other files." +warning.config.item.settings.unknown: "Issue found in file - The item '' is using an unknown setting type ''." +warning.config.item.missing_material: "Issue found in file - The item '' is missing the required 'material' argument." warning.config.item.invalid_material: "Issue found in file - The item '' is using an invalid material type ''." -warning.config.item.bad_custom_model_data_value: "Issue found in file - The item '' is using a custom model data [] that is too large. It's recommended to use a value lower than 16,777,216." -warning.config.item.custom_model_data_conflict: "Issue found in file - The item '' is using a custom model data [] that has been occupied by item ''" -warning.config.item.lack_model_id: "Issue found in file - The item '' is missing the required 'custom-model-data' or 'item-model' argument." -warning.config.block.duplicated: "Issue found in file - Duplicated block ''." -warning.config.block.lack_state: "Issue found in file - The block '' is missing the required 'state' argument." -warning.config.block.state.lack_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'." -warning.config.block.state.lack_state: "Issue found in file - The block '' is missing the required 'state' argument for 'state'." -warning.config.block.state.lack_properties: "Issue found in file - The block '' is missing the required 'properties' section for 'states'." -warning.config.block.state.lack_appearances: "Issue found in file - The block '' is missing the required 'appearances' section for 'states'." -warning.config.block.state.lack_variants: "Issue found in file - The block '' is missing the required 'variants' section for 'states'." -warning.config.block.state.variant.lack_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''." +warning.config.item.bad_custom_model_data: "Issue found in file - The item '' is using a custom model data '' that is too large. It's recommended to use a value lower than 16,777,216." +warning.config.item.custom_model_data_conflict: "Issue found in file - The item '' is using a custom model data '' that has been occupied by item ''." +warning.config.item.missing_model_id: "Issue found in file - The item '' is missing the required 'custom-model-data' or 'item-model' argument." +warning.config.item.behavior.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for its item behavior." +warning.config.item.behavior.invalid_type: "Issue found in file - The item '' is using an invalid item behavior type ''." +warning.config.item.behavior.block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'block_item' behavior." +warning.config.item.behavior.furniture.missing_furniture: "Issue found in file - The item '' is missing the required 'furniture' argument for 'furniture_item' behavior." +warning.config.item.behavior.liquid_collision.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'liquid_collision_block_item' behavior." +warning.config.item.model.invalid_type: "Issue found in file - The item '' is using an invalid model type ''." +warning.config.item.model.tint.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for tint." +warning.config.item.model.tint.invalid_type: "Issue found in file - The item '' is using an invalid tint type ''." +warning.config.item.model.tint.constant.missing_value: "Issue found in file - The item '' is missing the required 'value' argument for constant tint." +warning.config.item.model.tint.grass.invalid_temp: "Issue found in file - The item '' is using an invalid temperature '' for grass tint, which is expected to be between 0 and 1." +warning.config.item.model.tint.grass.invalid_downfall: "Issue found in file - The item '' is using an invalid downfall '' for grass tint, which is expected to be between 0 and 1." +warning.config.item.model.tint.invalid_value: "Issue found in file - The item '' is using an invalid tint ''." +warning.config.item.model.base.missing_path: "Issue found in file - The item '' is missing the required 'path' argument for model 'minecraft:model'." +warning.config.item.model.base.invalid_path: "Issue found in file - The item '' has an invalid 'path' argument '' for model 'minecraft:model' which contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.condition.missing_property: "Issue found in file - The item '' is missing the required 'property' argument for model 'minecraft:condition'." +warning.config.item.model.condition.invalid_property: "Issue found in file - The item '' is using an invalid property '' for model 'minecraft:condition'." +warning.config.item.model.condition.missing_on_true: "Issue found in file - The item '' is missing the required 'on-true' argument for model 'minecraft:condition'." +warning.config.item.model.condition.missing_on_false: "Issue found in file - The item '' is missing the required 'on-false' argument for model 'minecraft:condition'." +warning.config.item.model.condition.keybind.missing: "Issue found in file - The item '' is missing the required 'keybind' argument for property 'minecraft:keybind_down'." +warning.config.item.model.condition.has_component.missing_component: "Issue found in file - The item '' is missing the required 'component' argument for property 'minecraft:has_component'." +warning.config.item.model.composite.missing_models: "Issue found in file - The item '' is missing the required 'models' argument for 'minecraft:composite' model." +warning.config.item.model.range_dispatch.missing_property: "Issue found in file - The item '' is missing the required 'property' argument for model 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.invalid_property: "Issue found in file - The item '' is using an invalid property '' for model 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.missing_entries: "Issue found in file - The item '' is missing the required 'entries' argument for model 'minecraft:composite'." +warning.config.item.model.range_dispatch.entry.missing_model: "Issue found in file - The item '' is missing the required 'model' argument for one of the entries in model 'minecraft:composite'." +warning.config.item.model.range_dispatch.compass.missing_target: "Issue found in file - The item '' is missing the required 'target' argument for property 'minecraft:compass'." +warning.config.item.model.range_dispatch.time.missing_source: "Issue found in file - The item '' is missing the required 'source' argument for property 'minecraft:time'." +warning.config.item.model.select.missing_property: "Issue found in file - The item '' is missing the required 'property' argument for model 'minecraft:select'." +warning.config.item.model.select.invalid_property: "Issue found in file - The item '' is using an invalid property '' for model 'minecraft:select'." +warning.config.item.model.select.missing_cases: "Issue found in file - The item '' is missing the required 'cases' argument for model 'minecraft:select'." +warning.config.item.model.select.case.missing_when: "Issue found in file - The item '' is missing the required 'when' argument for one of the cases in model 'minecraft:select'." +warning.config.item.model.select.case.missing_model: "Issue found in file - The item '' is missing the required 'model' argument for one of the cases in model 'minecraft:select'." +warning.config.item.model.select.block_state.missing_property: "Issue found in file - The item '' is missing the required 'block-state-property' argument for property 'minecraft:block_state'." +warning.config.item.model.select.local_time.missing_pattern: "Issue found in file - The item '' is missing the required 'pattern' argument for property 'minecraft:local_time'." +warning.config.item.model.special.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for model 'minecraft:special'." +warning.config.item.model.special.invalid_type: "Issue found in file - The item '' is using an invalid type '' for model 'minecraft:special'." +warning.config.item.model.special.banner.missing_color: "Issue found in file - The item '' is missing the required 'color' argument for special model 'minecraft:banner'." +warning.config.item.model.special.bed.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:bed'." +warning.config.item.model.special.sign.missing_wood_type: "Issue found in file - The item '' is missing the required 'wood-type' argument for special model 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.sign.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.chest.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:chest'." +warning.config.item.model.special.chest.invalid_openness: "Issue found in file - The item '' is using an invalid 'openness' value '' for special model 'minecraft:chest'. Valid range '0~1.'" +warning.config.item.model.special.shulker_box.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:shulker_box'." +warning.config.item.model.special.shulker_box.invalid_openness: "Issue found in file - The item '' is using an invalid 'openness' value '' for special model 'minecraft:shulker_box'. Valid range '0~1.'" +warning.config.item.model.special.head.missing_kind: "Issue found in file - The item '' is missing the required 'kind' argument for special model 'minecraft:head'." +warning.config.item.model.special.head.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:head'." +warning.config.block.duplicate: "Issue found in file - Duplicated block ''. Please check if there is the same configuration in other files." +warning.config.block.missing_state: "Issue found in file - The block '' is missing the required 'state' argument." +warning.config.block.state.property.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for property ''." +warning.config.block.state.property.invalid_type: "Issue found in file - The block '' is using the invalid type argument '' for property ''." +warning.config.block.state.property.integer.invalid_range: "Issue found in file - The block '' is using the invalid 'range' argument '' for integer property ''. Correct syntax: 1~2." +warning.config.block.state.property.invalid_format: "Issue found in file - The block '' is using an invalid block state property format ''." +warning.config.block.state.missing_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'. 'id' is the serverside block id which is unique for each type of block state. If you create a serverside side block with 'note_block' and id 30, then the real block id would be 'craftengine:note_block_30'." +warning.config.block.state.missing_state: "Issue found in file - The block '' is missing the required 'state' argument for 'state'." +warning.config.block.state.missing_properties: "Issue found in file - The block '' is missing the required 'properties' section for 'states'." +warning.config.block.state.missing_appearances: "Issue found in file - The block '' is missing the required 'appearances' section for 'states'." +warning.config.block.state.missing_variants: "Issue found in file - The block '' is missing the required 'variants' section for 'states'." +warning.config.block.state.variant.missing_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''." warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''." -warning.config.block.state.invalid_state: "Issue found in file - The block '' is using an invalid vanilla block state ''." -warning.config.block.state.unavailable_state: "Issue found in file - The block '' is using an unavailable vanilla block state ''." -warning.config.block.state.invalid_vanilla_state_id: "Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'." -warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''." -warning.config.block.state.bind_real_state: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''." -warning.config.block.state.invalid_property_structure: "Issue found in file - The block '' has an invalid property structure ''." -warning.config.block.state.invalid_property: "Issue found in file - Failed to create property '' for block '': ." -warning.config.block.state.no_model_set: "Issue found in file - The block '' is missing the required 'model' or 'models' argument." -warning.config.block.state.invalid_real_state_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up." -warning.config.block.state.model.lack_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'." -warning.config.block.state.model.invalid_resource_location: "Issue found in file - The block '' has a 'path' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''" -warning.config.model.generation.texture.invalid_resource_location: "Issue found in file - The config '' has a '' texture argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.emoji.lack_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." -warning.config.emoji.duplicated: "Issue found in file - Duplicated emoji ''." +warning.config.block.state.invalid_vanilla: "Issue found in file - The block '' is using an invalid vanilla block state ''." +warning.config.block.state.unavailable_vanilla: "Issue found in file - The block '' is using an unavailable vanilla block state ''. Please free that state in mappings.yml." +warning.config.block.state.invalid_vanilla_id: "Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'." +warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''." +warning.config.block.state.bind_failed: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''." +warning.config.block.state.missing_model: "Issue found in file - The block '' is missing the required 'model' or 'models' argument." +warning.config.block.state.invalid_real_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up." +warning.config.block.state.model.missing_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'." +warning.config.block.state.model.invalid_path: "Issue found in file - The block '' has a 'path' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.block.settings.unknown: "Issue found in file - The block '' is using an unknown setting type ''." +warning.config.block.behavior.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for its block behavior." +warning.config.block.behavior.invalid_type: "Issue found in file - The block '' is using an invalid block behavior type ''." +warning.config.block.behavior.concrete.missing_solid: "Issue found in file - The block '' is missing the required 'solid-block' option for 'concrete_block' behavior." +warning.config.block.behavior.crop.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'crop_block' behavior." +warning.config.block.behavior.sugar_cane.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'sugar_cane_block' behavior." +warning.config.block.behavior.leaves.missing_persistent: "Issue found in file - The block '' is missing the required 'persistent' property for 'leaves_block' behavior." +warning.config.block.behavior.leaves.missing_distance: "Issue found in file - The block '' is missing the required 'distance' property for 'leaves_block' behavior." +warning.config.block.behavior.sapling.missing_stage: "Issue found in file - The block '' is missing the required 'stage' property for 'sapling_block' behavior." +warning.config.block.behavior.sapling.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'sapling_block' behavior." +warning.config.block.behavior.strippable.missing_stripped: "Issue found in file - The block '' is missing the required 'stripped' argument for 'strippable_block' behavior." +warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." +warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." +warning.config.model.generation.texture.invalid: "Issue found in file - The config '' has a texture '' with path '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.model.generation.parent.invalid: "Issue found in file - The config '' has a parent argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.emoji.missing_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." +warning.config.emoji.duplicate: "Issue found in file - Duplicated emoji ''. Please check if there is the same configuration in other files." warning.config.emoji.invalid_image: "Issue found in file - The emoji '' has an invalid 'image' argument ''." -warning.config.advancement.duplicated: "Issue found in file - Duplicated advancement ''." -warning.config.host.lack_type: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'type' for host." -warning.config.host.invalid_type: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Host 'type' [] is invalid. Please read https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host" -warning.config.host.external.lack_url: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'url' for external host." -warning.config.host.alist.lack_api_url: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-url' for alist host." -warning.config.host.alist.lack_username: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'username' or environment variable 'CE_ALIST_USERNAME' for alist host." -warning.config.host.alist.lack_password: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'password' or environment variable 'CE_ALIST_PASSWORD' for alist host." -warning.config.host.alist.lack_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for alist host." -warning.config.host.dropbox.lack_app_key: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'app-key' or environment variable 'CE_DROPBOX_APP_KEY' for dropbox host." -warning.config.host.dropbox.lack_app_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'app-secret' or environment variable 'CE_DROPBOX_APP_SECRET' for dropbox host." -warning.config.host.dropbox.lack_refresh_token: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'refresh-token' or environment variable 'CE_DROPBOX_REFRESH_TOKEN' for dropbox host." -warning.config.host.dropbox.lack_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for dropbox host." -warning.config.host.lobfile.lack_api_key: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-key' for lobfile host." -warning.config.host.onedrive.lack_client_id: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'client-id' or environment variable 'CE_ONEDRIVE_CLIENT_ID' for onedrive host." -warning.config.host.onedrive.lack_client_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'client-secret' or environment variable 'CE_ONEDRIVE_CLIENT_SECRET' for onedrive host." -warning.config.host.onedrive.lack_refresh_token: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'refresh-token' or environment variable 'CE_ONEDRIVE_REFRESH_TOKEN' for onedrive host." -warning.config.host.onedrive.lack_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for onedrive host." -warning.config.host.s3.lack_endpoint: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'endpoint' for s3 host." -warning.config.host.s3.lack_bucket: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'bucket' for s3 host." -warning.config.host.s3.lack_access_key_id: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'access-key-id' or environment variable 'CE_S3_ACCESS_KEY_ID' for s3 host." -warning.config.host.s3.lack_access_key_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'access-key-secret' or environment variable 'CE_S3_ACCESS_KEY_SECRET' for s3 host." -warning.config.host.s3.lack_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for s3 host." -warning.config.host.self.lack_ip: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'ip' for self host." -warning.config.host.self.invalid_port: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid 'port' [] for self host." \ No newline at end of file +warning.config.advancement.duplicate: "Issue found in file - Duplicated advancement ''. Please check if there is the same configuration in other files." +warning.config.loot_table.missing_pools: "Issue found in file - '' has a misconfigured loot table which is missing the required 'pools' argument." +warning.config.loot_table.invalid_pools_type: "Issue found in file - '' has a misconfigured loot table, 'pools' should be a string/map list, current type: ''." +warning.config.loot_table.invalid_conditions_type: "Issue found in file - '' has a misconfigured loot table, 'conditions' should be a map list, current type: ''." +warning.config.loot_table.invalid_functions_type: "Issue found in file - '' has a misconfigured loot table, 'functions' should be a map list, current type: ''." +warning.config.loot_table.invalid_entries_type: "Issue found in file - '' has a misconfigured loot table, 'entries' should be a map list, current type: ''." +warning.config.loot_table.function.missing_type: "Issue found in file - '' has a misconfigured loot table, one of the functions is missing the required 'type' argument." +warning.config.loot_table.function.invalid_type: "Issue found in file - '' has a misconfigured loot table, one of the functions is using an invalid function type ''." +warning.config.loot_table.function.apply_bonus.missing_enchantment: "Issue found in file - '' has a misconfigured loot table, function 'apply_bonus' is missing the required 'enchantment' argument." +warning.config.loot_table.function.apply_bonus.missing_formula: "Issue found in file - '' has a misconfigured loot table, function 'apply_bonus' is missing the required 'formula' argument." +warning.config.loot_table.function.drop_exp.missing_count: "Issue found in file - '' has a misconfigured loot table, function 'drop_exp' is missing the required 'count' argument." +warning.config.loot_table.function.set_count.missing_count: "Issue found in file - '' has a misconfigured loot table, function 'set_count' is missing the required 'count' argument." +warning.config.loot_table.entry.missing_type: "Issue found in file - '' has a misconfigured loot table, one of the entries is missing the required 'type' argument." +warning.config.loot_table.entry.invalid_type: "Issue found in file - '' has a misconfigured loot table, one of the entries is using an invalid entry type ''." +warning.config.loot_table.entry.exp.missing_count: "Issue found in file - '' has a misconfigured loot table, entry 'exp' is missing the required 'count' argument." +warning.config.loot_table.entry.item.missing_item: "Issue found in file - '' has a misconfigured loot table, entry 'item' is missing the required 'item' argument." +warning.config.loot_table.condition.missing_type: "Issue found in file - '' has a misconfigured loot table, one of the conditions is missing the required 'type' argument." +warning.config.loot_table.condition.invalid_type: "Issue found in file - '' has a misconfigured loot table, one of the conditions is using an invalid condition type ''." +warning.config.loot_table.condition.table_bonus.missing_enchantment: "Issue found in file - '' has a misconfigured loot table, 'table_bonus' condition is missing the required 'enchantment' argument." +warning.config.loot_table.condition.table_bonus.missing_chances: "Issue found in file - '' has a misconfigured loot table, 'table_bonus' condition is missing the required 'chances' argument." +warning.config.loot_table.number.missing_type: "Issue found in file - '' has a misconfigured loot table, one of the numbers is missing the required 'type' argument." +warning.config.loot_table.number.invalid_type: "Issue found in file - '' has a misconfigured loot table, one of the numbers is using an invalid number type ''." +warning.config.host.missing_type: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'type' argument for host." +warning.config.host.invalid_type: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Host type '' is invalid. Please read https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host." +warning.config.host.external.missing_url: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'url' argument for external host." +warning.config.host.alist.missing_api_url: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'api-url' argument for alist host." +warning.config.host.alist.missing_username: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'username' argument or environment variable 'CE_ALIST_USERNAME' for alist host." +warning.config.host.alist.missing_password: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'password' argument or environment variable 'CE_ALIST_PASSWORD' for alist host." +warning.config.host.alist.missing_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for alist host." +warning.config.host.dropbox.missing_app_key: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'app-key' argument or environment variable 'CE_DROPBOX_APP_KEY' for dropbox host." +warning.config.host.dropbox.missing_app_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'app-secret' argument or environment variable 'CE_DROPBOX_APP_SECRET' for dropbox host." +warning.config.host.dropbox.missing_refresh_token: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'refresh-token' argument or environment variable 'CE_DROPBOX_REFRESH_TOKEN' for dropbox host." +warning.config.host.dropbox.missing_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for dropbox host." +warning.config.host.lobfile.missing_api_key: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'api-key' argument for lobfile host." +warning.config.host.onedrive.missing_client_id: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'client-id' argument or environment variable 'CE_ONEDRIVE_CLIENT_ID' for onedrive host." +warning.config.host.onedrive.missing_client_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'client-secret' argument or environment variable 'CE_ONEDRIVE_CLIENT_SECRET' for onedrive host." +warning.config.host.onedrive.missing_refresh_token: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'refresh-token' argument or environment variable 'CE_ONEDRIVE_REFRESH_TOKEN' for onedrive host." +warning.config.host.onedrive.missing_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for onedrive host." +warning.config.host.s3.missing_endpoint: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'endpoint' argument for s3 host." +warning.config.host.s3.missing_bucket: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'bucket' argument for s3 host." +warning.config.host.s3.missing_access_key: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'access-key-id' argument or environment variable 'CE_S3_ACCESS_KEY_ID' for s3 host." +warning.config.host.s3.missing_secret: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'access-key-secret' argument or environment variable 'CE_S3_ACCESS_KEY_SECRET' for s3 host." +warning.config.host.s3.missing_upload_path: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for s3 host." +warning.config.host.self.missing_ip: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'ip' argument for self host." +warning.config.host.self.invalid_port: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid port '' for self host." +warning.config.host.gitlab.missing_url: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'gitlab-url' argument for gitlab host." +warning.config.host.gitlab.missing_token: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'access-token' argument for gitlab host." +warning.config.host.gitlab.missing_project: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'project-id' argument for gitlab host." +warning.config.host.proxy.missing_host: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'host' argument for proxy." +warning.config.host.proxy.missing_port: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'port' argument for proxy." +warning.config.host.proxy.missing_scheme: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'scheme' argument for proxy." +warning.config.host.proxy.invalid: "Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid proxy ''." +warning.config.conflict_matcher.missing_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the handlers." +warning.config.conflict_matcher.invalid_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the terms is using the invalid type ''." +warning.config.conflict_matcher.exact.missing_path: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'exact' matcher." +warning.config.conflict_matcher.contains.missing_path: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'contains' matcher." +warning.config.conflict_matcher.filename.missing_name: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'filename' matcher." +warning.config.conflict_matcher.pattern.missing_pattern: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'pattern' argument for 'pattern' matcher." +warning.config.conflict_matcher.parent_prefix.missing_prefix: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'prefix' argument for 'parent_path_prefix' matcher." +warning.config.conflict_matcher.parent_suffix.missing_suffix: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'suffix' argument for 'parent_path_suffix' matcher." +warning.config.conflict_matcher.inverted.missing_term: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'term' argument for 'inverted' matcher." +warning.config.conflict_matcher.all_of.missing_terms: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'all_of' matcher." +warning.config.conflict_matcher.any_of.missing_terms: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'any_of' matcher." +warning.config.conflict_resolution.missing_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the resolutions." +warning.config.conflict_resolution.invalid_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the resolutions is using the invalid type ''." \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index e3383afee..860a110af 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -1,6 +1,7 @@ # 别动这个 lang-version: "${lang_version}" +# 命令 exception.invalid_syntax: "无效语法. 正确语法: " exception.invalid_argument: "无效参数. 原因: " exception.invalid_sender: " 不允许执行该命令. 执行者必须是 " @@ -63,56 +64,213 @@ command.upload.failure.not_supported: "当前托管模式 '' 不支 command.upload.on_progress: "已开始上传进程. 检查控制台以获取详细信息." command.send_resource_pack.success.single: "发送资源包给 " command.send_resource_pack.success.multiple: "发送资源包给 个玩家" -warning.config.image.duplicated: "在文件 中发现问题 - 图片 '' 重复定义" -warning.config.image.lack_height: "在文件 中发现问题 - 图片 '' 缺少必要的 'height' 高度参数" -warning.config.image.height_smaller_than_ascent: "在文件 中发现问题 - 图片 '' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度" -warning.config.image.no_file: "在文件 中发现问题 - 图片 '' 缺少必要的 'file' 文件参数" -warning.config.image.invalid_resource_location: "在文件 中发现问题 - 图片 '' 的 'file' 参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.image.invalid_font_name: "在文件 中发现问题 - 图片 '' 的 'font' 字体参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.image.lack_char: "在文件 中发现问题 - 图片 '' 缺少必要的 'char' 字符参数" -warning.config.image.codepoint_in_use: "在文件 中发现问题 - 图片 '' 使用的字体 字符 [()] 已被其他图片 '' 占用" -warning.config.image.invalid_codepoint_grid: "在文件 中发现问题 - 图片 '' 的 'chars' 码位网格配置无效" -warning.config.image.file_not_exist: "在文件 中发现问题 - 图片 '' 对应的PNG文件 不存在" -warning.config.recipe.duplicated: "在文件 中发现问题 - 配方 '' 重复定义" -warning.config.i18n.unknown_locale: "在文件 中发现问题 - 未知的语言区域 ''" -warning.config.template.duplicated: "在文件 中发现问题 - 模板 '' 重复定义" -warning.config.vanilla_loot.type_not_exist: "在文件 中发现问题 - 原版战利品 '' 未设置 'type' 类型参数" -warning.config.vanilla_loot.block.invalid_target: "在文件 中发现问题 - 原版战利品 '' 中的方块目标 [] 无效" -warning.config.sound.duplicated: "在文件 中发现问题 - 音效 '' 重复定义" -warning.config.jukebox_song.duplicated: "在文件 中发现问题 - 唱片机歌曲 '' 重复定义" -warning.config.furniture.duplicated: "在文件 中发现问题 - 家具 '' 重复定义" -warning.config.furniture.lack_placement: "在文件 中发现问题 - 家具 '' 缺少必要的 'placement' 放置参数" -warning.config.furniture.element.lack_item: "在文件 中发现问题 - 家具 '' 的某个元素缺少必要的 'item' 物品参数" -warning.config.item.duplicated: "在文件 中发现问题 - 物品 '' 重复定义" -warning.config.item.lack_material: "在文件 中发现问题 - 物品 '' 缺少必要的 'material' 材料参数" -warning.config.item.invalid_material: "在文件 中发现问题 - 物品 '' 使用了无效的材料类型 ''" -warning.config.item.bad_custom_model_data_value: "在文件 中发现问题 - 物品 '' 的自定义模型数据值 [] 过大,建议使用低于16,777,216的值" -warning.config.item.custom_model_data_conflict: "在文件 中发现问题 - 物品 '' 的自定义模型数据 [] 与物品 '' 发生冲突" -warning.config.item.lack_model_id: "在文件 中发现问题 - 物品 '' 缺少必要的 'custom-model-data' 或 'item-model' 模型标识参数" -warning.config.block.duplicated: "在文件 中发现问题 - 方块 '' 重复定义" -warning.config.block.lack_state: "在文件 中发现问题 - 方块 '' 缺少必要的 'state' 状态参数" -warning.config.block.state.lack_real_id: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'id' 标识参数" -warning.config.block.state.lack_state: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'state' 状态参数" -warning.config.block.state.lack_properties: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'properties' 属性配置" -warning.config.block.state.lack_appearances: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'appearances' 外观配置" -warning.config.block.state.lack_variants: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'variants' 变体配置" -warning.config.block.state.variant.lack_appearance: "在文件 中发现问题 - 方块 '' 的 '' 变体配置缺少必要的 'appearance' 外观参数" -warning.config.block.state.variant.invalid_appearance: "在文件 中发现问题 - 方块 '' 的 '' 变体引用了不存在的外观配置 ''" -warning.config.block.state.invalid_state: "在文件 中发现问题 - 方块 '' 使用了无效的原版方块状态 ''" -warning.config.block.state.unavailable_state: "在文件 中发现问题 - 方块 '' 使用了不可用的原版方块状态 ''" -warning.config.block.state.invalid_vanilla_state_id: "在文件 中发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'" -warning.config.block.state.conflict: "在文件 中发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用" -warning.config.block.state.bind_real_state: "在文件 中发现问题 - 方块 '' 未能绑定真实方块状态 '',该状态已被 '' 占用" -warning.config.block.state.invalid_property_structure: "在文件 中发现问题 - 方块 '' 的属性结构 '' 配置无效" -warning.config.block.state.invalid_property: "在文件 中发现问题 - 无法为方块 '' 创建属性 '':" -warning.config.block.state.no_model_set: "在文件 中发现问题 - 方块 '' 缺少必要的 'model' 或 'models' 模型参数" -warning.config.block.state.invalid_real_state_id: "在文件 中发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~'。若槽位已用尽,请考虑在 additional-real-blocks.yml 中添加更多真实状态" -warning.config.block.state.model.lack_path: "在文件 中发现问题 - 方块 '' 的 'model' 配置缺少必要的 'path' 路径参数" -warning.config.block.state.model.invalid_resource_location: "在文件 中发现问题 - 方块 '' 的 'path' 路径参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.model.generation.conflict: "在文件 中发现问题 - 无法为 '' 生成模型,多个配置尝试用相同路径 '' 生成不同的JSON模型" -warning.config.model.generation.texture.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的 '' 纹理参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.emoji.lack_keywords: "在文件 中发现问题 - 表情 '' 缺少必要的 'keywords' 配置" -warning.config.emoji.duplicated: "在文件 中发现问题 - 表情 '' 重复定义" -warning.config.emoji.invalid_image: "在文件 中发现问题 - 表情 '' 使用了无效的 'image' 图片参数 ''." -warning.config.advancement.duplicated: "在文件 中发现问题 - 进度 '' 重复定义" \ No newline at end of file +warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" +warning.config.type.int: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为整数类型 (选项 '')" +warning.config.type.float: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为浮点数类型 (选项 '')" +warning.config.type.double: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度类型 (选项 '')" +warning.config.type.quaternionf: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为四元数类型 (选项 '')" +warning.config.type.vector3f: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为三维向量类型 (选项 '')" +warning.config.structure.not_section: "在文件 发现问题 - 配置项 '' 应为配置段落 但实际类型为 ''" +warning.config.image.duplicate: "在文件 发现问题 - 重复的图片配置 '' 请检查其他文件中是否存在相同配置" +warning.config.image.missing_height: "在文件 发现问题 - 图片 '' 缺少必需的 'height' 参数" +warning.config.image.height_ascent_conflict: "在文件 发现问题 - 图片 '' 违反位图规则: 'height' 参数 '' 必须不小于 'ascent' 参数 ''" +warning.config.image.missing_file: "在文件 发现问题 - 图片 '' 缺少必需的 'file' 参数" +warning.config.image.invalid_file_chars: "在文件 发现问题 - 图片 '' 的 'file' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.image.invalid_font_chars: "在文件 发现问题 - 图片 '' 的 'font' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.image.missing_char: "在文件 发现问题 - 图片 '' 缺少必需的 'char' 参数" +warning.config.image.codepoint_conflict: "在文件 发现问题 - 图片 '' 在字体 中使用的字符 '()' 已被其他图片 '' 占用" +warning.config.image.invalid_codepoint_grid: "在文件 发现问题 - 图片 '' 的 'chars' 码位网格无效" +warning.config.image.file_not_found: "在文件 发现问题 - 图片 '' 的 PNG 文件 '' 未找到" +warning.config.image.invalid_hex_value: "在文件 发现问题 - 图片 '' 使用的 Unicode 字符 '' 不是有效的十六进制值" +warning.config.recipe.duplicate: "在文件 发现问题 - 重复的配方 '' 请检查其他文件中是否存在相同配置" +warning.config.recipe.missing_type: "在文件 发现问题 - 配方 '' 缺少必需的 'type' 参数" +warning.config.recipe.invalid_type: "在文件 发现问题 - 配方 '' 使用了无效的配方类型 ''" +warning.config.recipe.invalid_item: "在文件 发现问题 - 配方 '' 使用了无效的物品 ''" +warning.config.recipe.missing_ingredient: "在文件 发现问题 - 烧炼配方 '' 缺少必需的 'ingredient' 参数" +warning.config.recipe.missing_result: "在文件 发现问题 - 配方 '' 缺少必需的 'result' 参数" +warning.config.recipe.result.missing_id: "在文件 发现问题 - 配方 '' 的结果缺少必需的 'id' 参数" +warning.config.recipe.crafting.invalid_category: "在文件 发现问题 - 合成配方 '' 使用了无效的分类 '' 允许的分类: []" +warning.config.recipe.cooking.invalid_category: "在文件 发现问题 - 烧炼配方 '' 使用了无效的分类 '' 允许的分类: []" +warning.config.recipe.shaped.missing_pattern: "在文件 发现问题 - 有序合成配方 '' 缺少必需的 'pattern' 参数" +warning.config.recipe.shaped.invalid_pattern: "在文件 发现问题 - 有序合成配方 '' 使用了无效的图案 ''" +warning.config.recipe.shaped.invalid_symbol: "在文件 发现问题 - 有序合成配方 '' 在图案中使用了无效符号 ''" +warning.config.recipe.smithing_transform.post_processor.missing_type: "在文件 发现问题 - 锻造升级配方 '' 的后处理器缺少必需的 'type' 参数" +warning.config.recipe.smithing_transform.post_processor.invalid_type: "在文件 发现问题 - 锻造升级配方 '' 使用了无效的后处理器类型 ''" +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_components' 后处理器缺少必需的 'components' 参数" +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_tags' 后处理器缺少必需的 'tags' 参数" +warning.config.i18n.unknown_locale: "在文件 发现问题 - 未知的语言环境 ''" +warning.config.template.duplicate: "在文件 发现问题 - 重复的模板 '' 请检查其他文件中是否存在相同配置" +warning.config.template.argument.self_increase_int.invalid_range: "在文件 发现问题 - 模板 '' 在 'self_increase_int' 参数中使用了一个起始值 '' 大于终止值 ''" +warning.config.template.argument.list.invalid_type: "在文件 发现问题 - 模板 '' 的 'list' 参数需要列表类型 但输入参数类型为 ''" +warning.config.vanilla_loot.missing_type: "在文件 发现问题 - 原版战利品 '' 缺少必需的 'type' 参数" +warning.config.vanilla_loot.invalid_type: "在文件 发现问题 - 原版战利品 '' 使用了无效类型 '' 允许的类型: []" +warning.config.vanilla_loot.block.invalid_target: "在文件 发现问题 - 原版战利品 '' 中存在无效的方块目标 ''" +warning.config.sound.duplicate: "在文件 发现问题 - 重复的音效 '' 请检查其他文件中是否存在相同配置" +warning.config.sound.missing_sounds: "在文件 发现问题 - 音效 '' 缺少必需的 'sounds' 参数" +warning.config.sound.missing_name: "在文件 发现问题 - 音效 '' 缺少必需的 'name' 参数" +warning.config.jukebox_song.duplicate: "在文件 发现问题 - 重复的唱片机歌曲 '' 请检查其他文件中是否存在相同配置" +warning.config.jukebox_song.missing_sound: "在文件 发现问题 - 唱片机歌曲 '' 缺少必需的 'sound' 参数" +warning.config.furniture.duplicate: "在文件 发现问题 - 重复的家具 '' 请检查其他文件中是否存在相同配置" +warning.config.furniture.missing_placement: "在文件 发现问题 - 家具 '' 缺少必需的 'placement' 参数" +warning.config.furniture.element.missing_item: "在文件 发现问题 - 家具 '' 的某个元素缺少必需的 'item' 参数" +warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" +warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" +warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" +warning.config.item.duplicate: "在文件 发现问题 - 重复的物品 '' 请检查其他文件中是否存在相同配置" +warning.config.item.settings.unknown: "在文件 发现问题 - 物品 '' 使用了未知的设置类型 ''" +warning.config.item.missing_material: "在文件 发现问题 - 物品 '' 缺少必需的 'material' 参数" +warning.config.item.invalid_material: "在文件 发现问题 - 物品 '' 使用了无效的材料类型 ''" +warning.config.item.bad_custom_model_data: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 数值过大 建议使用小于 16,777,216 的值" +warning.config.item.custom_model_data_conflict: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 已被物品 '' 占用" +warning.config.item.missing_model_id: "在文件 发现问题 - 物品 '' 缺少必需的 'custom-model-data' 或 'item-model' 参数" +warning.config.item.behavior.missing_type: "在文件 发现问题 - 物品 '' 的行为配置缺少必需的 'type' 参数" +warning.config.item.behavior.invalid_type: "在文件 发现问题 - 物品 '' 使用了无效的行为类型 ''" +warning.config.item.behavior.block.missing_block: "在文件 发现问题 - 物品 '' 的 'block_item' 行为缺少必需的 'block' 参数" +warning.config.item.behavior.furniture.missing_furniture: "在文件 发现问题 - 物品 '' 的 'furniture_item' 行为缺少必需的 'furniture' 参数" +warning.config.item.behavior.liquid_collision.missing_block: "在文件 发现问题 - 物品 '' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数" +warning.config.item.model.invalid_type: "在文件 发现问题 - 物品 '' 使用了无效的模型类型 ''" +warning.config.item.model.tint.missing_type: "在文件 发现问题 - 物品 '' 的染色配置缺少必需的 'type' 参数" +warning.config.item.model.tint.invalid_type: "在文件 发现问题 - 物品 '' 使用了无效的染色类型 ''" +warning.config.item.model.tint.constant.missing_value: "在文件 发现问题 - 物品 '' 的固定染色配置缺少必需的 'value' 参数" +warning.config.item.model.tint.grass.invalid_temp: "在文件 发现问题 - 物品 '' 的草地染色使用了无效的温度值 '' 有效范围应为 0 至 1" +warning.config.item.model.tint.grass.invalid_downfall: "在文件 发现问题 - 物品 '' 的草地染色使用了无效的降水值 '' 有效范围应为 0 至 1" +warning.config.item.model.tint.invalid_value: "在文件 发现问题 - 物品 '' 使用了无效的染色值 ''" +warning.config.item.model.base.missing_path: "在文件 发现问题 - 物品 '' 的 'minecraft:model' 模型缺少必需的 'path' 参数" +warning.config.item.model.base.invalid_path: "在文件 发现问题 - 物品 '' 的 'minecraft:model' 模型路径 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.item.model.condition.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'property' 参数" +warning.config.item.model.condition.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型使用了无效属性 ''" +warning.config.item.model.condition.missing_on_true: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'on-true' 参数" +warning.config.item.model.condition.missing_on_false: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'on-false' 参数" +warning.config.item.model.condition.keybind.missing: "在文件 发现问题 - 物品 '' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数" +warning.config.item.model.condition.has_component.missing_component: "在文件 发现问题 - 物品 '' 的 'minecraft:has_component' 属性缺少必需的 'component' 参数" +warning.config.item.model.composite.missing_models: "在文件 发现问题 - 物品 '' 的 'minecraft:composite' 模型缺少必需的 'models' 参数" +warning.config.item.model.range_dispatch.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型缺少必需的 'property' 参数" +warning.config.item.model.range_dispatch.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型使用了无效属性 ''" +warning.config.item.model.range_dispatch.missing_entries: "在文件 发现问题 - 物品 '' 的 'minecraft:composite' 模型缺少必需的 'entries' 参数" +warning.config.item.model.range_dispatch.entry.missing_model: "在文件 发现问题 - 物品 '' 的 'minecraft:composite' 模型的某个条目缺少必需的 'model' 参数" +warning.config.item.model.range_dispatch.compass.missing_target: "在文件 发现问题 - 物品 '' 的 'minecraft:compass' 属性缺少必需的 'target' 参数" +warning.config.item.model.range_dispatch.time.missing_source: "在文件 发现问题 - 物品 '' 的 'minecraft:time' 属性缺少必需的 'source' 参数" +warning.config.item.model.select.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型缺少必需的 'property' 参数" +warning.config.item.model.select.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型使用了无效属性 ''" +warning.config.item.model.select.missing_cases: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型缺少必需的 'cases' 参数" +warning.config.item.model.select.case.missing_when: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型的某个 case 缺少必需的 'when' 参数" +warning.config.item.model.select.case.missing_model: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型的某个 case 缺少必需的 'model' 参数" +warning.config.item.model.select.block_state.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:block_state' 属性缺少必需的 'block-state-property' 参数" +warning.config.item.model.select.local_time.missing_pattern: "在文件 发现问题 - 物品 '' 的 'minecraft:local_time' 属性缺少必需的 'pattern' 参数" +warning.config.item.model.special.missing_type: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型缺少必需的 'type' 参数" +warning.config.item.model.special.invalid_type: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型使用了无效类型 ''" +warning.config.item.model.special.banner.missing_color: "在文件 发现问题 - 物品 '' 的 'minecraft:banner' 特殊模型缺少必需的 'color' 参数" +warning.config.item.model.special.bed.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:bed' 特殊模型缺少必需的 'texture' 参数" +warning.config.item.model.special.sign.missing_wood_type: "在文件 发现问题 - 物品 '' 的 'minecraft:hanging_sign'/'minecraft:standing_sign' 特殊模型缺少必需的 'wood-type' 参数" +warning.config.item.model.special.sign.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:hanging_sign'/'minecraft:standing_sign' 特殊模型缺少必需的 'texture' 参数" +warning.config.item.model.special.chest.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:chest' 特殊模型缺少必需的 'texture' 参数" +warning.config.item.model.special.chest.invalid_openness: "在文件 发现问题 - 物品 '' 的 'minecraft:chest' 特殊模型使用了无效的 'openness' 值 '' 有效范围应为 0~1" +warning.config.item.model.special.shulker_box.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:shulker_box' 特殊模型缺少必需的 'texture' 参数" +warning.config.item.model.special.shulker_box.invalid_openness: "在文件 发现问题 - 物品 '' 的 'minecraft:shulker_box' 特殊模型使用了无效的 'openness' 值 '' 有效范围应为 0~1" +warning.config.item.model.special.head.missing_kind: "在文件 发现问题 - 物品 '' 的 'minecraft:head' 特殊模型缺少必需的 'kind' 参数" +warning.config.item.model.special.head.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:head' 特殊模型缺少必需的 'texture' 参数" +warning.config.block.duplicate: "在文件 发现问题 - 重复的方块 '' 请检查其他文件中是否存在相同配置" +warning.config.block.missing_state: "在文件 发现问题 - 方块 '' 缺少必需的 'state' 参数" +warning.config.block.state.property.missing_type: "在文件 发现问题 - 方块 '' 的属性 '' 缺少必需的 'type' 参数" +warning.config.block.state.property.invalid_type: "在文件 发现问题 - 方块 '' 的属性 '' 使用了无效的类型参数 ''" +warning.config.block.state.property.integer.invalid_range: "在文件 发现问题 - 方块 '' 的整数属性 '' 使用了无效的范围参数 '' 正确语法: 1~2" +warning.config.block.state.property.invalid_format: "在文件 发现问题 - 方块 '' 使用了无效的方块状态属性格式 ''." +warning.config.block.state.missing_real_id: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'id' 参数 该 ID 是服务端方块 ID 用于唯一标识每种方块状态类型" +warning.config.block.state.missing_state: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'state' 参数" +warning.config.block.state.missing_properties: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'properties' 段落" +warning.config.block.state.missing_appearances: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'appearances' 段落" +warning.config.block.state.missing_variants: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'variants' 段落" +warning.config.block.state.variant.missing_appearance: "在文件 发现问题 - 方块 '' 的变体 '' 缺少必需的 'appearance' 参数" +warning.config.block.state.variant.invalid_appearance: "在文件 发现问题 - 方块 '' 的变体 '' 使用了不存在的 appearance ''" +warning.config.block.state.invalid_vanilla: "在文件 发现问题 - 方块 '' 使用了无效的原版方块状态 ''" +warning.config.block.state.unavailable_vanilla: "在文件 发现问题 - 方块 '' 使用了不可用的原版方块状态 '' 请在 mappings.yml 中释放该状态" +warning.config.block.state.invalid_vanilla_id: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'" +warning.config.block.state.conflict: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用" +warning.config.block.state.bind_failed: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 因该状态已被 '' 占用" +warning.config.block.state.missing_model: "在文件 发现问题 - 方块 '' 缺少必需的 'model' 或 'models' 参数" +warning.config.block.state.invalid_real_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态" +warning.config.block.state.model.missing_path: "在文件 发现问题 - 方块 '' 的 'model' 缺少必需的 'path' 选项" +warning.config.block.state.model.invalid_path: "在文件 发现问题 - 方块 '' 的 'path' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.block.settings.unknown: "在文件 发现问题 - 方块 '' 使用了未知的设置类型 ''" +warning.config.block.behavior.missing_type: "在文件 发现问题 - 方块 '' 的行为配置缺少必需的 'type' 参数" +warning.config.block.behavior.invalid_type: "在文件 发现问题 - 方块 '' 使用了无效的行为类型 ''" +warning.config.block.behavior.concrete.missing_solid: "在文件 发现问题 - 方块 '' 的 'concrete_block' 行为缺少必需的 'solid-block' 选项" +warning.config.block.behavior.crop.missing_age: "在文件 发现问题 - 方块 '' 的 'crop_block' 行为缺少必需的 'age' 属性" +warning.config.block.behavior.sugar_cane.missing_age: "在文件 发现问题 - 方块 '' 的 'sugar_cane_block' 行为缺少必需的 'age' 属性" +warning.config.block.behavior.leaves.missing_persistent: "在文件 发现问题 - 方块 '' 的 'leaves_block' 行为缺少必需的 'persistent' 属性" +warning.config.block.behavior.leaves.missing_distance: "在文件 发现问题 - 方块 '' 的 'leaves_block' 行为缺少必需的 'distance' 属性" +warning.config.block.behavior.sapling.missing_stage: "在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'stage' 属性" +warning.config.block.behavior.sapling.missing_feature: "在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'feature' 参数" +warning.config.block.behavior.strippable.missing_stripped: "在文件 发现问题 - 方块 '' 的 'strippable_block' 行为缺少必需的 'stripped' 参数" +warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" +warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" +warning.config.model.generation.texture.invalid: "在文件 发现问题 - 配置项 '' 的纹理 '' 路径 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.model.generation.parent.invalid: "在文件 发现问题 - 配置项 '' 的父级参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.emoji.missing_keywords: "在文件 发现问题 - 表情 '' 缺少必需的 'keywords' 参数" +warning.config.emoji.duplicate: "在文件 发现问题 - 重复的表情 '' 请检查其他文件中是否存在相同配置" +warning.config.emoji.invalid_image: "在文件 发现问题 - 表情 '' 的 'image' 参数 '' 无效" +warning.config.advancement.duplicate: "在文件 发现问题 - 重复的进度 '' 请检查其他文件中是否存在相同配置" +warning.config.loot_table.missing_pools: "在文件 发现问题 - '' 的战利品表配置错误 缺少必需的 'pools' 参数" +warning.config.loot_table.invalid_pools_type: "在文件 发现问题 - '' 的战利品表配置错误 'pools' 应为字符串/映射列表 当前类型: ''" +warning.config.loot_table.invalid_conditions_type: "在文件 发现问题 - '' 的战利品表配置错误 'conditions' 应为映射列表 当前类型: ''" +warning.config.loot_table.invalid_functions_type: "在文件 发现问题 - '' 的战利品表配置错误 'functions' 应为映射列表 当前类型: ''" +warning.config.loot_table.invalid_entries_type: "在文件 发现问题 - '' 的战利品表配置错误 'entries' 应为映射列表 当前类型: ''" +warning.config.loot_table.function.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个函数缺少必需的 'type' 参数" +warning.config.loot_table.function.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个函数使用了无效的函数类型 ''" +warning.config.loot_table.function.apply_bonus.missing_enchantment: "在文件 发现问题 - '' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'enchantment' 参数" +warning.config.loot_table.function.apply_bonus.missing_formula: "在文件 发现问题 - '' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'formula' 参数" +warning.config.loot_table.function.drop_exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'drop_exp' 函数缺少必需的 'count' 参数" +warning.config.loot_table.function.set_count.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'set_count' 函数缺少必需的 'count' 参数" +warning.config.loot_table.entry.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目缺少必需的 'type' 参数" +warning.config.loot_table.entry.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目使用了无效的条目类型 ''" +warning.config.loot_table.entry.exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'exp' 条目缺少必需的 'count' 参数" +warning.config.loot_table.entry.item.missing_item: "在文件 发现问题 - '' 的战利品表配置错误 'item' 条目缺少必需的 'item' 参数" +warning.config.loot_table.condition.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条件缺少必需的 'type' 参数" +warning.config.loot_table.condition.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条件使用了无效的条件类型 ''" +warning.config.loot_table.condition.table_bonus.missing_enchantment: "在文件 发现问题 - '' 的战利品表配置错误 'table_bonus' 条件缺少必需的 'enchantment' 参数" +warning.config.loot_table.condition.table_bonus.missing_chances: "在文件 发现问题 - '' 的战利品表配置错误 'table_bonus' 条件缺少必需的 'chances' 参数" +warning.config.loot_table.number.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个数值缺少必需的 'type' 参数" +warning.config.loot_table.number.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个数值使用了无效的数值类型 ''" +warning.config.host.missing_type: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 缺少必需的 'type' 参数" +warning.config.host.invalid_type: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 无效的托管类型 '' 请参考 https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host" +warning.config.host.external.missing_url: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 外部托管缺少必需的 'url' 参数" +warning.config.host.alist.missing_api_url: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'api-url' 参数" +warning.config.host.alist.missing_username: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'username' 参数或环境变量 'CE_ALIST_USERNAME'" +warning.config.host.alist.missing_password: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'password' 参数或环境变量 'CE_ALIST_PASSWORD'" +warning.config.host.alist.missing_upload_path: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'upload-path' 参数" +warning.config.host.dropbox.missing_app_key: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'app-key' 参数或环境变量 'CE_DROPBOX_APP_KEY'" +warning.config.host.dropbox.missing_app_secret: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'app-secret' 参数或环境变量 'CE_DROPBOX_APP_SECRET'" +warning.config.host.dropbox.missing_refresh_token: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'refresh-token' 参数或环境变量 'CE_DROPBOX_REFRESH_TOKEN'" +warning.config.host.dropbox.missing_upload_path: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'upload-path' 参数" +warning.config.host.lobfile.missing_api_key: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Lobfile 托管缺少必需的 'api-key' 参数" +warning.config.host.onedrive.missing_client_id: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'client-id' 参数或环境变量 'CE_ONEDRIVE_CLIENT_ID'" +warning.config.host.onedrive.missing_client_secret: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'client-secret' 参数或环境变量 'CE_ONEDRIVE_CLIENT_SECRET'" +warning.config.host.onedrive.missing_refresh_token: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'refresh-token' 参数或环境变量 'CE_ONEDRIVE_REFRESH_TOKEN'" +warning.config.host.onedrive.missing_upload_path: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'upload-path' 参数" +warning.config.host.s3.missing_endpoint: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'endpoint' 参数" +warning.config.host.s3.missing_bucket: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'bucket' 参数" +warning.config.host.s3.missing_access_key: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'access-key-id' 参数或环境变量 'CE_S3_ACCESS_KEY_ID'" +warning.config.host.s3.missing_secret: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'access-key-secret' 参数或环境变量 'CE_S3_ACCESS_KEY_SECRET'" +warning.config.host.s3.missing_upload_path: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'upload-path' 参数" +warning.config.host.self.missing_ip: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 自托管托管缺少必需的 'ip' 参数" +warning.config.host.self.invalid_port: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 自托管托管的端口 '' 无效" +warning.config.host.gitlab.missing_url: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'gitlab-url' 参数" +warning.config.host.gitlab.missing_token: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'access-token' 参数" +warning.config.host.gitlab.missing_project: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'project-id' 参数" +warning.config.host.proxy.missing_host: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'host' 参数" +warning.config.host.proxy.missing_port: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'port' 参数" +warning.config.host.proxy.missing_scheme: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'scheme' 参数" +warning.config.host.proxy.invalid: "在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 无效的代理配置 ''" +warning.config.conflict_matcher.missing_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个匹配器缺少必需的 'type' 参数" +warning.config.conflict_matcher.invalid_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个匹配器使用了无效类型 ''" +warning.config.conflict_matcher.exact.missing_path: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 精确路径匹配器缺少必需的 'path' 参数" +warning.config.conflict_matcher.contains.missing_path: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 路径包含匹配器缺少必需的 'path' 参数" +warning.config.conflict_matcher.filename.missing_name: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件名匹配器缺少必需的 'path' 参数" +warning.config.conflict_matcher.pattern.missing_pattern: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 正则表达式匹配器缺少必需的 'pattern' 参数" +warning.config.conflict_matcher.parent_prefix.missing_prefix: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 父路径前缀匹配器缺少必需的 'prefix' 参数" +warning.config.conflict_matcher.parent_suffix.missing_suffix: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 父路径后缀匹配器缺少必需的 'suffix' 参数" +warning.config.conflict_matcher.inverted.missing_term: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 反向匹配器缺少必需的 'term' 参数" +warning.config.conflict_matcher.all_of.missing_terms: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 全匹配器缺少必需的 'terms' 参数" +warning.config.conflict_matcher.any_of.missing_terms: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 任一匹配器缺少必需的 'terms' 参数" +warning.config.conflict_resolution.missing_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案缺少必需的 'type' 参数" +warning.config.conflict_resolution.invalid_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 ''" \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java index c1ed78270..74e2a5916 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager; 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; @@ -51,8 +51,7 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (advancements.containsKey(id)) { - TranslationManager.instance().log("warning.config.advancement.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.advancement.duplicate", path, id); } JsonElement jsonTree = GsonHelper.get().toJsonTree(section); FastNMS.INSTANCE.registerAdvancement(id.decompose(), jsonTree); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 807b57514..74e9e5019 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -26,6 +26,7 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; 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.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -139,8 +140,8 @@ public class BukkitBlockManager extends AbstractBlockManager { this.appearanceToRealState.clear(); this.blockStateOverrides.clear(); this.modBlockStates.clear(); - if (EmptyBlock.INSTANCE != null) - Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState()); + if (EmptyBlock.STATE != null) + Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE); } @Override @@ -168,7 +169,6 @@ public class BukkitBlockManager extends AbstractBlockManager { this.tempVanillaBlockStateModels.clear(); } - @Nullable public Object getMinecraftBlockHolder(int stateId) { return stateId2BlockHolder.get(stateId); @@ -308,7 +308,7 @@ public class BukkitBlockManager extends AbstractBlockManager { } } - this.soundMapper = soundMapperBuilder.build(); + this.soundMapper = soundMapperBuilder.buildKeepingLast(); } catch (Throwable e) { plugin.logger().warn("Failed to inject blocks.", e); } @@ -331,77 +331,68 @@ public class BukkitBlockManager extends AbstractBlockManager { public void parseSection(Pack pack, Path path, Key id, Map section) { // check duplicated config if (byId.containsKey(id)) { - TranslationManager.instance().log("warning.config.block.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.duplicate"); } // read block settings BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false)); + // read loot table LootTable lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false)); + // read states Map> properties; Map appearances; Map variants; - Map stateSection = MiscUtils.castToMap(section.get("state"), true); - if (stateSection != null) { + Object stateObj = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"); + Map stateSection = MiscUtils.castToMap(stateObj, true); + + // single state + if (!stateSection.containsKey("properties")) { properties = Map.of(); - int internalId = MiscUtils.getAsInt(stateSection.getOrDefault("id", -1)); + int internalId = ResourceConfigUtils.getAsInt(stateSection.getOrDefault("id", -1), "id"); if (internalId < 0) { - TranslationManager.instance().log("warning.config.block.state.lack_real_id", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.missing_real_id"); } - Pair pair = parseAppearanceSection(pack, path, id, stateSection); + Pair pair = parseAppearanceSection(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)); + String internalBlock = pair.left().value() + "_" + internalId; + Key internalBlockId = Key.of(CraftEngine.NAMESPACE, internalBlock); + int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-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; + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", + internalBlock, + String.valueOf(registeredRealBlockSlots.get(pair.left()) - 1)); } variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId)); } else { - // states - Map statesSection = MiscUtils.castToMap(section.get("states"), true); - if (statesSection == null) { - TranslationManager.instance().log("warning.config.block.lack_state", path.toString(), id.toString()); - return; - } // properties - Map propertySection = MiscUtils.castToMap(statesSection.get("properties"), true); + Map propertySection = MiscUtils.castToMap(stateSection.get("properties"), true); if (propertySection == null) { - TranslationManager.instance().log("warning.config.block.state.lack_properties", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.missing_properties"); } - properties = parseProperties(path, id, propertySection); + properties = parseProperties(propertySection); // appearance - Map appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true); + Map appearancesSection = MiscUtils.castToMap(stateSection.get("appearances"), true); if (appearancesSection == null) { - TranslationManager.instance().log("warning.config.block.state.lack_appearances", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.missing_appearances"); } appearances = new HashMap<>(); Map tempTypeMap = new HashMap<>(); for (Map.Entry appearanceEntry : appearancesSection.entrySet()) { if (appearanceEntry.getValue() instanceof Map appearanceSection) { - Pair pair = parseAppearanceSection(pack, path, id, MiscUtils.castToMap(appearanceSection, false)); + Pair pair = parseAppearanceSection(id, MiscUtils.castToMap(appearanceSection, false)); if (pair == null) return; appearances.put(appearanceEntry.getKey(), pair.right()); tempTypeMap.put(appearanceEntry.getKey(), pair.left()); } } // variants - Map variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true); + Map variantsSection = MiscUtils.castToMap(stateSection.get("variants"), true); if (variantsSection == null) { - TranslationManager.instance().log("warning.config.block.state.lack_variants", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.missing_variants"); } variants = new HashMap<>(); for (Map.Entry variantEntry : variantsSection.entrySet()) { @@ -410,25 +401,19 @@ public class BukkitBlockManager extends AbstractBlockManager { 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; + throw new LocalizedResourceConfigException("warning.config.block.state.variant.missing_appearance", variantName); } if (!appearances.containsKey(appearance)) { - TranslationManager.instance().log("warning.config.block.state.variant.invalid_appearance", path.toString(), id.toString(), variantName, appearance); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantName, appearance); } - int internalId = MiscUtils.getAsInt(variantSection.getOrDefault("id", -1)); + int internalId = ResourceConfigUtils.getAsInt(variantSection.getOrDefault("id", -1), "id"); Key baseBlock = tempTypeMap.get(appearance); Key internalBlockId = Key.of(CraftEngine.NAMESPACE, baseBlock.value() + "_" + internalId); - int internalBlockRegistryId = MiscUtils.getAsInt(internalId2StateId.getOrDefault(internalBlockId, -1)); + int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1); if (internalBlockRegistryId == -1) { - TranslationManager.instance().log("warning.config.block.state.invalid_real_state_id", - path.toString(), - id.toString(), + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), - String.valueOf(MiscUtils.getAsInt(registeredRealBlockSlots.getOrDefault(baseBlock, 1)) - 1) - ); - return; + String.valueOf(registeredRealBlockSlots.getOrDefault(baseBlock, 1) - 1)); } Map anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true); variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId)); @@ -436,18 +421,21 @@ public class BukkitBlockManager extends AbstractBlockManager { } } - // create or get block holder - Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() -> - ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id))); - // create block - Map behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false); - BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable); + Map behaviors = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false); + CustomBlock block = BukkitCustomBlock.builder(id) + .appearances(appearances) + .variantMapper(variants) + .lootTable(lootTable) + .properties(properties) + .settings(settings) + .behavior(behaviors) + .build(); // bind appearance and real state for (ImmutableBlockState state : block.variantProvider().states()) { 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()); + TranslationManager.instance().log("warning.config.block.state.bind_failed", path.toString(), id.toString(), state.toString(), previous.toString()); continue; } stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state; @@ -456,6 +444,7 @@ public class BukkitBlockManager extends AbstractBlockManager { } byId.put(id, block); + // generate mod assets if (Config.generateModAssets()) { for (ImmutableBlockState state : block.variantProvider().states()) { @@ -466,67 +455,48 @@ public class BukkitBlockManager extends AbstractBlockManager { } } - private Map> parseProperties(Path path, Key id, Map propertiesSection) { + private Map> parseProperties(Map propertiesSection) { Map> properties = new HashMap<>(); for (Map.Entry entry : propertiesSection.entrySet()) { - if (entry.getValue() instanceof Map params) { - 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 { - TranslationManager.instance().log("warning.config.block.state.invalid_property_structure", path.toString(), id.toString(), entry.getKey()); - } + Property property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(entry.getValue(), false)); + properties.put(entry.getKey(), property); } return properties; } @Nullable - private Pair parseAppearanceSection(Pack pack, Path path, Key id, Map section) { + private Pair parseAppearanceSection(Key id, Map section) { // require state non null - String vanillaStateString = (String) section.get("state"); + Object vanillaStateString = section.get("state"); if (vanillaStateString == null) { - TranslationManager.instance().log("warning.config.block.state.lack_state", path.toString(), id.toString()); - return null; + throw new LocalizedResourceConfigException("warning.config.block.state.missing_state"); } // 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; - } + int vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString.toString()); // check conflict Key ifAny = this.tempRegistryIdConflictMap.get(vanillaStateRegistryId); if (ifAny != null && !ifAny.equals(id)) { - TranslationManager.instance().log("warning.config.block.state.conflict", path.toString(), id.toString(), BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaStateRegistryId)).getAsString(), ifAny.toString()); - return null; + throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaStateRegistryId)).getAsString(), ifAny.toString()); } // require models not to be null - Object models = section.getOrDefault("models", section.get("model")); + Object models = section.get("models"); if (models == null) { - TranslationManager.instance().log("warning.config.block.state.no_model_set", path.toString(), id.toString()); - return null; + models = section.get("model"); + } + if (models == null) { + throw new LocalizedResourceConfigException("warning.config.block.state.missing_model"); } List variants = new ArrayList<>(); if (models instanceof Map singleModelSection) { - loadVariantModel(pack, path, id, variants, MiscUtils.castToMap(singleModelSection, false)); + loadVariantModel(variants, MiscUtils.castToMap(singleModelSection, false)); } else if (models instanceof List modelList) { for (Object model : modelList) { if (model instanceof Map singleModelMap) { - loadVariantModel(pack, path, id, variants, MiscUtils.castToMap(singleModelMap, false)); + loadVariantModel(variants, MiscUtils.castToMap(singleModelMap, false)); } } } @@ -551,73 +521,68 @@ public class BukkitBlockManager extends AbstractBlockManager { return Pair.of(block, vanillaStateRegistryId); } - private void loadVariantModel(Pack pack, Path path, Key id, List variants, Map singleModelMap) { + private void loadVariantModel(List variants, Map singleModelMap) { JsonObject json = new JsonObject(); String modelPath = (String) singleModelMap.get("path"); if (modelPath == null) { - TranslationManager.instance().log("warning.config.block.state.model.lack_path", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.model.missing_path"); } if (!ResourceLocation.isValid(modelPath)) { - TranslationManager.instance().log("warning.config.block.state.model.invalid_resource_location", path.toString(), id.toString(), modelPath); - return; + throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath); } 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"))); + if (singleModelMap.containsKey("x")) json.addProperty("x", ResourceConfigUtils.getAsInt(singleModelMap.get("x"), "x")); + if (singleModelMap.containsKey("y")) json.addProperty("y", ResourceConfigUtils.getAsInt(singleModelMap.get("y"), "y")); if (singleModelMap.containsKey("uvlock")) json.addProperty("uvlock", (boolean) singleModelMap.get("uvlock")); - if (singleModelMap.containsKey("weight")) json.addProperty("weight", MiscUtils.getAsInt(singleModelMap.get("weight"))); + if (singleModelMap.containsKey("weight")) json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight")); Map generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true); if (generationMap != null) { - prepareModelGeneration(path, id, new ModelGeneration(Key.of(modelPath), generationMap)); + prepareModelGeneration(new ModelGeneration(Key.of(modelPath), generationMap)); } variants.add(json); } - private VanillaStateParseResult parseVanillaStateRegistryId(String blockState) { + private int parseVanillaStateRegistryId(String blockState) { String[] split = blockState.split(":", 3); if (split.length >= 4) { - return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); } int registryId; String stateOrId = split[split.length - 1]; boolean isId = !stateOrId.contains("[") && !stateOrId.contains("]"); if (isId) { - if (split.length == 1) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + if (split.length == 1) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", 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]); - if (id < 0) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + if (id < 0) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); + } List arranger = this.blockAppearanceArranger.get(block); - 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)}); + if (arranger == null) { + throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); + } + if (id >= arranger.size()) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla_id", blockState, String.valueOf(arranger.size() - 1)); + } registryId = arranger.get(id); } catch (NumberFormatException e) { - return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, 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}); + throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); } } catch (IllegalArgumentException e) { - return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState); } } - 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); - } + return registryId; } private void loadMappingsAndAdditionalBlocks() { @@ -837,9 +802,19 @@ public class BukkitBlockManager extends AbstractBlockManager { return states.get(0); } + @SuppressWarnings("unchecked") private void deceiveBukkit(Object newBlock, Key replacedBlock, boolean isNoteBlock) throws IllegalAccessException { - @SuppressWarnings("unchecked") Map magicMap = (Map) Reflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null); - magicMap.put(newBlock, isNoteBlock ? Material.STONE : org.bukkit.Registry.MATERIAL.get(new NamespacedKey(replacedBlock.namespace(), replacedBlock.value()))); + Map factories = (Map) Reflections.field$CraftBlockStates$FACTORIES.get(null); + if (isNoteBlock) { + magicMap.put(newBlock, Material.STONE); + } else { + Material material = org.bukkit.Registry.MATERIAL.get(new NamespacedKey(replacedBlock.namespace(), replacedBlock.value())); + if (Reflections.clazz$CraftBlockStates$BlockEntityStateFactory.isInstance(factories.get(material))) { + magicMap.put(newBlock, Material.STONE); + } else { + magicMap.put(newBlock, material); + } + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 2bb530a75..209e34662 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -9,8 +9,11 @@ import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; import net.momirealms.craftengine.core.util.Tristate; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.shared.ObjectHolder; @@ -25,17 +28,17 @@ import java.util.Set; public class BukkitCustomBlock extends CustomBlock { - public BukkitCustomBlock( + protected BukkitCustomBlock( Key id, Holder.Reference holder, Map> properties, Map appearances, Map variantMapper, BlockSettings settings, - Map behaviorSettings, + Map behavior, @Nullable LootTable lootTable ) { - super(id, holder, properties, appearances, variantMapper, settings, behaviorSettings, lootTable); + super(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable); } @SuppressWarnings("unchecked") @@ -139,4 +142,23 @@ public class BukkitCustomBlock extends CustomBlock { CraftEngine.instance().logger().warn("Failed to init block settings", e); } } + + public static Builder builder(Key id) { + return new Builder(id); + } + + public static class Builder extends CustomBlock.Builder { + + protected Builder(Key id) { + super(id); + } + + @Override + public CustomBlock build() { + // create or get block holder + Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() -> + ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id))); + return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index 6d8de9243..1ebf13dc8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -16,6 +16,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block"); public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block"); public static final Key CROP_BLOCK = Key.from("craftengine:crop_block"); + public static final Key GRASS_BLOCK = Key.from("craftengine:grass_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -30,5 +31,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY); register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY); register(CROP_BLOCK, CropBlockBehavior.FACTORY); + register(GRASS_BLOCK, GrassBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index 6f8cd8f8b..e15ae9e6f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -15,7 +15,7 @@ import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.block.BlockState; @@ -56,7 +56,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { } else { CraftEngine.instance().logger().warn("Failed to create solid block " + this.targetBlock + " in ConcretePowderBlockBehavior"); this.defaultBlockState = Reflections.instance$Blocks$STONE$defaultState; - this.defaultImmutableBlockState = EmptyBlock.INSTANCE.defaultState(); + this.defaultImmutableBlockState = EmptyBlock.STATE; } return this.defaultBlockState; } @@ -155,12 +155,9 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - float hurtAmount = MiscUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f)); - int hurtMax = MiscUtils.getAsInt(arguments.getOrDefault("max-hurt", -1)); - String solidBlock = (String) arguments.get("solid-block"); - if (solidBlock == null) { - throw new IllegalArgumentException("No `solid-block` specified for concrete powder block behavior"); - } + float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount"); + int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt"); + String solidBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("solid-block"), "warning.config.block.behavior.concrete.missing_solid"); return new ConcretePowderBlockBehavior(block, hurtAmount, hurtMax, Key.of(solidBlock)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index 1141f6815..6e18976af 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -16,8 +16,8 @@ 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.ResourceConfigUtils; import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.Vec3d; @@ -168,12 +168,9 @@ public class CropBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { Tuple, Set, Set> tuple = readTagsAndState(arguments, false); - Property ageProperty = (Property) block.getProperty("age"); - if (ageProperty == null) { - throw new IllegalArgumentException("age property not set for crop"); - } - int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("light-requirement", 9)); - float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.125f)); + Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.crop.missing_age"); + int minGrowLight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("light-requirement", 9), "light-requirement"); + float growSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.125f), "grow-speed"); boolean isBoneMealTarget = (boolean) arguments.getOrDefault("is-bone-meal-target", true); NumberProvider boneMealAgeBonus = NumberProviders.fromObject(arguments.getOrDefault("bone-meal-age-bonus", 1)); return new CropBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java index 07874400a..54b2d436a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java @@ -11,7 +11,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.parameter.LootParameters; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.Vec3d; @@ -129,8 +129,8 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - float hurtAmount = MiscUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f)); - int hurtMax = MiscUtils.getAsInt(arguments.getOrDefault("max-hurt", -1)); + float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount"); + int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt"); return new FallingBlockBehavior(block, hurtAmount, hurtMax); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java new file mode 100644 index 000000000..3b0c868bc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -0,0 +1,71 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.ParticleUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.shared.block.BlockBehavior; +import org.bukkit.World; + +import java.util.Map; + +public class GrassBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + + public GrassBlockBehavior(CustomBlock block) { + super(block); + } + + @Override + public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) { + return FastNMS.INSTANCE.method$GrassBlock$isValidBonemealTarget(args[0], args[1], args[2]); + } + + @Override + public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception { + if (!VersionHelper.isOrAbove1_20_2()) return true; + Object level = args[0]; + Object blockPos = args[2]; + Object blockState = args[3]; + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (immutableBlockState == null || immutableBlockState.isEmpty()) { + return false; + } + boolean sendParticles = false; + Object visualState = immutableBlockState.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); + if (!is) { + sendParticles = true; + } + } else { + sendParticles = true; + } + if (sendParticles) { + World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 1.5, z + 0.5, 20, 2, 0, 2); + } + return true; + } + + @Override + public void performBoneMeal(Object thisBlock, Object[] args) { + FastNMS.INSTANCE.method$GrassBlock$performBoneMeal(args[0], args[1], args[2], args[3], thisBlock); + } + + public static class Factory implements BlockBehaviorFactory { + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + return new GrassBlockBehavior(block); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index 9982700fa..4686b022c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.parameter.LootParameters; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.BlockPos; @@ -172,14 +173,8 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - Property persistent = (Property) block.getProperty("persistent"); - if (persistent == null) { - throw new NullPointerException("persistent property not set for block " + block.id()); - } - Property distance = (Property) block.getProperty("distance"); - if (distance == null) { - throw new NullPointerException("distance not set for block " + block.id()); - } + Property persistent = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("persistent"), "warning.config.block.behavior.leaves.missing_persistent"); + Property distance = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("distance"), "warning.config.block.behavior.leaves.missing_distance"); Property waterlogged = (Property) block.getProperty("waterlogged"); int actual = distance.possibleValues().get(distance.possibleValues().size() - 1); return new LeavesBlockBehavior(block, actual, distance, persistent, waterlogged); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 666114c6e..06d41970e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -11,8 +11,8 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Location; @@ -140,18 +140,12 @@ public class SaplingBlockBehavior extends BushBlockBehavior { @SuppressWarnings("unchecked") @Override public BlockBehavior create(CustomBlock block, Map arguments) { - String feature = (String) arguments.get("feature"); - if (feature == null) { - throw new IllegalArgumentException("feature is null"); - } - Property stageProperty = (Property) block.getProperty("stage"); - if (stageProperty == null) { - throw new IllegalArgumentException("stage property not set for sapling"); - } - double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45)); + String feature = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("feature"), "warning.config.block.behavior.sapling.missing_feature"); + Property stageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("stage"), "warning.config.block.behavior.sapling.missing_stage"); + double boneMealSuccessChance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45), "bone-meal-success-chance"); Tuple, Set, Set> tuple = readTagsAndState(arguments, false); return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance, - MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0))); + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0), "grow-speed")); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java index 6a5d4ee7f..f1a2478fe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.Map; @@ -24,10 +25,7 @@ public class StrippableBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - String stripped = (String) arguments.get("stripped"); - if (stripped == null) { - throw new IllegalArgumentException("stripped is null"); - } + String stripped = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("stripped"), "warning.config.block.behavior.strippable.missing_stripped"); return new StrippableBlockBehavior(block, Key.of(stripped)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java index 4a504d371..cf1b77194 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java @@ -14,10 +14,7 @@ import net.momirealms.craftengine.core.block.properties.IntegerProperty; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.Item; 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.util.VersionHelper; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; @@ -201,16 +198,13 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { Tuple, Set, Set> tuple = readTagsAndState(arguments, false); - Property ageProperty = (Property) block.getProperty("age"); - if (ageProperty == null) { - throw new IllegalArgumentException("age property not set for sugar cane"); - } - int maxHeight = MiscUtils.getAsInt(arguments.getOrDefault("max-height", 3)); + Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.sugar_cane.missing_age"); + int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 3), "max-height"); List nearbyLiquids = MiscUtils.getAsStringList(arguments.getOrDefault("required-adjacent-liquids", List.of())); boolean nearWater = nearbyLiquids.contains("water"); boolean nearLava = nearbyLiquids.contains("lava"); return new SugarCaneBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, maxHeight, nearWater, nearLava, - MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1))); + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1), "grow-speed")); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 29ee90605..faad973b1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -22,6 +22,7 @@ import org.bukkit.*; import org.bukkit.entity.*; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.joml.Vector3f; @@ -106,19 +107,18 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (byId.containsKey(id)) { - TranslationManager.instance().log("warning.config.furniture.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.furniture.duplicate", path, id); } Map lootMap = MiscUtils.castToMap(section.get("loot"), true); Map settingsMap = MiscUtils.castToMap(section.get("settings"), true); Map placementMap = MiscUtils.castToMap(section.get("placement"), true); - EnumMap placements = new EnumMap<>(AnchorType.class); if (placementMap == null) { - TranslationManager.instance().log("warning.config.furniture.lack_placement", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement", path, id); } + EnumMap placements = new EnumMap<>(AnchorType.class); + for (Map.Entry entry : placementMap.entrySet()) { // anchor type AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); @@ -130,16 +130,15 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { for (Map 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; + throw new LocalizedResourceConfigException("warning.config.furniture.element.missing_item", path, id); } 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")) + MiscUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale"), + MiscUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation"), + MiscUtils.getAsVector3f(element.getOrDefault("position", "0"), "position"), + MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation") ); elements.add(furnitureElement); } @@ -192,13 +191,12 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } } - CustomFurniture furniture = new CustomFurniture( - id, - FurnitureSettings.fromMap(settingsMap), - placements, - lootMap == null ? null : LootTable.fromMap(lootMap) - ); + // get furniture settings + FurnitureSettings settings = FurnitureSettings.fromMap(settingsMap); + // get loot table + LootTable lootTable = lootMap == null ? null : LootTable.fromMap(lootMap); + CustomFurniture furniture = new CustomFurniture(id, settings, placements, lootTable); byId.put(id, furniture); } } @@ -437,7 +435,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat"); return; } - Vector3f seatPos = MiscUtils.getVector3f(vector3f); + Vector3f seatPos = MiscUtils.getAsVector3f(vector3f, "seat"); furniture.removeOccupiedSeat(seatPos); if (player.getVehicle() != null) return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java index 9b00dc4fc..5a3e0780e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java @@ -4,8 +4,10 @@ import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.collision.AABB; @@ -80,11 +82,12 @@ public class CustomHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getVector3f(arguments.getOrDefault("position", "0")); - float scale = MiscUtils.getAsFloat(arguments.getOrDefault("scale", "1")); - EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", (String) arguments.getOrDefault("entity-type", "slime"))); + Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); + String type = (String) arguments.getOrDefault("entity-type", "slime"); + EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); if (entityType == null) { - throw new IllegalArgumentException("EntityType not found: " + arguments.get("entity-type")); + throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); } boolean canBeHitByProjectile = (boolean) arguments.getOrDefault("can-be-hit-by-projectile", false); boolean blocksBuilding = (boolean) arguments.getOrDefault("blocks-building", true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java index 571f126d4..dae5e7afe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Quaternionf; @@ -48,13 +49,13 @@ public class HappyGhastHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - double scale = MiscUtils.getAsDouble(arguments.getOrDefault("scale", "1")); + double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale"); boolean canUseOn = (boolean) arguments.getOrDefault("can-use-item-on", false); boolean canBeHitByProjectile = (boolean) arguments.getOrDefault("can-be-hit-by-projectile", false); boolean blocksBuilding = (boolean) arguments.getOrDefault("blocks-building", false); return new HappyGhastHitBox( HitBoxFactory.getSeats(arguments), - MiscUtils.getVector3f(arguments.getOrDefault("position", "0")), + MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"), scale, canUseOn, blocksBuilding, canBeHitByProjectile ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java index fa21617cc..e48713e89 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.collision.AABB; @@ -87,7 +88,7 @@ public class InteractionHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getVector3f(arguments.getOrDefault("position", "0")); + Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); float width; float height; if (arguments.containsKey("scale")) { @@ -95,8 +96,8 @@ public class InteractionHitBox extends AbstractHitBox { width = Float.parseFloat(split[0]); height = Float.parseFloat(split[1]); } else { - width = MiscUtils.getAsFloat(arguments.getOrDefault("width", "1")); - height = MiscUtils.getAsFloat(arguments.getOrDefault("height", "1")); + width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", "1"), "width"); + height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", "1"), "height"); } boolean canUseOn = (boolean) arguments.getOrDefault("can-use-item-on", false); boolean interactive = (boolean) arguments.getOrDefault("interactive", true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java index 0dd717463..56d8985a4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java @@ -272,9 +272,9 @@ public class ShulkerHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getVector3f(arguments.getOrDefault("position", "0")); - float scale = MiscUtils.getAsFloat(arguments.getOrDefault("scale", "1")); - byte peek = (byte) MiscUtils.getAsInt(arguments.getOrDefault("peek", 0)); + Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); + byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP); boolean interactive = (boolean) arguments.getOrDefault("interactive", true); boolean interactionEntity = (boolean) arguments.getOrDefault("interaction-entity", true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index c0b332ec6..fc4745a63 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -112,8 +112,9 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onCommand(PlayerCommandPreprocessEvent event) { + Player player = event.getPlayer(); if (!Config.filterCommand()) return; - if (!event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { + if (!player.hasPermission(FontManager.BYPASS_COMMAND)) { IllegalCharacterProcessResult result = processIllegalCharacters(event.getMessage()); if (result.has()) { event.setMessage(result.text()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 01ccba6fe..6dc5566af 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -28,7 +28,7 @@ import net.momirealms.craftengine.core.pack.model.select.ChargeTypeSelectPropert import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectProperty; 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; @@ -240,32 +240,29 @@ public class BukkitItemManager extends AbstractItemManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (customItems.containsKey(id)) { - TranslationManager.instance().log("warning.config.item.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.item.duplicate"); } - // just register for recipes + // register for recipes Holder.Reference holder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(id) .orElseGet(() -> ((WritableRegistry) BuiltInRegistries.OPTIMIZED_ITEM_ID) .register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id)); boolean isVanillaItem = id.namespace().equals("minecraft") && Registry.MATERIAL.get(new NamespacedKey(id.namespace(), id.value())) != null; - String materialStringId = (String) section.get("material"); - if (isVanillaItem) + String materialStringId; + if (isVanillaItem) { materialStringId = id.value(); - if (materialStringId == null) { - TranslationManager.instance().log("warning.config.item.lack_material", path.toString(), id.toString()); - return; + } else { + materialStringId = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("material"), "warning.config.item.missing_material"); } Material material = MaterialUtils.getMaterial(materialStringId); if (material == null) { - TranslationManager.instance().log("warning.config.item.invalid_material", path.toString(), id.toString(), materialStringId); - return; + throw new LocalizedResourceConfigException("warning.config.item.invalid_material", materialStringId); } Key materialId = Key.of(material.getKey().namespace(), material.getKey().value()); - int customModelData = MiscUtils.getAsInt(section.getOrDefault("custom-model-data", 0)); + int customModelData = ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data"); Key itemModelKey = null; CustomItem.Builder itemBuilder = BukkitCustomItem.builder().id(id).material(materialId); @@ -342,13 +339,7 @@ public class BukkitItemManager extends AbstractItemManager { } } - ItemSettings itemSettings; - if (section.containsKey("settings")) { - Map settings = MiscUtils.castToMap(section.get("settings"), false); - itemSettings = ItemSettings.fromMap(settings); - } else { - itemSettings = ItemSettings.of(); - } + ItemSettings itemSettings = ItemSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true)); if (isVanillaItem) { itemSettings.canPlaceRelatedVanillaBlock(true); } @@ -392,6 +383,7 @@ public class BukkitItemManager extends AbstractItemManager { return; } + ItemModel model = ItemModels.fromMap(modelSection); boolean hasModel = false; if (customModelData != 0) { hasModel= true; @@ -399,20 +391,18 @@ public class BukkitItemManager extends AbstractItemManager { // check conflict Map 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; + throw new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString()); } if (customModelData > 16_777_216) { - TranslationManager.instance().log("warning.config.item.bad_custom_model_data_value", path.toString(), id.toString(), String.valueOf(customModelData)); + throw new LocalizedResourceConfigException("warning.config.item.bad_custom_model_data", String.valueOf(customModelData)); } conflict.put(customModelData, id); // Parse models - ItemModel model = ItemModels.fromMap(modelSection); for (ModelGeneration generation : model.modelsToGenerate()) { - prepareModelGeneration(path, id, generation); + prepareModelGeneration(generation); } if (Config.packMaxVersion() > 21.39f) { @@ -429,10 +419,8 @@ public class BukkitItemManager extends AbstractItemManager { } if (itemModelKey != null) { hasModel = true; - // use components - ItemModel model = ItemModels.fromMap(modelSection); for (ModelGeneration generation : model.modelsToGenerate()) { - prepareModelGeneration(path, id, generation); + prepareModelGeneration(generation); } if (Config.packMaxVersion() > 21.39f) { @@ -451,7 +439,7 @@ public class BukkitItemManager extends AbstractItemManager { } } if (!hasModel) { - TranslationManager.instance().log("warning.config.item.lack_model_id", path.toString(), id.toString()); + throw new LocalizedResourceConfigException("warning.config.item.missing_model_id"); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index c778d1de2..847935ca0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext; 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.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.BlockPos; @@ -187,7 +188,7 @@ public class BlockItemBehavior extends ItemBehavior { public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { Object id = arguments.get("block"); if (id == null) { - throw new IllegalArgumentException("Missing required parameter 'block' for block_item behavior"); + throw new LocalizedResourceConfigException("warning.config.item.behavior.block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for block_item behavior")); } if (id instanceof Map map) { if (map.containsKey(key.toString())) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java index fc08fb7c6..6d60e9e44 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior; +import net.momirealms.craftengine.bukkit.block.behavior.GrassBlockBehavior; import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; @@ -43,6 +44,10 @@ public class BoneMealItemBehavior extends ItemBehavior { } } else if (state.behavior() instanceof SaplingBlockBehavior) { shouldHandle = true; + } else if (state.behavior() instanceof GrassBlockBehavior) { + if (block.getLocation().add(0, 1, 0).getBlock().isEmpty()) { + shouldHandle = true; + } } if (!shouldHandle) return InteractionResult.PASS; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index ca01dba72..f61092a23 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; @@ -154,7 +155,7 @@ public class FurnitureItemBehavior extends ItemBehavior { public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { Object id = arguments.get("furniture"); if (id == null) { - throw new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"); + throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); } if (id instanceof Map map) { if (map.containsKey(key.toString())) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java index 3e35bc7df..628dfa775 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -12,9 +12,11 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; @@ -63,9 +65,9 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { Object id = arguments.get("block"); if (id == null) { - throw new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior"); + throw new LocalizedResourceConfigException("warning.config.item.behavior.liquid_collision.missing_block", new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior")); } - int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1)); + int offset = ResourceConfigUtils.getAsInt(arguments.getOrDefault("y-offset", 1), "y-offset"); if (id instanceof Map map) { if (map.containsKey(key.toString())) { // 防呆 diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index ef2800849..1bfb8cd09 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -15,10 +15,8 @@ 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.VersionHelper; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.Vec3d; import org.bukkit.Bukkit; @@ -108,12 +106,13 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme @Override public void parseSection(Pack pack, Path path, Key id, Map 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; + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type"); + VanillaLoot.Type typeEnum; + try { + typeEnum = VanillaLoot.Type.valueOf(type.toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.vanilla_loot.invalid_type", type, EnumUtils.toString(VanillaLoot.Type.values())); } - VanillaLoot.Type typeEnum = VanillaLoot.Type.valueOf(type.toUpperCase(Locale.ENGLISH)); boolean override = (boolean) section.getOrDefault("override", false); List targets = MiscUtils.getAsStringList(section.getOrDefault("target", List.of())); LootTable lootTable = LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), false)); @@ -123,16 +122,14 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme if (target.endsWith("]") && target.contains("[")) { java.lang.Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(target)); if (blockState == Reflections.instance$Blocks$AIR$defaultState) { - TranslationManager.instance().log("warning.config.vanilla_loot.block.invalid_target", path.toString(), id.toString(), target); - return; + throw new LocalizedResourceConfigException("warning.config.vanilla_loot.block.invalid_target", target); } 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; + throw new LocalizedResourceConfigException("warning.config.vanilla_loot.block.invalid_target", target); } VanillaLoot vanillaLoot = blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK)); if (override) vanillaLoot.override(true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 1b9b0bbf3..b68ef4a2d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java @@ -49,6 +49,7 @@ import net.momirealms.craftengine.core.util.SectionPosUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.CESection; import net.momirealms.craftengine.core.world.chunk.InjectedPalettedContainerHolder; import net.momirealms.craftengine.shared.ObjectHolder; @@ -101,8 +102,9 @@ public class BukkitInjector { .name("net.minecraft.world.level.chunk.InjectedPalettedContainer") .implement(InjectedPalettedContainerHolder.class) .defineField("target", Reflections.clazz$PalettedContainer, Visibility.PRIVATE) - .defineField("cesection", CESection.class, Visibility.PRIVATE) .defineField("ceworld", CEWorld.class, Visibility.PRIVATE) + .defineField("cesection", CESection.class, Visibility.PRIVATE) + .defineField("cechunk", CEChunk.class, Visibility.PRIVATE) .defineField("cepos", SectionPos.class, Visibility.PRIVATE) .method(ElementMatchers.any() .and(ElementMatchers.not(ElementMatchers.is(Reflections.method$PalettedContainer$getAndSet))) @@ -117,6 +119,8 @@ public class BukkitInjector { .intercept(FieldAccessor.ofField("target")) .method(ElementMatchers.named("ceSection")) .intercept(FieldAccessor.ofField("cesection")) + .method(ElementMatchers.named("ceChunk")) + .intercept(FieldAccessor.ofField("cechunk")) .method(ElementMatchers.named("ceWorld")) .intercept(FieldAccessor.ofField("ceworld")) .method(ElementMatchers.named("cePos")) @@ -384,14 +388,20 @@ public class BukkitInjector { // } // } - public static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, SectionPos pos) { + public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, CEChunk chunk, SectionPos pos) { try { Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); if (!clazz$InjectedPalettedContainer.isInstance(container)) { - InjectedPalettedContainerHolder injectedObject = (InjectedPalettedContainerHolder) Reflections.UNSAFE.allocateInstance(clazz$InjectedPalettedContainer); - varHandle$InjectedPalettedContainer$target.set(injectedObject, container); - injectedObject.ceSection(ceSection); + InjectedPalettedContainerHolder injectedObject; + if (Config.fastPaletteInjection()) { + injectedObject = FastNMS.INSTANCE.createInjectedPalettedContainerHolder(container); + } else { + injectedObject = (InjectedPalettedContainerHolder) Reflections.UNSAFE.allocateInstance(clazz$InjectedPalettedContainer); + varHandle$InjectedPalettedContainer$target.set(injectedObject, container); + } injectedObject.ceWorld(ceWorld); + injectedObject.ceChunk(chunk); + injectedObject.ceSection(ceSection); injectedObject.cePos(pos); Reflections.varHandle$PalettedContainer$data.setVolatile(injectedObject, Reflections.varHandle$PalettedContainer$data.get(container)); Reflections.field$LevelChunkSection$states.set(targetSection, injectedObject); @@ -682,16 +692,24 @@ public class BukkitInjector { Object newState = args[3]; int stateId = BlockStateUtils.blockStateToId(newState); CESection section = holder.ceSection(); + // 如果是原版方块 if (BlockStateUtils.isVanillaBlock(stateId)) { - section.setBlockState(x, y, z, EmptyBlock.INSTANCE.defaultState()); + // 那么应该情况自定义块 + ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); + // 如果先前不是空气则标记 + if (!previous.isEmpty()) { + holder.ceChunk().setDirty(true); + } 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 (Config.enableLightSystem()) { + ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); + // 如果之前的自定义块(空气)和当前自定义块不同 + if (previousImmutableBlockState != immutableBlockState) { + holder.ceChunk().setDirty(true); + if (Config.enableLightSystem() && !immutableBlockState.isEmpty()) { updateLightIfChanged(holder, previousState, newState, immutableBlockState.vanillaBlockState().handle(), y, z, x); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 7eb2bc5e4..d485c0da4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -151,6 +151,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.HANDSHAKE_C2S, Reflections.clazz$ClientIntentionPacket); registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket); + registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, Reflections.clazz$ClientboundEntityEventPacket); registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 13eab66b8..0b9b202fa 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -2286,4 +2286,19 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e); } }; + + public static final TriConsumer ENTITY_EVENT = (user, event, packet) -> { + try { + Object player = user.serverPlayer(); + if (player == null) return; + int entityId = Reflections.field$ClientboundEntityEventPacket$entityId.getInt(packet); + if (entityId != FastNMS.INSTANCE.method$Entity$getId(player)) return; + byte eventId = Reflections.field$ClientboundEntityEventPacket$eventId.getByte(packet); + if (eventId >= 24 && eventId <= 28) { + CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid()); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java index efe651614..3b44594e6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Particle; public class ParticleUtils { @@ -10,7 +11,7 @@ public class ParticleUtils { } catch (IllegalArgumentException e) { return switch (particle) { case "REDSTONE" -> Particle.valueOf("DUST"); - case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER"); + case "VILLAGER_HAPPY" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "HAPPY_VILLAGER" : "VILLAGER_HAPPY"); default -> Particle.valueOf(particle); }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 7d4d24a24..85165e2e5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -397,7 +397,7 @@ public class Reflections { ); public static final Class clazz$HolderLookup$Provider = BukkitReflectionUtils.findReobfOrMojmapClass( - "core.HolderLookup$b", + VersionHelper.isOrAbove1_20_5() ? "core.HolderLookup$a" : "core.HolderLookup$b", "core.HolderLookup$Provider" ); @@ -2076,6 +2076,12 @@ public class Reflections { ) ); + public static final Class clazz$PlacedFeature = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.levelgen.placement.PlacedFeature") + ) + ); + // 1.21+ public static final Class clazz$JukeboxSong = ReflectionUtils.getClazz( @@ -2092,6 +2098,7 @@ public class Reflections { public static final Object instance$BuiltInRegistries$ENTITY_TYPE; public static final Object instance$BuiltInRegistries$FLUID; public static final Object instance$BuiltInRegistries$RECIPE_TYPE; + public static final Object instance$BuiltInRegistries$PLACED_FEATURE; public static final Object instance$InternalRegistries$DIMENSION_TYPE; @Nullable // 1.21+ public static final Object instance$InternalRegistries$JUKEBOX_SONG; @@ -2108,6 +2115,7 @@ public class Reflections { public static final Object instance$Registries$RECIPE_TYPE; public static final Object instance$Registries$DIMENSION_TYPE; public static final Object instance$Registries$CONFIGURED_FEATURE; + public static final Object instance$Registries$PLACED_FEATURE; @Nullable // 1.21+ public static final Object instance$Registries$JUKEBOX_SONG; @@ -2128,6 +2136,7 @@ public class Reflections { Object registries$Fluid = null; Object registries$RecipeType = null; Object registries$ConfiguredFeature = null; + Object registries$PlacedFeature = null; Object registries$JukeboxSong = null; for (Field field : fields) { Type fieldType = field.getGenericType(); @@ -2166,6 +2175,8 @@ public class Reflections { registries$Fluid = field.get(null); } else if (VersionHelper.isOrAbove1_21() && type == clazz$JukeboxSong) { registries$JukeboxSong = field.get(null); + } else if (type == clazz$PlacedFeature) { + registries$PlacedFeature = field.get(null); } } } @@ -2184,6 +2195,7 @@ public class Reflections { instance$Registries$FLUID = requireNonNull(registries$Fluid); instance$Registries$RECIPE_TYPE = requireNonNull(registries$RecipeType); instance$Registries$CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature); + instance$Registries$PLACED_FEATURE = requireNonNull(registries$PlacedFeature); instance$Registries$JUKEBOX_SONG = registries$JukeboxSong; Object server = method$MinecraftServer$getServer.invoke(null); Object registries = field$MinecraftServer$registries.get(server); @@ -2199,6 +2211,7 @@ public class Reflections { instance$BuiltInRegistries$ENTITY_TYPE = method$RegistryAccess$registryOrThrow.invoke(instance$registryAccess, registries$EntityType); instance$BuiltInRegistries$FLUID = method$RegistryAccess$registryOrThrow.invoke(instance$registryAccess, registries$Fluid); instance$BuiltInRegistries$RECIPE_TYPE = method$RegistryAccess$registryOrThrow.invoke(instance$registryAccess, registries$RecipeType); + instance$BuiltInRegistries$PLACED_FEATURE = method$RegistryAccess$registryOrThrow.invoke(instance$registryAccess, registries$PlacedFeature); if (registries$JukeboxSong == null) instance$InternalRegistries$JUKEBOX_SONG = null; else instance$InternalRegistries$JUKEBOX_SONG = method$RegistryAccess$registryOrThrow.invoke(instance$registryAccess, registries$JukeboxSong); } catch (ReflectiveOperationException e) { @@ -3125,6 +3138,18 @@ public class Reflections { ) ); + public static final Field field$ClientboundEntityEventPacket$entityId = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundEntityEventPacket, int.class, 0 + ) + ); + + public static final Field field$ClientboundEntityEventPacket$eventId = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundEntityEventPacket, byte.class, 0 + ) + ); + public static final Constructor constructor$ClientboundEntityEventPacket = requireNonNull( ReflectionUtils.getConstructor( clazz$ClientboundEntityEventPacket, clazz$Entity, byte.class @@ -3587,6 +3612,8 @@ public class Reflections { public static final Object instance$Blocks$FIRE; public static final Object instance$Blocks$SOUL_FIRE; public static final Object instance$Blocks$ICE; + public static final Object instance$Blocks$SHORT_GRASS; + public static final Object instance$Blocks$SHORT_GRASS$defaultState; static { try { @@ -3602,6 +3629,9 @@ public class Reflections { instance$Blocks$STONE$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$STONE); Object ice = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "ice"); instance$Blocks$ICE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, ice); + Object shortGrass = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass"); + instance$Blocks$SHORT_GRASS = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, shortGrass); + instance$Blocks$SHORT_GRASS$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$SHORT_GRASS); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -6573,4 +6603,16 @@ public class Reflections { clazz$ServerboundResourcePackPacket, clazz$ServerboundResourcePackPacket$Action, 0 ) ); + + public static final Field field$CraftBlockStates$FACTORIES = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$CraftBlockStates, "FACTORIES" + ) + ); + + public static final Class clazz$CraftBlockStates$BlockEntityStateFactory = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleCBClass("block.CraftBlockStates$BlockEntityStateFactory") + ) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 070737eee..c644076c8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -264,34 +264,42 @@ public class BukkitWorldManager implements WorldManager, Listener { ChunkPos pos = new ChunkPos(chunk.getX(), chunk.getZ()); CEChunk ceChunk = world.getChunkAtIfLoaded(chunk.getX(), chunk.getZ()); if (ceChunk != null) { - try { - world.worldDataStorage().writeChunkAt(pos, ceChunk, false); - } catch (IOException e) { - this.plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e); - } finally { - if (Config.restoreVanillaBlocks()) { - CESection[] ceSections = ceChunk.sections(); - Object worldServer = FastNMS.INSTANCE.field$CraftChunk$worldServer(chunk); - Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); - Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ()); - Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); - for (int i = 0; i < ceSections.length; i++) { - CESection ceSection = ceSections[i]; - Object section = sections[i]; - BukkitInjector.uninjectLevelChunkSection(section); - if (ceSection.statesContainer().isEmpty()) continue; + if (ceChunk.dirty()) { + try { + world.worldDataStorage().writeChunkAt(pos, ceChunk, false); + ceChunk.setDirty(false); + } catch (IOException e) { + this.plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e); + } + } + if (Config.restoreVanillaBlocks()) { + boolean unsaved = false; + CESection[] ceSections = ceChunk.sections(); + Object worldServer = FastNMS.INSTANCE.field$CraftChunk$worldServer(chunk); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); + Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ()); + Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + BukkitInjector.uninjectLevelChunkSection(section); + if (!ceSection.statesContainer().isEmpty()) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); if (!customState.isEmpty() && customState.vanillaBlockState() != null) { FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().handle(), false); + unsaved = true; } } } } } } + if (unsaved && !FastNMS.INSTANCE.method$LevelChunk$isUnsaved(levelChunk)) { + FastNMS.INSTANCE.method$LevelChunk$markUnsaved(levelChunk); + } } ceChunk.unload(); } @@ -375,7 +383,7 @@ public class BukkitWorldManager implements WorldManager, Listener { } } } - BukkitInjector.injectLevelChunkSection(section, ceSection, ceWorld, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); + BukkitInjector.injectLevelChunkSection(section, ceSection, ceWorld, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); } if (Config.enableRecipeSystem()) { @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java new file mode 100644 index 000000000..8e19578b0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.block; + +import net.momirealms.sparrow.nbt.CompoundTag; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class BlockEntityState { + private final CompoundTag nbt; + + public BlockEntityState(CompoundTag nbt) { + this.nbt = nbt.deepClone(); + } + + public CompoundTag nbt() { + return nbt; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index b4c5efafc..cfcfd042c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.block; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.Nullable; @@ -54,7 +55,7 @@ public class BlockSettings { if (factory != null) { factory.createModifier(entry.getValue()).apply(settings); } else { - throw new IllegalArgumentException("Unknown block settings key: " + entry.getKey()); + throw new LocalizedResourceConfigException("warning.config.block.settings.unknown", entry.getKey()); } } return settings; @@ -330,19 +331,19 @@ public class BlockSettings { static { registerFactory("luminance", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "luminance"); return settings -> settings.luminance(intValue); })); registerFactory("block-light", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "block-light"); return settings -> settings.blockLight(intValue); })); registerFactory("hardness", (value -> { - float floatValue = MiscUtils.getAsFloat(value); + float floatValue = ResourceConfigUtils.getAsFloat(value, "hardness"); return settings -> settings.hardness(floatValue); })); registerFactory("resistance", (value -> { - float floatValue = MiscUtils.getAsFloat(value); + float floatValue = ResourceConfigUtils.getAsFloat(value, "resistance"); return settings -> settings.resistance(floatValue); })); registerFactory("is-randomly-ticking", (value -> { @@ -354,7 +355,7 @@ public class BlockSettings { return settings -> settings.pushReaction(reaction); })); registerFactory("map-color", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "map-color"); return settings -> settings.mapColor(MapColor.get(intValue)); })); registerFactory("burnable", (value -> { @@ -374,11 +375,11 @@ public class BlockSettings { return settings -> settings.tags(tags.stream().map(Key::of).collect(Collectors.toSet())); })); registerFactory("burn-chance", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "burn-chance"); return settings -> settings.burnChance(intValue); })); registerFactory("fire-spread-chance", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "fire-spread-chance"); return settings -> settings.fireSpreadChance(intValue); })); registerFactory("replaceable", (value -> { @@ -422,7 +423,7 @@ public class BlockSettings { return settings -> settings.respectToolComponent(booleanValue); })); registerFactory("incorrect-tool-dig-speed", (value -> { - float floatValue = MiscUtils.getAsFloat(value); + float floatValue = ResourceConfigUtils.getAsFloat(value, "incorrect-tool-dig-speed"); return settings -> settings.incorrectToolSpeed(floatValue); })); registerFactory("name", (value -> { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index 54727da4d..ced0f7f7e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.shared.block.BlockBehavior; @@ -31,14 +32,14 @@ public abstract class CustomBlock { @Nullable protected final LootTable lootTable; - public CustomBlock( + protected CustomBlock( @NotNull Key id, @NotNull Holder.Reference holder, @NotNull Map> properties, @NotNull Map appearances, @NotNull Map variantMapper, @NotNull BlockSettings settings, - @Nullable Map behaviorSettings, + @Nullable Map behavior, @Nullable LootTable lootTable ) { holder.bindValue(this); @@ -49,18 +50,17 @@ public abstract class CustomBlock { this.placements = new ArrayList<>(); this.variantProvider = new BlockStateVariantProvider(holder, ImmutableBlockState::new, properties); this.defaultState = this.variantProvider.getDefaultState(); - this.behavior = BlockBehaviors.fromMap(this, behaviorSettings); + this.behavior = BlockBehaviors.fromMap(this, behavior); for (Map.Entry entry : variantMapper.entrySet()) { String nbtString = entry.getKey(); CompoundTag tag = BlockNbtParser.deserialize(this, nbtString); if (tag == null) { - CraftEngine.instance().logger().warn("Illegal block state: " + nbtString); - continue; + throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); } VariantState variantState = entry.getValue(); int vanillaStateRegistryId = appearances.getOrDefault(variantState.appearance(), -1); + // This should never happen if (vanillaStateRegistryId == -1) { - CraftEngine.instance().logger().warn("Could not find appearance " + variantState.appearance() + " for block " + id); vanillaStateRegistryId = appearances.values().iterator().next(); } // Late init states @@ -159,4 +159,78 @@ public abstract class CustomBlock { } return state; } + + public abstract static class Builder { + protected final Key id; + protected Map> properties; + protected Map appearances; + protected Map variantMapper; + protected BlockSettings settings; + protected Map behavior; + protected LootTable lootTable; + + protected Builder(Key id) { + this.id = id; + } + + public Builder appearances(Map appearances) { + this.appearances = appearances; + return this; + } + + public Builder behavior(Map behavior) { + this.behavior = behavior; + return this; + } + + public Builder lootTable(LootTable lootTable) { + this.lootTable = lootTable; + return this; + } + + public Builder properties(Map> properties) { + this.properties = properties; + return this; + } + + public Builder settings(BlockSettings settings) { + this.settings = settings; + return this; + } + + public Builder variantMapper(Map variantMapper) { + this.variantMapper = variantMapper; + return this; + } + + public Map appearances() { + return appearances; + } + + public Map behavior() { + return behavior; + } + + public Key id() { + return id; + } + + public LootTable lootTable() { + return lootTable; + } + + public Map> properties() { + return properties; + } + + public BlockSettings settings() { + return settings; + } + + public Map variantMapper() { + return variantMapper; + } + + public abstract CustomBlock build(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java index 84d079d37..4f827823b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java @@ -7,10 +7,12 @@ import java.util.Map; public class EmptyBlock extends CustomBlock { public static EmptyBlock INSTANCE; + public static ImmutableBlockState STATE; public EmptyBlock(Key id, Holder.Reference holder) { super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), null, null); INSTANCE = this; + STATE = defaultState(); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index 928e11df0..4f6ca3a96 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -51,7 +51,7 @@ public class ImmutableBlockState extends BlockStateHolder { } public boolean isEmpty() { - return this == EmptyBlock.INSTANCE.defaultState(); + return this == EmptyBlock.STATE; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java index 5cbc64295..e67a96dc1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java @@ -1,11 +1,13 @@ package net.momirealms.craftengine.core.block.behavior; import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import net.momirealms.craftengine.shared.block.BlockBehavior; import net.momirealms.craftengine.shared.block.EmptyBlockBehavior; @@ -23,15 +25,12 @@ public class BlockBehaviors { } public static BlockBehavior fromMap(CustomBlock block, @Nullable Map map) { - if (map == null) return EmptyBlockBehavior.INSTANCE; - String type = (String) map.getOrDefault("type", "empty"); - if (type == null) { - throw new NullPointerException("behavior type cannot be null"); - } + if (map == null || map.isEmpty()) return EmptyBlockBehavior.INSTANCE; + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.block.behavior.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); BlockBehaviorFactory factory = BuiltInRegistries.BLOCK_BEHAVIOR_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown behavior type: " + type); + throw new LocalizedResourceConfigException("warning.config.block.behavior.invalid_type", type.toString()); } return factory.create(block, map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/EnumProperty.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/EnumProperty.java index 085e09505..d26941108 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/EnumProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/EnumProperty.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.block.properties; import com.google.common.collect.ImmutableMap; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.sparrow.nbt.StringTag; import net.momirealms.sparrow.nbt.Tag; @@ -116,10 +115,7 @@ public class EnumProperty> extends Property { A defaultValue = enums.stream() .filter(e -> e.name().toLowerCase(Locale.ENGLISH).equals(defaultValueName)) .findFirst() - .orElseGet(() -> { - CraftEngine.instance().logger().warn("Invalid default value '" + defaultValueName + "' for property '" + name + "'"); - return enums.get(0); - }); + .orElseGet(() -> enums.get(0)); return EnumProperty.create(name, enumClass, enums, defaultValue); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java index 4b17184ac..7b77e0bd0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java @@ -1,7 +1,8 @@ package net.momirealms.craftengine.core.block.properties; import it.unimi.dsi.fastutil.ints.IntImmutableList; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.sparrow.nbt.IntTag; import net.momirealms.sparrow.nbt.NumericTag; import net.momirealms.sparrow.nbt.Tag; @@ -89,10 +90,17 @@ public class IntegerProperty extends Property { public Property create(String name, Map arguments) { String range = arguments.getOrDefault("range", "1~1").toString(); String[] split = range.split("~"); - int min = Integer.parseInt(split[0]); - int max = Integer.parseInt(split[1]); - int defaultValue = MiscUtils.getAsInt(arguments.getOrDefault("default", min)); - return IntegerProperty.create(name, min, max,defaultValue); + if (split.length != 2) { + throw new LocalizedResourceConfigException("warning.config.block.state.property.integer.invalid_range", range, name); + } + try { + int min = Integer.parseInt(split[0]); + int max = Integer.parseInt(split[1]); + int defaultValue = ResourceConfigUtils.getAsInt(arguments.getOrDefault("default", min), "default"); + return IntegerProperty.create(name, min, max,defaultValue); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.block.state.property.integer.invalid_range", e, range, name); + } } } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index ac84cf0d5..bf1b1c0a5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java @@ -1,13 +1,11 @@ package net.momirealms.craftengine.core.block.properties; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; -import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.HorizontalDirection; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceKey; +import net.momirealms.craftengine.core.util.*; import java.util.Map; @@ -34,14 +32,11 @@ public class Properties { } public static Property fromMap(String name, Map map) { - String type = (String) map.getOrDefault("type", "empty"); - if (type == null) { - throw new NullPointerException("Property type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.block.state.property.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); PropertyFactory factory = BuiltInRegistries.PROPERTY_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown property type: " + type); + throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_type", key.toString(), name); } return factory.create(name, map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java index ee1e88cff..c6ef572e3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.block.properties; import com.google.common.collect.ImmutableMap; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.sparrow.nbt.StringTag; import net.momirealms.sparrow.nbt.Tag; @@ -99,10 +98,7 @@ public class StringProperty extends Property { String defaultValue = values.stream() .filter(e -> e.toLowerCase(Locale.ENGLISH).equals(defaultValueName)) .findFirst() - .orElseGet(() -> { - CraftEngine.instance().logger().warn("Invalid default value '" + defaultValueName + "' for property '" + name + "'"); - return values.getFirst(); - }); + .orElseGet(() -> values.get(0)); return StringProperty.create(name, values, defaultValue); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java index d997323f3..298dbc877 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import org.jetbrains.annotations.Nullable; @@ -38,7 +39,7 @@ public class FurnitureSettings { if (factory != null) { factory.createModifier(entry.getValue()).apply(settings); } else { - throw new IllegalArgumentException("Unknown item settings key: " + entry.getKey()); + throw new LocalizedResourceConfigException("warning.config.furniture.settings.unknown", entry.getKey()); } } return settings; @@ -72,10 +73,12 @@ public class FurnitureSettings { return this; } + @FunctionalInterface public interface Modifier { void apply(FurnitureSettings settings); + @FunctionalInterface interface Factory { FurnitureSettings.Modifier createModifier(Object value); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java index 54a4f6c39..3720eb9bd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java @@ -15,8 +15,8 @@ public interface HitBoxFactory { return seats.stream() .map(arg -> { String[] split = arg.split(" "); - if (split.length == 1) return new Seat(MiscUtils.getVector3f(split[0]), 0, false); - return new Seat(MiscUtils.getVector3f(split[0]), Float.parseFloat(split[1]), true); + if (split.length == 1) return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), 0, false); + return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), Float.parseFloat(split[1]), true); }) .toArray(Seat[]::new); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java index af2246a5c..f77435813 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; @@ -23,10 +24,10 @@ public class HitBoxTypes { } public static HitBox fromMap(Map arguments) { - Key type = Optional.ofNullable((String) arguments.get("type")).map(Key::of).orElse(HitBoxTypes.INTERACTION); + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(HitBoxTypes.INTERACTION); HitBoxFactory factory = BuiltInRegistries.HITBOX_FACTORY.getValue(type); if (factory == null) { - throw new IllegalArgumentException("Unknown hitbox type: " + type); + throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.invalid_type", type.toString()); } return factory.create(arguments); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index 60bcb92b4..3277303cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -7,6 +7,7 @@ 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.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.context.ContextHolder; @@ -15,6 +16,10 @@ import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; import org.jetbrains.annotations.NotNull; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -352,19 +357,16 @@ public abstract class AbstractFontManager implements FontManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (emojis.containsKey(id)) { - TranslationManager.instance().log("warning.config.emoji.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.duplicate", path, id); } String permission = (String) section.get("permission"); Object keywordsRaw = section.get("keywords"); if (keywordsRaw == null) { - TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); } List keywords = MiscUtils.getAsStringList(keywordsRaw); if (keywords.isEmpty()) { - TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); } String content = section.getOrDefault("content", "").toString(); String image = null; @@ -377,8 +379,7 @@ public abstract class AbstractFontManager implements FontManager { if (bitmapImage.isPresent()) { image = bitmapImage.get().miniMessage(0, 0); } else { - TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); } } else if (split.length == 4) { Key imageId = new Key(split[0], split[1]); @@ -387,16 +388,13 @@ public abstract class AbstractFontManager implements FontManager { try { image = bitmapImage.get().miniMessage(Integer.parseInt(split[2]), Integer.parseInt(split[3])); } catch (ArrayIndexOutOfBoundsException e) { - TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); } } else { - TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); } } else { - TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); - return; + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); } } Emoji emoji = new Emoji(content, permission, image, keywords); @@ -420,46 +418,36 @@ public abstract class AbstractFontManager implements FontManager { @Override public void parseSection(Pack pack, Path path, Key id, Map 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; + throw new LocalizedResourceConfigException("warning.config.image.duplicate", path, id); } Object file = section.get("file"); if (file == null) { - TranslationManager.instance().log("warning.config.image.no_file", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.image.missing_file", path, id); } String resourceLocation = file.toString().replace("\\", "/"); if (!ResourceLocation.isValid(resourceLocation)) { - TranslationManager.instance().log("warning.config.image.invalid_resource_location", path.toString(), id.toString(), resourceLocation); - return; + throw new LocalizedResourceConfigException("warning.config.image.invalid_file_chars", path, id, resourceLocation); } 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; + throw new LocalizedResourceConfigException("warning.config.image.invalid_font_chars", path, id, fontName); } Key fontKey = Key.withDefaultNamespace(fontName, id.namespace()); Font font = getOrCreateFont(fontKey); List chars; - if (section.containsKey("chars")) { - chars = MiscUtils.getAsStringList(section.get("chars")).stream().map(it -> { + Object charsObj = section.get("chars"); + if (charsObj == null) { + charsObj = section.get("char"); + } + if (charsObj == null) { + throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); + } + if (charsObj instanceof List list) { + chars = MiscUtils.getAsStringList(list).stream().map(it -> { if (it.startsWith("\\u")) { return CharacterUtils.decodeUnicodeToChars(it); } else { @@ -467,15 +455,10 @@ public abstract class AbstractFontManager implements FontManager { } }).toList(); } else { - Object c = section.get("char"); - if (c == null) { - TranslationManager.instance().log("warning.config.image.lack_char", path.toString(), id.toString()); - return; - } - if (c instanceof Integer integer) { + if (charsObj instanceof Integer integer) { chars = List.of(new char[]{(char) integer.intValue()}); } else { - String character = c.toString(); + String character = charsObj.toString(); if (character.length() == 1) { chars = List.of(character.toCharArray()); } else { @@ -491,29 +474,25 @@ public abstract class AbstractFontManager implements FontManager { 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(), + throw new LocalizedResourceConfigException("warning.config.image.codepoint_conflict", path, id, fontKey.toString(), CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), new String(Character.toChars(codepoint)), - image.id().toString() - ); - return; + image.id().toString()); } } if (codepoints.length == 0) { - TranslationManager.instance().log("warning.config.image.lack_char", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); } 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; + throw new LocalizedResourceConfigException("warning.config.image.invalid_codepoint_grid", path, id); } } + Object heightObj = section.get("height"); + if (!resourceLocation.endsWith(".png")) resourceLocation += ".png"; Key namespacedPath = Key.of(resourceLocation); Path targetImagePath = pack.resourcePackFolder() @@ -523,8 +502,26 @@ public abstract class AbstractFontManager implements FontManager { .resolve(namespacedPath.value()); if (!Files.exists(targetImagePath)) { - TranslationManager.instance().log("warning.config.image.file_not_exist", path.toString(), id.toString(), targetImagePath.toString()); + TranslationManager.instance().log("warning.config.image.file_not_found", path.toString(), id.toString(), targetImagePath.toString()); // DO NOT RETURN, JUST GIVE WARNINGS + } else if (heightObj == null) { + try (InputStream in = Files.newInputStream(targetImagePath)) { + BufferedImage image = ImageIO.read(in); + heightObj = image.getHeight() / codepointGrid.length; + } catch (IOException e) { + plugin.logger().warn("Failed to load image " + targetImagePath, e); + return; + } + } + + if (heightObj == null) { + throw new LocalizedResourceConfigException("warning.config.image.missing_height", path, id); + } + + int height = ResourceConfigUtils.getAsInt(heightObj, "height"); + int ascent = ResourceConfigUtils.getAsInt(section.getOrDefault("ascent", height - 1), "ascent"); + if (height < ascent) { + throw new LocalizedResourceConfigException("warning.config.image.height_ascent_conflict", path, id, String.valueOf(height), String.valueOf(ascent)); } BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid); @@ -534,7 +531,7 @@ public abstract class AbstractFontManager implements FontManager { } } - AbstractFontManager.this.images.put(id, bitmapImage); + images.put(id, bitmapImage); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java b/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java index 859165162..8272d7419 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java @@ -7,7 +7,9 @@ import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; import net.momirealms.craftengine.core.util.Key; -public class BitmapImage implements FontProvider { +import java.util.function.Supplier; + +public class BitmapImage implements Supplier { private final Key id; private final Key font; private final int height; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java deleted file mode 100644 index 7055a82b4..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.font; - -import com.google.gson.JsonObject; - -import java.util.function.Supplier; - -public interface FontProvider extends Supplier { -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 2e02df698..59d8a06a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -3,8 +3,10 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.item.modifier.EquippableModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.jetbrains.annotations.Nullable; @@ -35,6 +37,7 @@ public class ItemSettings { } public static ItemSettings fromMap(Map map) { + if (map == null) return ItemSettings.of(); return applyModifiers(ItemSettings.of(), map); } @@ -56,7 +59,7 @@ public class ItemSettings { if (factory != null) { factory.createModifier(entry.getValue()).apply(settings); } else { - throw new IllegalArgumentException("Unknown item settings key: " + entry.getKey()); + throw new LocalizedResourceConfigException("warning.config.item.settings.unknown", entry.getKey()); } } return settings; @@ -155,14 +158,14 @@ public class ItemSettings { List> materials = (List>) value; List anvilRepairItemList = new ArrayList<>(); for (Map material : materials) { - int amount = MiscUtils.getAsInt(material.getOrDefault("amount", 0)); - double percent = MiscUtils.getAsDouble(material.getOrDefault("percent", 0)); + int amount = ResourceConfigUtils.getAsInt(material.getOrDefault("amount", 0), "amount"); + double percent = ResourceConfigUtils.getAsDouble(material.getOrDefault("percent", 0), "percent"); anvilRepairItemList.add(new AnvilRepairItem(MiscUtils.getAsStringList(material.get("target")), amount, percent)); } return settings -> settings.repairItems(anvilRepairItemList); })); registerFactory("fuel-time", (value -> { - int intValue = MiscUtils.getAsInt(value); + int intValue = ResourceConfigUtils.getAsInt(value, "fuel-time"); return settings -> settings.fuelTime(intValue); })); registerFactory("tags", (value -> { @@ -182,7 +185,7 @@ public class ItemSettings { EquipmentGeneration.Layer.fromConfig(args.get("wolf-body")), EquipmentGeneration.Layer.fromConfig(args.get("wings")), data, - MiscUtils.getAsInt(args.getOrDefault("trim", -1)) + ResourceConfigUtils.getAsInt(args.getOrDefault("trim", -1), "trim") ); return settings -> settings.equipment(equipment); })); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java index b491baf28..c2217ec6a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java @@ -1,11 +1,13 @@ package net.momirealms.craftengine.core.item.behavior; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.nio.file.Path; @@ -21,14 +23,12 @@ public class ItemBehaviors { } public static ItemBehavior fromMap(Pack pack, Path path, Key id, Map map) { - String type = (String) map.getOrDefault("type", "empty"); - if (type == null) { - throw new NullPointerException("behavior type cannot be null"); - } + if (map == null || map.isEmpty()) return EmptyItemBehavior.INSTANCE; + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.behavior.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); ItemBehaviorFactory factory = BuiltInRegistries.ITEM_BEHAVIOR_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown behavior type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type.toString()); } return factory.create(pack, path, id, map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java new file mode 100644 index 000000000..4ecd81f55 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java @@ -0,0 +1,64 @@ +package net.momirealms.craftengine.core.item.recipe; + +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.util.EnumUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.*; + +public abstract class AbstractRecipeFactory implements RecipeFactory { + + protected List ingredients(Map arguments) { + return MiscUtils.getAsStringList(getIngredientOrThrow(arguments)); + } + + protected Map ingredientMap(Map arguments) { + return MiscUtils.castToMap(getIngredientOrThrow(arguments), true); + } + + protected Set> ingredientHolders(Map arguments) { + Set> holders = new HashSet<>(); + for (String item : ingredients(arguments)) { + if (item.charAt(0) == '#') { + holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); + } else { + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); + } + } + return holders; + } + + protected Object getIngredientOrThrow(Map arguments) { + Object ingredient = ResourceConfigUtils.get(arguments, "ingredient", "ingredients"); + if (ingredient == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.missing_ingredient"); + } + return ingredient; + } + + protected CookingRecipeCategory cookingRecipeCategory(Map arguments) { + CookingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.cooking.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CookingRecipeCategory.values())); + } + return recipeCategory; + } + + protected CraftingRecipeCategory craftingRecipeCategory(Map arguments) { + CraftingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.crafting.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CraftingRecipeCategory.values())); + } + return recipeCategory; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 6f55615cf..7220614c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -10,7 +10,7 @@ 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -157,16 +157,9 @@ public abstract class AbstractRecipeManager implements RecipeManager { public void parseSection(Pack pack, Path path, Key id, Map section) { if (!Config.enableRecipeSystem()) return; if (AbstractRecipeManager.this.byId.containsKey(id)) { - TranslationManager.instance().log("warning.config.recipe.duplicated", path.toString(), id.toString()); - return; - } - Recipe recipe; - try { - recipe = RecipeTypes.fromMap(id, section); - } catch (Exception e) { - CraftEngine.instance().logger().warn(path, "Failed to create recipe: " + id, e); - return; + throw new LocalizedResourceConfigException("warning.config.recipe.duplicate", path, id); } + Recipe recipe = RecipeTypes.fromMap(id, section); try { markAsCustomRecipe(id); registerInternalRecipe(id, recipe); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java index 8d80181ec..d7f2e531d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java @@ -1,13 +1,12 @@ package net.momirealms.craftengine.core.item.recipe; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Map; +import java.util.Set; public class CustomBlastingRecipe extends CustomCookingRecipe { public static final Factory FACTORY = new Factory<>(); @@ -21,33 +20,16 @@ public class CustomBlastingRecipe extends CustomCookingRecipe { return RecipeTypes.BLASTING; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { - CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80)); - float experience = MiscUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f)); - List items = MiscUtils.getAsStringList(arguments.get("ingredient")); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); - } - } - return new CustomBlastingRecipe( - id, - recipeCategory, - group, - Ingredient.of(holders), - cookingTime, - experience, - parseResult(arguments) - ); + int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); + float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); + Set> holders = ingredientHolders(arguments); + return new CustomBlastingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java index fd3220981..007381419 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java @@ -1,13 +1,12 @@ package net.momirealms.craftengine.core.item.recipe; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Map; +import java.util.Set; public class CustomCampfireRecipe extends CustomCookingRecipe { public static final Factory FACTORY = new Factory<>(); @@ -21,33 +20,16 @@ public class CustomCampfireRecipe extends CustomCookingRecipe { return RecipeTypes.CAMPFIRE_COOKING; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { - CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80)); - float experience = MiscUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f)); - List items = MiscUtils.getAsStringList(arguments.get("ingredient")); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); - } - } - return new CustomCampfireRecipe( - id, - recipeCategory, - group, - Ingredient.of(holders), - cookingTime, - experience, - parseResult(arguments) - ); + int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); + float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); + Set> holders = ingredientHolders(arguments); + return new CustomCampfireRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index d5e4b7877..de8f7e4f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; @@ -133,29 +134,25 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { List pattern = MiscUtils.getAsStringList(arguments.get("pattern")); if (pattern.isEmpty()) { - throw new IllegalArgumentException("pattern cannot be empty"); + throw new LocalizedResourceConfigException("warning.config.recipe.shaped.missing_pattern"); } if (!validatePattern(pattern)) { - throw new IllegalArgumentException("Invalid pattern: " + pattern); + throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_pattern", pattern.toString()); } - Map ingredientMap = MiscUtils.castToMap(arguments.get("ingredients"), true); - if (ingredientMap == null) { - throw new IllegalArgumentException("ingredients cannot be empty"); - } - CraftingRecipeCategory recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + Object ingredientObj = getIngredientOrThrow(arguments); String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; Map> ingredients = new HashMap<>(); - for (Map.Entry entry : ingredientMap.entrySet()) { + for (Map.Entry entry : MiscUtils.castToMap(ingredientObj, false).entrySet()) { String key = entry.getKey(); if (key.length() != 1) { - throw new IllegalArgumentException("Invalid key: " + key); + throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_symbol", key); } char ch = key.charAt(0); List items = MiscUtils.getAsStringList(entry.getValue()); @@ -164,18 +161,13 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { if (item.charAt(0) == '#') { holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); } } ingredients.put(ch, Ingredient.of(holders)); } - return new CustomShapedRecipe( - id, - recipeCategory, - group, - new Pattern<>(pattern.toArray(new String[0]), ingredients), - parseResult(arguments) - ); + return new CustomShapedRecipe(id, craftingRecipeCategory(arguments), group, new Pattern<>(pattern.toArray(new String[0]), ingredients), parseResult(arguments)); } private boolean validatePattern(List pattern) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index f0bb3cc87..2f71fd2d4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; @@ -52,37 +53,65 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { return RecipeTypes.SHAPELESS; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { - Map ingredientMap = MiscUtils.castToMap(arguments.get("ingredients"), true); - if (ingredientMap == null) { - throw new IllegalArgumentException("ingredients cannot be empty"); - } - CraftingRecipeCategory recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; List> ingredients = new ArrayList<>(); - for (Map.Entry entry : ingredientMap.entrySet()) { - List items = MiscUtils.getAsStringList(entry.getValue()); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); + Object ingredientsObject = getIngredientOrThrow(arguments); + if (ingredientsObject instanceof Map map) { + for (Map.Entry entry : (MiscUtils.castToMap(map, false)).entrySet()) { + List items = MiscUtils.getAsStringList(entry.getValue()); + Set> holders = new HashSet<>(); + for (String item : items) { + if (item.charAt(0) == '#') { + holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); + } else { + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); + } } + ingredients.add(Ingredient.of(holders)); + } + } else if (ingredientsObject instanceof List list) { + for (Object obj : list) { + if (obj instanceof List inner) { + Set> holders = new HashSet<>(); + for (String item : MiscUtils.getAsStringList(inner)) { + if (item.charAt(0) == '#') { + holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); + } else { + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); + } + } + ingredients.add(Ingredient.of(holders)); + } else { + String item = obj.toString(); + Set> holders = new HashSet<>(); + if (item.charAt(0) == '#') { + holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); + } else { + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); + } + ingredients.add(Ingredient.of(holders)); + } + } + } else { + String item = ingredientsObject.toString(); + Set> holders = new HashSet<>(); + if (item.charAt(0) == '#') { + holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); + } else { + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); } ingredients.add(Ingredient.of(holders)); } - return new CustomShapelessRecipe( - id, - recipeCategory, - group, - ingredients, - parseResult(arguments) - ); + return new CustomShapelessRecipe(id, craftingRecipeCategory(arguments), group, ingredients, parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java index 7f4fed503..0a667cdff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java @@ -1,13 +1,12 @@ package net.momirealms.craftengine.core.item.recipe; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Map; +import java.util.Set; public class CustomSmeltingRecipe extends CustomCookingRecipe { public static final Factory FACTORY = new Factory<>(); @@ -21,33 +20,16 @@ public class CustomSmeltingRecipe extends CustomCookingRecipe { return RecipeTypes.SMELTING; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { - CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80)); - float experience = MiscUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f)); - List items = MiscUtils.getAsStringList(arguments.get("ingredient")); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); - } - } - return new CustomSmeltingRecipe( - id, - recipeCategory, - group, - Ingredient.of(holders), - cookingTime, - experience, - parseResult(arguments) - ); + int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); + float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); + Set> holders = ingredientHolders(arguments); + return new CustomSmeltingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 51dabc68d..1742fcc19 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; @@ -149,7 +150,8 @@ public class CustomSmithingTransformRecipe implements Recipe { if (item.charAt(0) == '#') { holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); + holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", item))); } } return holders.isEmpty() ? null : Ingredient.of(holders); @@ -180,12 +182,12 @@ public class CustomSmithingTransformRecipe implements Recipe { public static ItemDataProcessor fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("processor type cannot be null"); + throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.missing_type"); } Key key = Key.withDefaultNamespace(type, "craftengine"); ItemDataProcessor.Factory factory = BuiltInRegistries.SMITHING_RESULT_PROCESSOR_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown processor type: " + type); + throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.invalid_type", type); } return factory.create(map); } @@ -233,7 +235,11 @@ public class CustomSmithingTransformRecipe implements Recipe { @Override public ItemDataProcessor create(Map arguments) { - List components = MiscUtils.getAsStringList(arguments.get("components")); + Object componentsObj = arguments.get("components"); + if (componentsObj == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components"); + } + List components = MiscUtils.getAsStringList(componentsObj); return new KeepComponents(components.stream().map(Key::of).toList()); } } @@ -266,7 +272,11 @@ public class CustomSmithingTransformRecipe implements Recipe { @Override public ItemDataProcessor create(Map arguments) { - List tags = MiscUtils.getAsStringList(arguments.get("tags")); + Object tagsObj = arguments.get("tags"); + if (tagsObj == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags"); + } + List tags = MiscUtils.getAsStringList(tagsObj); return new KeepTags(tags.stream().map(it -> it.split("\\.")).toList()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java index 2976836e0..9054a2b02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java @@ -1,13 +1,12 @@ package net.momirealms.craftengine.core.item.recipe; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Map; +import java.util.Set; public class CustomSmokingRecipe extends CustomCookingRecipe { public static final Factory FACTORY = new Factory<>(); @@ -21,33 +20,16 @@ public class CustomSmokingRecipe extends CustomCookingRecipe { return RecipeTypes.SMOKING; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { - CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80)); - float experience = MiscUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f)); - List items = MiscUtils.getAsStringList(arguments.get("ingredient")); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); - } - } - return new CustomSmokingRecipe( - id, - recipeCategory, - group, - Ingredient.of(holders), - cookingTime, - experience, - parseResult(arguments) - ); + int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); + float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); + Set> holders = ingredientHolders(arguments); + return new CustomSmokingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java index bc0cbc901..1d9b4b528 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java @@ -2,14 +2,10 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import org.jetbrains.annotations.NotNull; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,27 +39,14 @@ public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { return ingredient; } - public static class Factory implements RecipeFactory { + public static class Factory extends AbstractRecipeFactory { @SuppressWarnings({"DuplicatedCode"}) @Override public Recipe create(Key id, Map arguments) { String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - List items = MiscUtils.getAsStringList(arguments.get("ingredient")); - Set> holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item))); - } - } - return new CustomStoneCuttingRecipe<>( - id, - group, - Ingredient.of(holders), - parseResult(arguments) - ); + Set> holders = ingredientHolders(arguments); + return new CustomStoneCuttingRecipe<>(id, group, Ingredient.of(holders), parseResult(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java index e6092042f..63c75b959 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java @@ -1,8 +1,10 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -14,15 +16,13 @@ public interface RecipeFactory { default CustomRecipeResult parseResult(Map arguments) { Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); if (resultMap == null) { - throw new IllegalArgumentException("result cannot be empty"); + throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); } - String id = (String) resultMap.get("id"); - if (id == null) { - throw new IllegalArgumentException("result.id cannot be empty"); - } - int count = MiscUtils.getAsInt(resultMap.getOrDefault("count", 1)); + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); + int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); return new CustomRecipeResult( - CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new IllegalArgumentException("Unknown recipe result item id: " + id)), + CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow( + () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_item", id)), count ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java index 4f9b7a047..8d716b772 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.item.recipe; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -39,14 +41,11 @@ public class RecipeTypes { @SuppressWarnings("unchecked") public static Recipe fromMap(Key id, Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("recipe type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.recipe.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); RecipeFactory factory = (RecipeFactory) BuiltInRegistries.RECIPE_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown recipe type: " + type); + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_type", type); } return factory.create(id, map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java index 0af5630ca..e8b2176ff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java @@ -10,7 +10,9 @@ import net.momirealms.craftengine.core.loot.function.LootFunction; import net.momirealms.craftengine.core.loot.function.LootFunctions; import net.momirealms.craftengine.core.loot.number.NumberProvider; import net.momirealms.craftengine.core.loot.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.World; import org.jetbrains.annotations.Nullable; @@ -22,6 +24,7 @@ import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Supplier; public class LootTable { private final List> pools; @@ -38,25 +41,40 @@ public class LootTable { @SuppressWarnings("unchecked") public static LootTable fromMap(Map map) { if (map == null || map.isEmpty()) return null; - List> poolList = (List>) map.get("pools"); + Object pools = ResourceConfigUtils.requireNonNullOrThrow(map.get("pools"), "warning.config.loot_table.missing_pools"); + if (!(pools instanceof List list) || list.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.loot_table.invalid_pools_type", pools.getClass().getSimpleName()); + } + List poolList = (List) map.get("pools"); List> lootPools = new ArrayList<>(); - for (Map pool : poolList) { - NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1)); - NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0)); - List conditions = Optional.ofNullable(pool.get("conditions")) - .map(it -> LootConditions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'conditions' should be a map list, current type: " + it.getClass().getSimpleName())))) - .orElse(Lists.newArrayList()); - List> containers = Optional.ofNullable(pool.get("entries")) - .map(it -> (List>) new ArrayList>(LootEntryContainers.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'entries' should be a map list, current type: " + it.getClass().getSimpleName()))))) - .orElse(Lists.newArrayList()); - List> functions = Optional.ofNullable(pool.get("functions")) - .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName()))))) - .orElse(Lists.newArrayList()); - lootPools.add(new LootPool<>(containers, conditions, functions, rolls, bonus_rolls)); + for (Object rawPool : poolList) { + if (rawPool instanceof Map rawPoolMap) { + Map pool = MiscUtils.castToMap(rawPoolMap, false); + NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1)); + NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0)); + List conditions = Optional.ofNullable(pool.get("conditions")) + .map(it -> LootConditions.fromMapList(castToMapListOrThrow(it, + () -> new LocalizedResourceConfigException("warning.config.loot_table.invalid_conditions_type", it.getClass().getSimpleName())))) + .orElse(Lists.newArrayList()); + List> containers = Optional.ofNullable(pool.get("entries")) + .map(it -> (List>) new ArrayList>(LootEntryContainers.fromMapList(castToMapListOrThrow(it, + () -> new LocalizedResourceConfigException("warning.config.loot_table.invalid_entries_type", it.getClass().getSimpleName()))))) + .orElse(Lists.newArrayList()); + List> functions = Optional.ofNullable(pool.get("functions")) + .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(castToMapListOrThrow(it, + () -> new LocalizedResourceConfigException("warning.config.loot_table.invalid_functions_type", it.getClass().getSimpleName()))))) + .orElse(Lists.newArrayList()); + lootPools.add(new LootPool<>(containers, conditions, functions, rolls, bonus_rolls)); + } else if (rawPool instanceof String string) { + LootPool lootPool = readFlatFormatLootPool(string); + if (lootPool != null) + lootPools.add(lootPool); + } } return new LootTable<>(lootPools, Optional.ofNullable(map.get("functions")) - .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName()))))) + .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(castToMapListOrThrow(it, + () -> new LocalizedResourceConfigException("warning.config.loot_table.invalid_functions_type", it.getClass().getSimpleName()))))) .orElse(Lists.newArrayList()) ); } @@ -105,4 +123,18 @@ public class LootTable { pool.addRandomItems(consumer, context); } } + + // TODO https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/loot-system/flat-format + public static LootPool readFlatFormatLootPool(String pool) { + return null; + } + + @SuppressWarnings("unchecked") + private static List> castToMapListOrThrow(Object obj, Supplier exceptionSupplier) { + if (obj instanceof List list) { + return (List>) list; + } else { + throw exceptionSupplier.get(); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java index 7fac12892..fd8f55e2b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.loot.condition; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; @@ -89,14 +90,13 @@ public class LootConditions { public static LootCondition fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("condition type cannot be null"); + throw new LocalizedResourceConfigException("warning.config.loot_table.condition.missing_type"); } Key key = Key.withDefaultNamespace(type, "craftengine"); LootConditionFactory factory = BuiltInRegistries.LOOT_CONDITION_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown loot condition type: " + type); + throw new LocalizedResourceConfigException("warning.config.loot_table.condition.invalid_type", type); } return factory.create(map); } - } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java index 547daf8ed..50cb43f1e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java @@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.loot.condition; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -28,7 +28,7 @@ public class RandomCondition implements LootCondition { public static class Factory implements LootConditionFactory { @Override public LootCondition create(Map arguments) { - float chance = MiscUtils.getAsFloat(arguments.getOrDefault("value", 0.5f)); + float chance = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("value", 0.5f), "value"); return new RandomCondition(chance); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/TableBonusCondition.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/TableBonusCondition.java index 8560f80bd..125d5859d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/TableBonusCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/TableBonusCondition.java @@ -4,9 +4,11 @@ import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.loot.parameter.LootParameters; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,9 +39,24 @@ public class TableBonusCondition implements LootCondition { public static class Factory implements LootConditionFactory { @Override public LootCondition create(Map arguments) { - Key enchantmentType = Key.of((String) arguments.get("enchantment")); - List floats = MiscUtils.getAsFloatList(arguments.get("chances")); - return new TableBonusCondition(enchantmentType, floats); + Object enchantmentObj = arguments.get("enchantment"); + if (enchantmentObj == null) { + throw new LocalizedResourceConfigException("warning.config.loot_table.condition.table_bonus.missing_enchantment"); + } + Key enchantmentType = Key.of(enchantmentObj.toString()); + Object chances = arguments.get("chances"); + if (chances != null) { + if (chances instanceof Number number) { + return new TableBonusCondition(enchantmentType, List.of(number.floatValue())); + } else if (chances instanceof List list) { + List values = new ArrayList<>(list.size()); + for (Object o : list) { + values.add(ResourceConfigUtils.getAsFloat(o, "chances")); + } + return new TableBonusCondition(enchantmentType, values); + } + } + throw new LocalizedResourceConfigException("warning.config.loot_table.condition.table_bonus.missing_chances"); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java index 30eeb35c6..41d0d5de8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java @@ -7,6 +7,7 @@ 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.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Collections; import java.util.List; @@ -43,10 +44,7 @@ public class ExpLootEntryContainer extends AbstractLootEntryContainer { @SuppressWarnings("unchecked") @Override public LootEntryContainer create(Map arguments) { - Object value = arguments.get("count"); - if (value == null) { - throw new IllegalArgumentException("count can not be null"); - } + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.loot_table.entry.exp.missing_count"); List conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) .orElse(Collections.emptyList()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java index 4c11af995..b9f9ce3e0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.loot.entry; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.ArrayList; @@ -39,14 +41,11 @@ public class LootEntryContainers { @SuppressWarnings("unchecked") public static LootEntryContainer fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("loot entry type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.loot_table.entry.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); LootEntryContainerFactory factory = (LootEntryContainerFactory) BuiltInRegistries.LOOT_ENTRY_CONTAINER_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown loot entry type: " + type); + throw new LocalizedResourceConfigException("warning.config.loot_table.entry.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java index 845d275e0..28830b557 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunctions; 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.ResourceConfigUtils; import java.util.*; import java.util.function.Consumer; @@ -34,7 +35,7 @@ public class SingleItemLootEntryContainer extends AbstractSingleLootEntryCont if (tItem != null) { lootConsumer.accept(tItem); } else { - CraftEngine.instance().logger().warn("Failed to create item: " + this.item); + CraftEngine.instance().logger().warn("Failed to create item: " + this.item + " as loots. Please check if this item exists."); } } @@ -42,9 +43,10 @@ public class SingleItemLootEntryContainer extends AbstractSingleLootEntryCont @SuppressWarnings("unchecked") @Override public LootEntryContainer create(Map arguments) { - Key item = Key.from((String) arguments.get("item")); - int weight = (int) arguments.getOrDefault("weight", 1); - int quality = (int) arguments.getOrDefault("quality", 0); + String itemObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.loot_table.entry.item.missing_item"); + Key item = Key.from(itemObj); + int weight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("weight", 1), "weight"); + int quality = ResourceConfigUtils.getAsInt(arguments.getOrDefault("quality", 0), "quality"); List conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) .orElse(Collections.emptyList()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index 180e6c100..3e55a198f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -6,14 +6,12 @@ import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.loot.condition.LootCondition; import net.momirealms.craftengine.core.loot.condition.LootConditions; import net.momirealms.craftengine.core.loot.parameter.LootParameters; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.RandomUtils; -import net.momirealms.craftengine.core.util.ResourceKey; +import net.momirealms.craftengine.core.util.*; import java.util.Collections; import java.util.List; @@ -51,13 +49,10 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< @SuppressWarnings("unchecked") @Override public LootFunction create(Map arguments) { - String enchantment = (String) arguments.get("enchantment"); - if (enchantment == null || enchantment.isEmpty()) { - throw new IllegalArgumentException("enchantment is required"); - } + String enchantment = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("enchantment"), "warning.config.loot_table.function.apply_bonus.missing_enchantment"); Map formulaMap = MiscUtils.castToMap(arguments.get("formula"), true); if (formulaMap == null) { - throw new IllegalArgumentException("formula is required"); + throw new LocalizedResourceConfigException("warning.config.loot_table.function.apply_bonus.missing_formula"); } List conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) @@ -166,8 +161,8 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< @Override public Formula create(Map arguments) { - int extra = MiscUtils.getAsInt(arguments.getOrDefault("extra", 1)); - float probability = MiscUtils.getAsFloat(arguments.getOrDefault("probability", 0.5f)); + int extra = ResourceConfigUtils.getAsInt(arguments.getOrDefault("extra", 1), "extra"); + float probability = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("probability", 0.5f), "probability"); return new CropDrops(extra, probability); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java index ef68a68bf..cecd00e2e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java @@ -8,6 +8,7 @@ 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.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Collections; import java.util.List; @@ -39,10 +40,7 @@ public class DropExpFunction extends AbstractLootConditionalFunction { @SuppressWarnings("unchecked") @Override public LootFunction create(Map arguments) { - Object value = arguments.get("count"); - if (value == null) { - throw new IllegalArgumentException("count can not be null"); - } + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.loot_table.function.drop_exp.missing_count"); List conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) .orElse(Collections.emptyList()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java index 0ab815f53..17a88c0c7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java @@ -2,11 +2,13 @@ package net.momirealms.craftengine.core.loot.function; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.ArrayList; @@ -67,14 +69,11 @@ public class LootFunctions { @SuppressWarnings("unchecked") public static LootFunction fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("function type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.loot_table.function.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); LootFunctionFactory factory = (LootFunctionFactory) BuiltInRegistries.LOOT_FUNCTION_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown function type: " + type); + throw new LocalizedResourceConfigException("warning.config.loot_table.function.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/SetCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/SetCountFunction.java index b2ae864ff..969c778f9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/SetCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/SetCountFunction.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.loot.condition.LootConditions; import net.momirealms.craftengine.core.loot.number.NumberProvider; import net.momirealms.craftengine.core.loot.number.NumberProviders; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Collections; import java.util.List; @@ -41,7 +42,7 @@ public class SetCountFunction extends AbstractLootConditionalFunction { @SuppressWarnings("unchecked") @Override public LootFunction create(Map arguments) { - Object value = arguments.get("count"); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.loot_table.function.set_count.missing_count"); boolean add = (boolean) arguments.getOrDefault("add", false); List conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/number/NumberProviders.java b/core/src/main/java/net/momirealms/craftengine/core/loot/number/NumberProviders.java index 45ec409e9..32af65b44 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/number/NumberProviders.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/number/NumberProviders.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.loot.number; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.ArrayList; @@ -36,14 +38,11 @@ public class NumberProviders { } public static NumberProvider fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("number type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.loot_table.number.missing_type"); Key key = Key.withDefaultNamespace(type, "craftengine"); NumberProviderFactory factory = BuiltInRegistries.NUMBER_PROVIDER_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown number type: " + type); + throw new LocalizedResourceConfigException("warning.config.loot_table.number.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 79cfe4596..75cf89e13 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -22,6 +22,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; import net.momirealms.craftengine.core.plugin.locale.I18NData; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; @@ -130,16 +131,6 @@ public abstract class AbstractPackManager implements PackManager { } } - private void loadInternalPng(String path, Consumer callback) { - try (InputStream inputStream = this.plugin.resourceStream(path)) { - if (inputStream != null) { - callback.accept(inputStream.readAllBytes()); - } - } catch (IOException e) { - this.plugin.logger().warn("Failed to load " + path, e); - } - } - @Override public Path resourcePackPath() { return this.plugin.dataFolderPath() @@ -157,6 +148,10 @@ public abstract class AbstractPackManager implements PackManager { // we might add multiple host methods in future versions this.resourcePackHost = ResourcePackHosts.fromMap(MiscUtils.castToMap(list.get(0), false)); } catch (LocalizedException e) { + if (e instanceof LocalizedResourceConfigException exception) { + exception.setPath(plugin.dataFolderPath().resolve("config.yml")); + e.setArgument(1, "hosting"); + } TranslationManager.instance().log(e.node(), e.arguments()); this.resourcePackHost = NoneHost.INSTANCE; } @@ -426,7 +421,7 @@ public abstract class AbstractPackManager implements PackManager { for (Path path : files.right()) { try (InputStreamReader inputStream = new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8)) { Map dataRaw = GsonHelper.get().fromJson(JsonParser.parseReader(inputStream).getAsJsonObject(), Map.class); - Map data = MiscUtils.castToMap(dataRaw, false); + Map data = castToMap(dataRaw, false); for (Map.Entry entry : data.entrySet()) { processConfigEntry(entry, path, pack); } @@ -445,20 +440,29 @@ public abstract class AbstractPackManager implements PackManager { String key = configEntry.getKey(); try { Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); - 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 configSection1 = castToMap(configSection0, false); - if ((boolean) configSection1.getOrDefault("enable", true)) { - parser.parseSection(cached.pack(), cached.filePath(), id, plugin.templateManager().applyTemplates(configSection1)); + try { + 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 configSection1 = castToMap(configSection0, false); + if ((boolean) configSection1.getOrDefault("enable", true)) { + parser.parseSection(cached.pack(), cached.filePath(), id, plugin.templateManager().applyTemplates(configSection1)); + } + } else { + TranslationManager.instance().log("warning.config.structure.not_section", cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName()); } - } else { - this.plugin.logger().warn(cached.filePath(), "Configuration section is required for " + parser.sectionId()[0] + "." + configEntry.getKey() + " - "); } + } catch (LocalizedException e) { + if (e instanceof LocalizedResourceConfigException exception) { + exception.setPath(cached.filePath()); + exception.setId(cached.prefix() + "." + key); + } + TranslationManager.instance().log(e.node(), e.arguments()); + this.plugin.debug(e::node); } } catch (Exception e) { - this.plugin.logger().warn(cached.filePath(), "Error loading " + parser.sectionId()[0] + "." + key, e); + this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "." + key + "'. Please find the cause according to the stacktrace or seek developer help.", e); } } } @@ -475,7 +479,7 @@ public abstract class AbstractPackManager implements PackManager { 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))); + .add(new CachedConfig(key, castToMap(typeSections0, false), path, pack))); } } @@ -499,15 +503,18 @@ public abstract class AbstractPackManager implements PackManager { List folders = new ArrayList<>(); folders.addAll(loadedPacks().stream().filter(Pack::enabled).map(Pack::resourcePackFolder).toList()); folders.addAll(Config.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList()); - List>> duplicated = mergeFolder(folders, generatedPackPath); if (!duplicated.isEmpty()) { - this.plugin.logger().severe("Duplicated files Found. Please resolve them through config.yml resource-pack.duplicated-files-handler."); + plugin.logger().severe(AdventureHelper.miniMessage().stripTags(TranslationManager.instance().miniMessageTranslation("warning.config.pack.duplicated_files"))); + int x = 1; for (Pair> path : duplicated) { - this.plugin.logger().warn(""); - this.plugin.logger().warn("Target: " + path.left()); - for (Path path0 : path.right()) { - this.plugin.logger().warn(" - " + path0.toAbsolutePath()); + this.plugin.logger().warn("[ " + (x++) + " ] " + path.left()); + for (int i = 0, size = path.right().size(); i < size; i++) { + if (i == size - 1) { + this.plugin.logger().info(" └ " + path.right().get(i).toAbsolutePath()); + } else { + this.plugin.logger().info(" ├ " + path.right().get(i).toAbsolutePath()); + } } } } @@ -917,9 +924,10 @@ public abstract class AbstractPackManager implements PackManager { } } - 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()); - } + // TODO it later +// if (Config.packMinVersion() < 21.19f && has) { +// plugin.logger().warn("You are using 'item-model' component for some models which requires 1.21.2+ client. But the min-supported-version set in 'config.yml' is " + "1." + Config.packMinVersion()); +// } } private void generateModernItemModels1_21_4(Path generatedPackPath) { @@ -1138,7 +1146,7 @@ public abstract class AbstractPackManager implements PackManager { if (Files.exists(sourceFolder)) { Files.walkFileTree(sourceFolder, new SimpleFileVisitor<>() { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { Path relative = sourceFolder.relativize(file); Path targetPath = targetFolder.resolve(relative); List conflicts = conflictChecker.computeIfAbsent(relative, k -> new ArrayList<>()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java index 808c649f0..e98faf8f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java @@ -6,12 +6,14 @@ import java.util.Map; public class CachedConfig { private final Pack pack; private final Path filePath; + private final String prefix; private final Map config; - public CachedConfig(Map config, Path filePath, Pack pack) { + public CachedConfig(String prefix, Map config, Path filePath, Pack pack) { this.config = config; this.filePath = filePath; this.pack = pack; + this.prefix = prefix; } public Map config() { @@ -25,4 +27,8 @@ public class CachedConfig { public Pack pack() { return pack; } + + public String prefix() { + return prefix; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AllOfPathMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AllOfPathMatcher.java index e71d62d3e..a353f7591 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AllOfPathMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AllOfPathMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.nio.file.Path; import java.util.List; @@ -34,8 +36,16 @@ public class AllOfPathMatcher implements PathMatcher { @SuppressWarnings("unchecked") @Override public PathMatcher create(Map arguments) { - List> terms = (List>) arguments.get("terms"); - return new AllOfPathMatcher(PathMatchers.fromMapList(terms)); + Object termsObj = arguments.get("terms"); + if (termsObj instanceof List list) { + List> terms = (List>) list; + return new AllOfPathMatcher(PathMatchers.fromMapList(terms)); + } else if (termsObj instanceof Map) { + Map terms = MiscUtils.castToMap(termsObj, false); + return new AllOfPathMatcher(PathMatchers.fromMapList(List.of(terms))); + } else { + throw new LocalizedException("warning.config.conflict_matcher.all_of.missing_terms"); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AnyOfPathMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AnyOfPathMatcher.java index 97b1fb1c6..ad0632b19 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AnyOfPathMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/AnyOfPathMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.nio.file.Path; import java.util.List; @@ -34,8 +36,16 @@ public class AnyOfPathMatcher implements PathMatcher { @SuppressWarnings("unchecked") @Override public PathMatcher create(Map arguments) { - List> terms = (List>) arguments.get("terms"); - return new AnyOfPathMatcher(PathMatchers.fromMapList(terms)); + Object termsObj = arguments.get("terms"); + if (termsObj instanceof List list) { + List> terms = (List>) list; + return new AnyOfPathMatcher(PathMatchers.fromMapList(terms)); + } else if (termsObj instanceof Map) { + Map terms = MiscUtils.castToMap(termsObj, false); + return new AnyOfPathMatcher(PathMatchers.fromMapList(List.of(terms))); + } else { + throw new LocalizedException("warning.config.conflict_matcher.any_of.missing_terms"); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ExactPathMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ExactPathMatcher.java index fdff56bcd..0fbdedb4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ExactPathMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ExactPathMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -28,10 +30,7 @@ public class ExactPathMatcher implements PathMatcher { @Override public PathMatcher create(Map arguments) { - String path = (String) arguments.get("path"); - if (path == null) { - throw new IllegalArgumentException("The 'path' argument must not be null"); - } + String path = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("path"), () -> new LocalizedException("warning.config.conflict_matcher.exact.missing_path")); return new ExactPathMatcher(path); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/FilenameMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/FilenameMatcher.java index e52d8a590..9bfac4789 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/FilenameMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/FilenameMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -28,10 +30,7 @@ public class FilenameMatcher implements PathMatcher { @Override public PathMatcher create(Map arguments) { - String name = (String) arguments.get("name"); - if (name == null) { - throw new IllegalArgumentException("The 'name' argument must not be null"); - } + String name = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("name"), () -> new LocalizedException("warning.config.conflict_matcher.filename.missing_name")); return new FilenameMatcher(name); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/InvertedPathMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/InvertedPathMatcher.java index 34055c5a6..f95c47da9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/InvertedPathMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/InvertedPathMatcher.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -25,10 +28,10 @@ public class InvertedPathMatcher implements PathMatcher { public static class Factory implements PathMatcherFactory { - @SuppressWarnings("unchecked") @Override public PathMatcher create(Map arguments) { - Map term = (Map) arguments.get("term"); + Object inverted = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("term"), () -> new LocalizedException("warning.config.conflict_matcher.inverted.missing_term")); + Map term = MiscUtils.castToMap(inverted, false); return new InvertedPathMatcher(PathMatchers.fromMap(term)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathPrefixMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathPrefixMatcher.java index 49d45fa5d..706010712 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathPrefixMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathPrefixMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -30,10 +32,7 @@ public class ParentPathPrefixMatcher implements PathMatcher { @Override public PathMatcher create(Map arguments) { - String prefix = (String) arguments.get("prefix"); - if (prefix == null) { - throw new IllegalArgumentException("The prefix argument must not be null"); - } + String prefix = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("prefix"), () -> new LocalizedException("warning.config.conflict_matcher.parent_prefix.missing_prefix")); return new ParentPathPrefixMatcher(prefix); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathSuffixMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathSuffixMatcher.java index b5728bd52..f31a62c83 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathSuffixMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/ParentPathSuffixMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -30,10 +32,7 @@ public class ParentPathSuffixMatcher implements PathMatcher { @Override public PathMatcher create(Map arguments) { - String suffix = (String) arguments.get("suffix"); - if (suffix == null) { - throw new IllegalArgumentException("The suffix argument must not be null"); - } + String suffix = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("suffix"), () -> new LocalizedException("warning.config.conflict_matcher.parent_suffix.missing_suffix")); return new ParentPathSuffixMatcher(suffix); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathContainsMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathContainsMatcher.java index 2a138c0d9..c8225f57e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathContainsMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathContainsMatcher.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; @@ -28,7 +30,7 @@ public class PathContainsMatcher implements PathMatcher { @Override public PathMatcher create(Map arguments) { - String path = (String) arguments.get("path"); + String path = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("path"), () -> new LocalizedException("warning.config.conflict_matcher.contains.missing_path")); return new PathContainsMatcher(path); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathMatchers.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathMatchers.java index 55d6ba56d..a9de55bfd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathMatchers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathMatchers.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.ArrayList; @@ -48,14 +50,11 @@ public class PathMatchers { } public static PathMatcher fromMap(Map map) { - String type = (String) map.getOrDefault("type", "empty"); - if (type == null) { - throw new NullPointerException("path matcher type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), () -> new LocalizedException("warning.config.conflict_matcher.missing_type")); Key key = Key.withDefaultNamespace(type, "craftengine"); PathMatcherFactory factory = BuiltInRegistries.PATH_MATCHER_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown matcher type: " + type); + throw new LocalizedException("warning.config.conflict_matcher.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathPatternMatcher.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathPatternMatcher.java index 30c9e958f..e469c7a6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathPatternMatcher.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/matcher/PathPatternMatcher.java @@ -1,22 +1,29 @@ package net.momirealms.craftengine.core.pack.conflict.matcher; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.Map; +import java.util.regex.Pattern; public class PathPatternMatcher implements PathMatcher { public static final Factory FACTORY = new Factory(); - private final String pattern; + private final Pattern pattern; public PathPatternMatcher(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + public PathPatternMatcher(Pattern pattern) { this.pattern = pattern; } @Override public boolean test(Path path) { String pathStr = path.toString().replace("\\", "/"); - return pathStr.matches(pattern); + return this.pattern.matcher(pathStr).matches(); } @Override @@ -24,14 +31,15 @@ public class PathPatternMatcher implements PathMatcher { return PathMatchers.PATTERN; } + public Pattern pattern() { + return pattern; + } + public static class Factory implements PathMatcherFactory { @Override public PathMatcher create(Map arguments) { - String pattern = (String) arguments.get("pattern"); - if (pattern == null) { - throw new IllegalArgumentException("The pattern argument must not be null"); - } + String pattern = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), () -> new LocalizedException("warning.config.conflict_matcher.pattern.missing_pattern")); return new PathPatternMatcher(pattern); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergePackMcMetaResolution.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergePackMcMetaResolution.java index a03ed528e..799466cc3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergePackMcMetaResolution.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergePackMcMetaResolution.java @@ -207,7 +207,7 @@ public class MergePackMcMetaResolution implements Resolution { public static class Factory implements ResolutionFactory { @Override public Resolution create(Map arguments) { - String description = (String) arguments.getOrDefault("description", "CraftEngine ResourcePack"); + String description = arguments.getOrDefault("description", "CraftEngine ResourcePack").toString(); return new MergePackMcMetaResolution(description); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java index 9d65cf9cc..38f7754ca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.conflict.resolution; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -30,14 +32,11 @@ public class Resolutions { } public static Resolution fromMap(Map map) { - String type = (String) map.getOrDefault("type", "empty"); - if (type == null) { - throw new NullPointerException("path matcher type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), () -> new LocalizedException("warning.config.conflict_resolution.missing_type")); Key key = Key.withDefaultNamespace(type, "craftengine"); ResolutionFactory factory = BuiltInRegistries.RESOLUTION_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown matcher type: " + type); + throw new LocalizedException("warning.config.conflict_resolution.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHostFactory.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHostFactory.java index 21c8bbf27..323004431 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHostFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHostFactory.java @@ -1,8 +1,35 @@ package net.momirealms.craftengine.core.pack.host; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.net.InetSocketAddress; +import java.net.ProxySelector; import java.util.Map; public interface ResourcePackHostFactory { ResourcePackHost create(Map arguments); + + default ProxySelector getProxySelector(Map proxySetting) { + ProxySelector proxy = ProxySelector.getDefault(); + if (proxySetting != null) { + Object hostObj = proxySetting.get("host"); + if (hostObj == null) { + throw new LocalizedException("warning.config.host.proxy.missing_host", new NullPointerException("'host' should not be null for proxy setting")); + } + String proxyHost = hostObj.toString(); + Object portObj = proxySetting.get("port"); + if (portObj == null) { + throw new LocalizedException("warning.config.host.proxy.missing_port", new NullPointerException("'port' should not be null for proxy setting")); + } + int proxyPort = ResourceConfigUtils.getAsInt(portObj, "port"); + if (proxyHost == null || proxyHost.isEmpty() || proxyPort <= 0 || proxyPort > 65535) { + throw new LocalizedException("warning.config.host.proxy.invalid", proxyHost + ":" + proxyPort); + } else { + proxy = ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort)); + } + } + return proxy; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHosts.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHosts.java index 3db5b1c4a..8421c62c8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHosts.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHosts.java @@ -43,7 +43,7 @@ public class ResourcePackHosts { public static ResourcePackHost fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new LocalizedException("warning.config.host.external.lack_url"); + throw new LocalizedException("warning.config.host.missing_type"); } Key key = Key.withDefaultNamespace(type, "craftengine"); ResourcePackHostFactory factory = BuiltInRegistries.RESOURCE_PACK_HOST_FACTORY.getValue(key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java index 309c30a17..2ae5335c1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java @@ -292,27 +292,21 @@ public class AlistHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String apiUrl = (String) arguments.get("api-url"); - if (apiUrl == null || apiUrl.isEmpty()) { - throw new LocalizedException("warning.config.host.alist.lack_api_url"); - } - String userName = useEnv ? System.getenv("CE_ALIST_USERNAME") : (String) arguments.get("username"); + String apiUrl = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("api-url"), () -> new LocalizedException("warning.config.host.alist.missing_api_url")); + String userName = useEnv ? System.getenv("CE_ALIST_USERNAME") : Optional.ofNullable(arguments.get("username")).map(String::valueOf).orElse(null); if (userName == null || userName.isEmpty()) { - throw new LocalizedException("warning.config.host.alist.lack_username"); + throw new LocalizedException("warning.config.host.alist.missing_username"); } - String password = useEnv ? System.getenv("CE_ALIST_PASSWORD") : (String) arguments.get("password"); + String password = useEnv ? System.getenv("CE_ALIST_PASSWORD") : Optional.ofNullable(arguments.get("password")).map(String::valueOf).orElse(null); if (password == null || password.isEmpty()) { - throw new LocalizedException("warning.config.host.alist.lack_password"); + throw new LocalizedException("warning.config.host.alist.missing_password"); } - String filePassword = useEnv ? System.getenv("CE_ALIST_FILE_PASSWORD") : (String) arguments.getOrDefault("file-password", ""); - String otpCode = (String) arguments.get("otp-code"); + String filePassword = useEnv ? System.getenv("CE_ALIST_FILE_PASSWORD") : arguments.getOrDefault("file-password", "").toString(); + String otpCode = Optional.ofNullable(arguments.get("otp-code")).map(String::valueOf).orElse(null); Duration jwtTokenExpiration = Duration.ofHours((int) arguments.getOrDefault("jwt-token-expiration", 48)); - String uploadPath = (String) arguments.get("upload-path"); - if (uploadPath == null || uploadPath.isEmpty()) { - throw new LocalizedException("warning.config.host.alist.lack_upload_path"); - } + String uploadPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("upload-path"), () -> new LocalizedException("warning.config.host.alist.missing_upload_path")); boolean disableUpload = (boolean) arguments.getOrDefault("disable-upload", false); - ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy")); + ProxySelector proxy = getProxySelector(MiscUtils.castToMap(arguments.get("proxy"), true)); return new AlistHost(apiUrl, userName, password, filePassword, otpCode, jwtTokenExpiration, uploadPath, disableUpload, proxy); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java index 9d4d6c743..091115e16 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java @@ -8,10 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; -import net.momirealms.craftengine.core.util.GsonHelper; -import net.momirealms.craftengine.core.util.HashUtils; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; import java.io.IOException; import java.io.InputStream; @@ -267,23 +264,20 @@ public class DropboxHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String appKey = useEnv ? System.getenv("CE_DROPBOX_APP_KEY") : (String) arguments.get("app-key"); + String appKey = useEnv ? System.getenv("CE_DROPBOX_APP_KEY") : Optional.ofNullable(arguments.get("app-key")).map(String::valueOf).orElse(null); if (appKey == null || appKey.isEmpty()) { - throw new LocalizedException("warning.config.host.dropbox.lack_app_key"); + throw new LocalizedException("warning.config.host.dropbox.missing_app_key"); } - String appSecret = useEnv ? System.getenv("CE_DROPBOX_APP_SECRET") : (String) arguments.get("app-secret"); + String appSecret = useEnv ? System.getenv("CE_DROPBOX_APP_SECRET") : Optional.ofNullable(arguments.get("app-secret")).map(String::valueOf).orElse(null); if (appSecret == null || appSecret.isEmpty()) { - throw new LocalizedException("warning.config.host.dropbox.lack_app_secret"); + throw new LocalizedException("warning.config.host.dropbox.missing_app_secret"); } - String refreshToken = useEnv ? System.getenv("CE_DROPBOX_REFRESH_TOKEN") : (String) arguments.get("refresh-token"); + String refreshToken = useEnv ? System.getenv("CE_DROPBOX_REFRESH_TOKEN") : Optional.ofNullable(arguments.get("refresh-token")).map(String::valueOf).orElse(null); if (refreshToken == null || refreshToken.isEmpty()) { - throw new LocalizedException("warning.config.host.dropbox.lack_refresh_token"); + throw new LocalizedException("warning.config.host.dropbox.missing_refresh_token"); } - String uploadPath = (String) arguments.get("upload-path"); - if (uploadPath == null || uploadPath.isEmpty()) { - throw new LocalizedException("warning.config.host.dropbox.lack_upload_path"); - } - ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy")); + String uploadPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("upload-path"), () -> new LocalizedException("warning.config.host.dropbox.missing_upload_path")); + ProxySelector proxy = getProxySelector(MiscUtils.castToMap(arguments.get("proxy"), true)); return new DropboxHost(appKey, appSecret, refreshToken, "/" + uploadPath, proxy); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java index a57bae861..dec6095cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.util.Key; import java.nio.file.Path; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -45,19 +46,16 @@ public class ExternalHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { - String url = (String) arguments.get("url"); + String url = Optional.ofNullable(arguments.get("url")).map(String::valueOf).orElse(null); if (url == null || url.isEmpty()) { - throw new LocalizedException("warning.config.host.external.lack_url"); + throw new LocalizedException("warning.config.host.external.missing_url"); } - String uuid = (String) arguments.get("uuid"); + String uuid = Optional.ofNullable(arguments.get("uuid")).map(String::valueOf).orElse(null); if (uuid == null || uuid.isEmpty()) { uuid = UUID.nameUUIDFromBytes(url.getBytes()).toString(); } UUID hostUUID = UUID.fromString(uuid); - String sha1 = (String) arguments.get("sha1"); - if (sha1 == null) { - sha1 = ""; - } + String sha1 = arguments.getOrDefault("sha1", "").toString(); return new ExternalHost(new ResourcePackDownloadData(url, hostUUID, sha1)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java index 7f5d6830f..d0c1f9b57 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.HashUtils; import net.momirealms.craftengine.core.util.Key; @@ -176,23 +177,23 @@ public class GitLabHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String gitlabUrl = (String) arguments.get("gitlab-url"); + String gitlabUrl = Optional.ofNullable(arguments.get("gitlab-url")).map(String::valueOf).orElse(null); if (gitlabUrl == null || gitlabUrl.isEmpty()) { - throw new IllegalArgumentException("'gitlab-url' cannot be empty for GitLab host"); + throw new LocalizedException("warning.config.host.gitlab.missing_url"); } if (gitlabUrl.endsWith("/")) { gitlabUrl = gitlabUrl.substring(0, gitlabUrl.length() - 1); } - String accessToken = useEnv ? System.getenv("CE_GITLAB_ACCESS_TOKEN") : (String) arguments.get("access-token"); + String accessToken = useEnv ? System.getenv("CE_GITLAB_ACCESS_TOKEN") : Optional.ofNullable(arguments.get("access-token")).map(String::valueOf).orElse(null); if (accessToken == null || accessToken.isEmpty()) { - throw new IllegalArgumentException("Missing required 'access-token' configuration"); + throw new LocalizedException("warning.config.host.gitlab.missing_token"); } - String projectId = (String) arguments.get("project-id"); + String projectId = Optional.ofNullable(arguments.get("project-id")).map(String::valueOf).orElse(null); if (projectId == null || projectId.isEmpty()) { - throw new IllegalArgumentException("Missing required 'project-id' configuration"); + throw new LocalizedException("warning.config.host.gitlab.missing_project"); } projectId = URLEncoder.encode(projectId, StandardCharsets.UTF_8).replace("/", "%2F"); - ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy")); + ProxySelector proxy = getProxySelector(MiscUtils.castToMap(arguments.get("proxy"), true)); return new GitLabHost(gitlabUrl, accessToken, projectId, proxy); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java index 54023c414..3359d3b8b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java @@ -272,11 +272,11 @@ public class LobFileHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String apiKey = useEnv ? System.getenv("CE_LOBFILE_API_KEY") : (String) arguments.get("api-key"); + String apiKey = useEnv ? System.getenv("CE_LOBFILE_API_KEY") : Optional.ofNullable(arguments.get("api-key")).map(String::valueOf).orElse(null); if (apiKey == null || apiKey.isEmpty()) { - throw new LocalizedException("warning.config.host.lobfile.lack_api_key"); + throw new LocalizedException("warning.config.host.lobfile.missing_api_key"); } - ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy")); + ProxySelector proxy = getProxySelector(MiscUtils.castToMap(arguments.get("proxy"), true)); return new LobFileHost(apiKey, proxy); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java index f1e7dc100..de06dd2b3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java @@ -232,23 +232,23 @@ public class OneDriveHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String clientId = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_ID") : (String) arguments.get("client-id"); + String clientId = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_ID") : Optional.ofNullable(arguments.get("client-id")).map(String::valueOf).orElse(null); if (clientId == null || clientId.isEmpty()) { - throw new LocalizedException("warning.config.host.onedrive.lack_client_id"); + throw new LocalizedException("warning.config.host.onedrive.missing_client_id"); } - String clientSecret = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_SECRET") : (String) arguments.get("client-secret"); + String clientSecret = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_SECRET") : Optional.ofNullable(arguments.get("client-secret")).map(String::valueOf).orElse(null); if (clientSecret == null || clientSecret.isEmpty()) { - throw new LocalizedException("warning.config.host.onedrive.lack_client_secret"); + throw new LocalizedException("warning.config.host.onedrive.missing_client_secret"); } - String refreshToken = useEnv ? System.getenv("CE_ONEDRIVE_REFRESH_TOKEN") : (String) arguments.get("refresh-token"); + String refreshToken = useEnv ? System.getenv("CE_ONEDRIVE_REFRESH_TOKEN") : Optional.ofNullable(arguments.get("refresh-token")).map(String::valueOf).orElse(null); if (refreshToken == null || refreshToken.isEmpty()) { - throw new LocalizedException("warning.config.host.onedrive.lack_refresh_token"); + throw new LocalizedException("warning.config.host.onedrive.missing_refresh_token"); } - String uploadPath = (String) arguments.getOrDefault("upload-path", "resource_pack.zip"); + String uploadPath = arguments.getOrDefault("upload-path", "resource_pack.zip").toString(); if (uploadPath == null || uploadPath.isEmpty()) { - throw new LocalizedException("warning.config.host.onedrive.lack_upload_path"); + throw new LocalizedException("warning.config.host.onedrive.missing_upload_path"); } - ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy")); + ProxySelector proxy = getProxySelector(MiscUtils.castToMap(arguments.get("proxy"), true)); return new OneDriveHost(clientId, clientSecret, refreshToken, uploadPath, proxy); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/S3Host.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/S3Host.java index 176d5cbe7..21f20a97d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/S3Host.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/S3Host.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.HashUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.auth.signer.AwsS3V4Signer; @@ -31,10 +32,7 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -158,28 +156,28 @@ public class S3Host implements ResourcePackHost { @SuppressWarnings("deprecation") public ResourcePackHost create(Map arguments) { boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false); - String endpoint = (String) arguments.get("endpoint"); + String endpoint = Optional.ofNullable(arguments.get("endpoint")).map(String::valueOf).orElse(null); if (endpoint == null || endpoint.isEmpty()) { - throw new LocalizedException("warning.config.host.s3.lack_endpoint"); + throw new LocalizedException("warning.config.host.s3.missing_endpoint"); } - String protocol = (String) arguments.getOrDefault("protocol", "https"); + String protocol = arguments.getOrDefault("protocol", "https").toString(); boolean usePathStyle = (boolean) arguments.getOrDefault("path-style", false); - String bucket = (String) arguments.get("bucket"); + String bucket = Optional.ofNullable(arguments.get("bucket")).map(String::valueOf).orElse(null); if (bucket == null || bucket.isEmpty()) { - throw new LocalizedException("warning.config.host.s3.lack_bucket"); + throw new LocalizedException("warning.config.host.s3.missing_bucket"); } - String region = (String) arguments.getOrDefault("region", "auto"); - String accessKeyId = useEnv ? System.getenv("CE_S3_ACCESS_KEY_ID") : (String) arguments.get("access-key-id"); + String region = arguments.getOrDefault("region", "auto").toString(); + String accessKeyId = useEnv ? System.getenv("CE_S3_ACCESS_KEY_ID") : Optional.ofNullable(arguments.get("access-key-id")).map(String::valueOf).orElse(null); if (accessKeyId == null || accessKeyId.isEmpty()) { - throw new LocalizedException("warning.config.host.s3.lack_access_key_id"); + throw new LocalizedException("warning.config.host.s3.missing_access_key"); } - String accessKeySecret = useEnv ? System.getenv("CE_S3_ACCESS_KEY_SECRET") : (String) arguments.get("access-key-secret"); + String accessKeySecret = useEnv ? System.getenv("CE_S3_ACCESS_KEY_SECRET") : Optional.ofNullable(arguments.get("access-key-secret")).map(String::valueOf).orElse(null); if (accessKeySecret == null || accessKeySecret.isEmpty()) { - throw new LocalizedException("warning.config.host.s3.lack_access_key_secret"); + throw new LocalizedException("warning.config.host.s3.missing_secret"); } - String uploadPath = (String) arguments.getOrDefault("upload-path", "craftengine/resource_pack.zip"); + String uploadPath = arguments.getOrDefault("upload-path", "craftengine/resource_pack.zip").toString(); if (uploadPath == null || uploadPath.isEmpty()) { - throw new LocalizedException("warning.config.host.s3.lack_upload_path"); + throw new LocalizedException("warning.config.host.s3.missing_upload_path"); } boolean useLegacySignature = (boolean) arguments.getOrDefault("use-legacy-signature", true); Duration validity = Duration.ofSeconds((int) arguments.getOrDefault("validity", 10)); @@ -188,8 +186,8 @@ public class S3Host implements ResourcePackHost { String cdnDomain = null; String cdnProtocol = "https"; if (cdn != null) { - cdnDomain = (String) cdn.get("domain"); - cdnProtocol = (String) cdn.getOrDefault("protocol", "https"); + cdnDomain = Optional.ofNullable(arguments.get("domain")).map(String::valueOf).orElse(null); + cdnProtocol = arguments.getOrDefault("protocol", "https").toString(); } AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKeyId, accessKeySecret); @@ -208,14 +206,14 @@ public class S3Host implements ResourcePackHost { Map proxySetting = MiscUtils.castToMap(arguments.get("proxy"), true); if (proxySetting != null) { - String host = (String) proxySetting.get("host"); - int port = (Integer) proxySetting.get("port"); - String scheme = (String) proxySetting.get("scheme"); - String username = (String) proxySetting.get("username"); - String password = (String) proxySetting.get("password"); - if (host == null || host.isEmpty() || port <= 0 || port > 65535 || scheme == null || scheme.isEmpty()) { - throw new IllegalArgumentException("Invalid proxy configuration"); + String host = ResourceConfigUtils.requireNonEmptyStringOrThrow(proxySetting.get("host"), () -> new LocalizedException("warning.config.host.proxy.missing_host")); + int port = ResourceConfigUtils.getAsInt(proxySetting.get("port"), "port"); + if (port <= 0 || port > 65535) { + throw new LocalizedException("warning.config.host.proxy.missing_port"); } + String scheme = ResourceConfigUtils.requireNonEmptyStringOrThrow(proxySetting.get("scheme"), () -> new LocalizedException("warning.config.host.proxy.missing_scheme")); + String username = Optional.ofNullable(proxySetting.get("username")).map(String::valueOf).orElse(null); + String password = Optional.ofNullable(proxySetting.get("password")).map(String::valueOf).orElse(null); ProxyConfiguration.Builder builder = ProxyConfiguration.builder().host(host).port(port).scheme(scheme); if (username != null) builder.username(username); if (password != null) builder.password(password); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java index 90ca54755..de38d7771 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.nio.file.Path; import java.util.List; @@ -60,23 +61,20 @@ public class SelfHost implements ResourcePackHost { @Override public ResourcePackHost create(Map arguments) { SelfHostHttpServer selfHostHttpServer = SelfHostHttpServer.instance(); - String ip = (String) arguments.get("ip"); - if (ip == null) { - throw new LocalizedException("warning.config.host.self.lack_ip"); - } - int port = MiscUtils.getAsInt(arguments.getOrDefault("port", 8163)); - if (port < 0 || port > 65535) { + String ip = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("ip"), () -> new LocalizedException("warning.config.host.self.missing_ip")); + int port = ResourceConfigUtils.getAsInt(arguments.getOrDefault("port", 8163), "port"); + if (port <= 0 || port > 65535) { throw new LocalizedException("warning.config.host.self.invalid_port", String.valueOf(port)); } boolean oneTimeToken = (boolean) arguments.getOrDefault("one-time-token", true); - String protocol = (String) arguments.getOrDefault("protocol", "http"); + String protocol = arguments.getOrDefault("protocol", "http").toString(); boolean denyNonMinecraftRequest = (boolean) arguments.getOrDefault("deny-non-minecraft-request", true); Map rateMap = MiscUtils.castToMap(arguments.get("rate-map"), true); int maxRequests = 5; int resetInterval = 20_000; if (rateMap != null) { - maxRequests = MiscUtils.getAsInt(rateMap.getOrDefault("max-requests", 5)); - resetInterval = MiscUtils.getAsInt(rateMap.getOrDefault("reset-interval", 20)) * 1000; + maxRequests = ResourceConfigUtils.getAsInt(rateMap.getOrDefault("max-requests", 5), "max-requests"); + resetInterval = ResourceConfigUtils.getAsInt(rateMap.getOrDefault("reset-interval", 20), "reset-interval") * 1000; } selfHostHttpServer.updateProperties(ip, port, denyNonMinecraftRequest, protocol, maxRequests, resetInterval, oneTimeToken); return INSTANCE; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java index 7b85ef0d8..4b2609c3b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java @@ -2,17 +2,19 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.tint.Tint; import net.momirealms.craftengine.core.pack.model.tint.Tints; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; public class BaseItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); @@ -68,7 +70,10 @@ public class BaseItemModel implements ItemModel { @SuppressWarnings("unchecked") @Override public ItemModel create(Map arguments) { - String modelPath = Objects.requireNonNull(arguments.get("path"), "'path' is required for 'minecraft:model'. Current arguments: "+ arguments).toString(); + String modelPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("path"), "warning.config.item.model.base.missing_path"); + if (!ResourceLocation.isValid(modelPath)) { + throw new LocalizedResourceConfigException("warning.config.item.model.base.invalid_path", modelPath); + } Map generation = MiscUtils.castToMap(arguments.get("generation"), true); ModelGeneration modelGeneration = null; if (generation != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java index 58487f35e..7d9d97af0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java @@ -3,7 +3,9 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; import java.util.List; @@ -52,15 +54,22 @@ public class CompositeItemModel implements ItemModel { @SuppressWarnings("unchecked") @Override public ItemModel create(Map arguments) { - List> models = (List>) arguments.get("models"); - if (models == null || models.isEmpty()) { - throw new IllegalArgumentException("No 'models' specified for 'minecraft:composite'"); + Object m = arguments.get("models"); + if (m instanceof List list) { + List> models = (List>) list; + if (models.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.item.model.composite.missing_models"); + } + List modelList = new ArrayList<>(); + for (Map model : models) { + modelList.add(ItemModels.fromMap(model)); + } + return new CompositeItemModel(modelList); + } else if (m instanceof Map map) { + return new CompositeItemModel(List.of(ItemModels.fromMap(MiscUtils.castToMap(map, false)))); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.composite.missing_models"); } - List modelList = new ArrayList<>(); - for (Map model : models) { - modelList.add(ItemModels.fromMap(model)); - } - return new CompositeItemModel(modelList); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java index f30d9878f..cb51e65a8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java @@ -4,12 +4,13 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.condition.ConditionProperties; import net.momirealms.craftengine.core.pack.model.condition.ConditionProperty; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; public class ConditionItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); @@ -60,13 +61,22 @@ public class ConditionItemModel implements ItemModel { public static class Factory implements ItemModelFactory { - @SuppressWarnings("unchecked") @Override public ItemModel create(Map arguments) { ConditionProperty property = ConditionProperties.fromMap(arguments); - Map onTrue = Objects.requireNonNull((Map) arguments.get("on-true"), "No 'on-true' set for 'minecraft:condition'"); - Map onFalse = Objects.requireNonNull((Map) arguments.get("on-false"), "No 'on-false' set for 'minecraft:condition'"); - return new ConditionItemModel(property, ItemModels.fromMap(onTrue), ItemModels.fromMap(onFalse)); + ItemModel onTrue; + if (arguments.get("on-true") instanceof Map map1) { + onTrue = ItemModels.fromMap(MiscUtils.castToMap(map1, false)); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.condition.missing_on_true"); + } + ItemModel onFalse; + if (arguments.get("on-false") instanceof Map map2) { + onFalse = ItemModels.fromMap(MiscUtils.castToMap(map2, false)); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.condition.missing_on_false"); + } + return new ConditionItemModel(property, onTrue, onFalse); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index 37df55401..b96282128 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; @@ -35,11 +36,11 @@ public class ItemModels { } public static ItemModel fromMap(Map map) { - String type = (String) map.getOrDefault("type", "minecraft:model"); + String type = map.getOrDefault("type", "minecraft:model").toString(); Key key = Key.withDefaultNamespace(type, "minecraft"); ItemModelFactory factory = BuiltInRegistries.ITEM_MODEL_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown model type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java index 1efe99241..90372bd90 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java @@ -5,8 +5,10 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchProperties; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchProperty; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -96,19 +98,28 @@ public class RangeDispatchItemModel implements ItemModel { @Override public ItemModel create(Map arguments) { RangeDispatchProperty property = RangeDispatchProperties.fromMap(arguments); - float scale = MiscUtils.getAsFloat(arguments.getOrDefault("scale", 1.0)); + float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1.0), "scale"); Map fallback = MiscUtils.castToMap(arguments.get("fallback"), true); - List> entries = (List>) arguments.get("entries"); - if (entries != null && !entries.isEmpty()) { - Map entryMap = new HashMap<>(); - for (Map entry : entries) { - float threshold = MiscUtils.getAsFloat(entry.getOrDefault("threshold", 1)); - Map model = MiscUtils.castToMap(entry.getOrDefault("model", fallback), false); - entryMap.put(threshold, ItemModels.fromMap(model)); + Object entriesObj = arguments.get("entries"); + if (entriesObj instanceof List list) { + List> entries = (List>) list; + if (!entries.isEmpty()) { + Map entryMap = new HashMap<>(); + for (Map entry : entries) { + float threshold = ResourceConfigUtils.getAsFloat(entry.getOrDefault("threshold", 1), "threshold"); + Object model = entry.getOrDefault("model", fallback); + if (model == null) { + throw new LocalizedResourceConfigException("warning.config.item.model.range_dispatch.entry.missing_model"); + } + entryMap.put(threshold, ItemModels.fromMap(MiscUtils.castToMap(model, false))); + } + return new RangeDispatchItemModel(property, scale, fallback == null ? null : ItemModels.fromMap(fallback), entryMap); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.range_dispatch.missing_entries"); } - return new RangeDispatchItemModel(property, scale, fallback == null ? null : ItemModels.fromMap(fallback), entryMap); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.range_dispatch.missing_entries"); } - throw new IllegalArgumentException("No 'entries' set for 'minecraft:range_dispatch'"); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java index 38ed5829a..f57f30efb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java @@ -5,6 +5,7 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.select.SelectProperties; import net.momirealms.craftengine.core.pack.model.select.SelectProperty; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import org.incendo.cloud.type.Either; @@ -96,28 +97,39 @@ public class SelectItemModel implements ItemModel { public ItemModel create(Map arguments) { SelectProperty property = SelectProperties.fromMap(arguments); Map fallback = MiscUtils.castToMap(arguments.get("fallback"), true); - List> cases = (List>) arguments.get("cases"); - if (cases != null && !cases.isEmpty()) { - Map>, ItemModel> whenMap = new HashMap<>(); - for (Map c : cases) { - Object when = c.get("when"); - if (when == null) continue; - Either> either; - if (when instanceof List whenList) { - List whens = new ArrayList<>(whenList.size()); - for (Object o : whenList) { - whens.add(o.toString()); + Object casesObj = arguments.get("cases"); + if (casesObj instanceof List list) { + List> cases = (List>) list; + if (!cases.isEmpty()) { + Map>, ItemModel> whenMap = new HashMap<>(); + for (Map c : cases) { + Object when = c.get("when"); + if (when == null) { + throw new LocalizedResourceConfigException("warning.config.item.model.select.case.missing_when"); } - either = Either.ofFallback(whens); - } else { - either = Either.ofPrimary(when.toString()); + Either> either; + if (when instanceof List whenList) { + List whens = new ArrayList<>(whenList.size()); + for (Object o : whenList) { + whens.add(o.toString()); + } + either = Either.ofFallback(whens); + } else { + either = Either.ofPrimary(when.toString()); + } + Object model = c.get("model"); + if (model == null) { + throw new LocalizedResourceConfigException("warning.config.item.model.select.case.missing_model"); + } + whenMap.put(either, ItemModels.fromMap(MiscUtils.castToMap(model, false))); } - Map model = MiscUtils.castToMap(c.get("model"), false); - whenMap.put(either, ItemModels.fromMap(model)); + return new SelectItemModel(property, whenMap, fallback == null ? null : ItemModels.fromMap(fallback)); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.select.missing_cases"); } - return new SelectItemModel(property, whenMap, fallback == null ? null : ItemModels.fromMap(fallback)); + } else { + throw new LocalizedResourceConfigException("warning.config.item.model.select.missing_cases"); } - throw new IllegalArgumentException("No 'cases' set for 'minecraft:select'"); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index c0e1e83e6..ccb36b529 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.model.condition; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -45,14 +47,11 @@ public class ConditionProperties { } public static ConditionProperty fromMap(Map map) { - String type = (String) map.get("property"); - if (type == null) { - throw new NullPointerException("property type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.condition.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); ConditionPropertyFactory factory = BuiltInRegistries.CONDITION_PROPERTY_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown property type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.condition.invalid_property", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java index 3cfea5b88..205301496 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.pack.model.condition; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -30,7 +30,7 @@ public class CustomModelDataConditionProperty implements ConditionProperty { @Override public ConditionProperty create(Map arguments) { - int index = MiscUtils.getAsInt(arguments.getOrDefault("index", 0)); + int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataConditionProperty(index); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java index 790e8b87d..7da522b40 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.condition; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class HasComponentConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); @@ -35,8 +35,8 @@ public class HasComponentConditionProperty implements ConditionProperty { @Override public ConditionProperty create(Map arguments) { boolean ignoreDefault = (boolean) arguments.getOrDefault("ignore-default", false); - String component = Objects.requireNonNull(arguments.get("component"), "component").toString(); - return new HasComponentConditionProperty(component, ignoreDefault); + String componentObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("component"), "warning.config.item.model.condition.has_component.missing_component"); + return new HasComponentConditionProperty(componentObj, ignoreDefault); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java index 19df014e9..12b099843 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.condition; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class KeyBindDownConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); @@ -29,8 +29,8 @@ public class KeyBindDownConditionProperty implements ConditionProperty { @Override public ConditionProperty create(Map arguments) { - String keybind = Objects.requireNonNull(arguments.get("keybind"), "keybind").toString(); - return new KeyBindDownConditionProperty(keybind); + String keybindObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("keybind"), "warning.config.item.model.condition.keybind.missing"); + return new KeyBindDownConditionProperty(keybindObj); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java index 90ce13fca..e34e9f374 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java @@ -2,10 +2,9 @@ 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,21 +27,20 @@ public abstract class AbstractModelGenerator implements ModelGenerator { this.modelsToGenerate.clear(); } - public void prepareModelGeneration(Path path, Key id, ModelGeneration model) { + public void prepareModelGeneration(ModelGeneration model) { ModelGeneration conflict = this.modelsToGenerate.get(model.path()); if (conflict != null) { if (conflict.equals(model)) { return; } - TranslationManager.instance().log("warning.config.model.generation.conflict", path.toString(), id.toString(), model.path().toString()); - return; + throw new LocalizedResourceConfigException("warning.config.model.generation.conflict", model.path().toString()); } if (!ResourceLocation.isValid(model.parentModelPath())) { - TranslationManager.instance().log("warning.config.model.generation.parent.invalid_resource_location", path.toString(), id.toString(), model.parentModelPath()); + throw new LocalizedResourceConfigException("warning.config.model.generation.parent.invalid", model.parentModelPath()); } for (Map.Entry 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()); + throw new LocalizedResourceConfigException("warning.config.model.generation.texture.invalid", texture.getKey(), texture.getValue()); } } this.modelsToGenerate.put(model.path(), model); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java index 51a7d16fc..086d8c6d5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.pack.model.generation; import com.google.gson.JsonObject; -import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -21,25 +21,13 @@ public class ModelGeneration { this.texturesOverride = texturesOverride; } - public ModelGeneration(Key path, Section section) { - this.path = path; - this.parentModelPath = Objects.requireNonNull(section.getString("parent")); - Section texturesSection = section.getSection("textures"); - if (texturesSection != null) { - this.texturesOverride = new LinkedHashMap<>(); - for (Map.Entry entry : texturesSection.getStringRouteMappedValues(false).entrySet()) { - if (entry.getValue() instanceof String p) { - this.texturesOverride.put(entry.getKey(), p); - } - } - } else { - this.texturesOverride = Collections.emptyMap(); - } - } - public ModelGeneration(Key path, Map map) { this.path = path; - this.parentModelPath = Objects.requireNonNull((String) map.get("parent")); + Object parent = map.get("parent"); + if (parent == null) { + throw new LocalizedResourceConfigException("warning.config.model.generation.missing_parent"); + } + this.parentModelPath = parent.toString(); Map texturesMap = MiscUtils.castToMap(map.get("textures"), true); if (texturesMap != null) { this.texturesOverride = new LinkedHashMap<>(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java index 0481ace29..e1008a576 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class CompassRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); @@ -29,8 +29,8 @@ public class CompassRangeDispatchProperty implements RangeDispatchProperty { @Override public RangeDispatchProperty create(Map arguments) { - String target = Objects.requireNonNull(arguments.get("target")).toString(); - return new CompassRangeDispatchProperty(target); + String targetObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("target"), "warning.config.item.model.range_dispatch.compass.missing_target"); + return new CompassRangeDispatchProperty(targetObj); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java index 52a0aab71..7c7b51031 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.LegacyModelPredicate; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -40,7 +40,7 @@ public class CustomModelDataRangeDispatchProperty implements RangeDispatchProper @Override public RangeDispatchProperty create(Map arguments) { - int index = MiscUtils.getAsInt(arguments.getOrDefault("index", 0)); + int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataRangeDispatchProperty(index); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index 087a3acaa..cafb43f05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -41,14 +43,11 @@ public class RangeDispatchProperties { } public static RangeDispatchProperty fromMap(Map map) { - String type = (String) map.get("property"); - if (type == null) { - throw new NullPointerException("property type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.range_dispatch.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); RangeDispatchPropertyFactory factory = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown property type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.range_dispatch.invalid_property", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java index de8bc007f..3cd818c4a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class TimeRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); @@ -34,9 +34,9 @@ public class TimeRangeDispatchProperty implements RangeDispatchProperty { @Override public RangeDispatchProperty create(Map arguments) { - String source = Objects.requireNonNull(arguments.get("source")).toString(); + String sourceObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("source"), "warning.config.item.model.range_dispatch.time.missing_source"); boolean wobble = (boolean) arguments.getOrDefault("wobble", true); - return new TimeRangeDispatchProperty(source, wobble); + return new TimeRangeDispatchProperty(sourceObj, wobble); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java index ff2e71104..972bcbbed 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -29,7 +29,7 @@ public class UseCycleRangeDispatchProperty implements RangeDispatchProperty { @Override public RangeDispatchProperty create(Map arguments) { - int period = MiscUtils.getAsInt(arguments.getOrDefault("period", 0)); + int period = ResourceConfigUtils.getAsInt(arguments.getOrDefault("period", 0), "period"); return new UseCycleRangeDispatchProperty(period); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java index 92c67a2a2..bb89446eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.select; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class BlockStateSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); @@ -29,8 +29,8 @@ public class BlockStateSelectProperty implements SelectProperty { @Override public SelectProperty create(Map arguments) { - String blockStateProperty = Objects.requireNonNull(arguments.get("block-state-property")).toString(); - return new BlockStateSelectProperty(blockStateProperty); + String property = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("block-state-property"), "warning.config.item.model.select.block_state.missing_property"); + return new BlockStateSelectProperty(property); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java index 600f50e5b..cafd0e00a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.pack.model.select; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -29,7 +29,7 @@ public class CustomModelDataSelectProperty implements SelectProperty { @Override public SelectProperty create(Map arguments) { - int index = MiscUtils.getAsInt(arguments.getOrDefault("index", 0)); + int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataSelectProperty(index); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java index e89314e55..7de594b79 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java @@ -2,11 +2,11 @@ package net.momirealms.craftengine.core.pack.model.select; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; -import java.util.Objects; public class LocalTimeSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); @@ -43,7 +43,7 @@ public class LocalTimeSelectProperty implements SelectProperty { @Override public SelectProperty create(Map arguments) { - String pattern = Objects.requireNonNull(arguments.get("pattern")).toString(); + String pattern = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.item.model.select.local_time.missing_pattern"); String locale = (String) arguments.get("locale"); String timeZone = (String) arguments.get("time-zone"); return new LocalTimeSelectProperty(pattern, locale, timeZone); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index bd01a1d6a..ee01af5d5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.model.select; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -39,14 +41,11 @@ public class SelectProperties { } public static SelectProperty fromMap(Map map) { - String type = (String) map.get("property"); - if (type == null) { - throw new NullPointerException("property type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.select.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); SelectPropertyFactory factory = BuiltInRegistries.SELECT_PROPERTY_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown property type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.select.invalid_property", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java index 847ad6c0d..d8f27dc8c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class BannerSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -31,7 +31,7 @@ public class BannerSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { - String color = Objects.requireNonNull(arguments.get("color"), "color").toString(); + String color = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("color"), "warning.config.item.model.special.banner.missing_color"); return new BannerSpecialModel(color); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java index 318f18252..4b2e60004 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class BedSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -31,7 +31,7 @@ public class BedSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { - String color = Objects.requireNonNull(arguments.get("texture"), "texture").toString(); + String color = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.bed.missing_texture"); return new BedSpecialModel(color); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java index 7e1511e0f..31aaf4563 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java @@ -1,11 +1,11 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class ChestSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -35,10 +35,10 @@ public class ChestSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { - float openness = MiscUtils.getAsFloat(arguments.getOrDefault("openness", 0)); - String texture = Objects.requireNonNull(arguments.get("texture"), "texture").toString(); + float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); + String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.chest.missing_texture"); if (openness > 1 || openness < 0) { - throw new IllegalArgumentException("Invalid openness: " + openness + ". Valid range 0~1"); + throw new LocalizedResourceConfigException("warning.config.item.model.special.chest.invalid_openness", String.valueOf(openness)); } return new ChestSpecialModel(texture, openness); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java index 680b7cda2..6f1cf530c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java @@ -2,10 +2,9 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class HeadSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -38,9 +37,9 @@ public class HeadSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { - String kind = Objects.requireNonNull(arguments.get("kind"), "kind").toString(); - String texture = Objects.requireNonNull(arguments.get("texture"), "texture").toString(); - int animation = MiscUtils.getAsInt(arguments.getOrDefault("animation", 0)); + String kind = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("kind"), "warning.config.item.model.special.head.missing_kind"); + String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.head.missing_texture"); + int animation = ResourceConfigUtils.getAsInt(arguments.getOrDefault("animation", 0), "animation"); return new HeadSpecialModel(kind, texture, animation); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java index bc80c2693..0eaf640a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java @@ -1,13 +1,13 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Locale; import java.util.Map; -import java.util.Objects; public class ShulkerBoxSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -40,11 +40,11 @@ public class ShulkerBoxSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { - float openness = MiscUtils.getAsFloat(arguments.getOrDefault("openness", 0)); - String texture = Objects.requireNonNull(arguments.get("texture"), "texture").toString(); + float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); + String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.shulker_box.missing_texture"); Direction orientation = Direction.valueOf(arguments.getOrDefault("orientation", "down").toString().toUpperCase(Locale.ENGLISH)); if (openness > 1 || openness < 0) { - throw new IllegalArgumentException("Invalid openness: " + openness + ". Valid range 0~1"); + throw new LocalizedResourceConfigException("warning.config.item.model.special.shulker_box.invalid_openness", String.valueOf(openness)); } return new ShulkerBoxSpecialModel(texture, openness, orientation); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index 2779c7807..9f64bab5e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; -import java.util.Objects; public class SignSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -37,8 +37,8 @@ public class SignSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); - String woodType = Objects.requireNonNull(arguments.get("wood-type"), "wood-type").toString(); - String texture = Objects.requireNonNull(arguments.get("texture"), "texture").toString(); + String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type"); + String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture"); return new SignSpecialModel(type, woodType, texture); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index 40ab72ed4..7856d5907 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.model.special; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -42,14 +44,11 @@ public class SpecialModels { } public static SpecialModel fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("special model type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.model.special.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); SpecialModelFactory factory = BuiltInRegistries.SPECIAL_MODEL_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown special model type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.special.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java index a34494783..e1b926ae6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.pack.model.tint; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.incendo.cloud.type.Either; import java.util.List; @@ -32,7 +33,7 @@ public class ConstantTint implements Tint { @Override public Tint create(Map arguments) { - Object value = arguments.get("value"); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("value"), "warning.config.item.model.tint.constant.missing_value"); return new ConstantTint(parseTintValue(value)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java index f524bd1af..c76be9f9e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.pack.model.tint; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.incendo.cloud.type.Either; import java.util.List; @@ -37,8 +37,8 @@ public class CustomModelDataTint implements Tint { @Override public Tint create(Map arguments) { - Object value = arguments.get("default"); - int index = MiscUtils.getAsInt(arguments.getOrDefault("index", 0)); + Object value = arguments.getOrDefault("default", 0); + int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataTint(parseTintValue(value), index); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java index 562b351e4..d579c0c43 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java @@ -1,8 +1,9 @@ package net.momirealms.craftengine.core.pack.model.tint; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -34,13 +35,13 @@ public class GrassTint implements Tint { @Override public Tint create(Map arguments) { - float temperature = MiscUtils.getAsFloat(arguments.getOrDefault("temperature", 0)); - float downfall = MiscUtils.getAsFloat(arguments.getOrDefault("downfall", 0)); + float temperature = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("temperature", 0), "temperature"); + float downfall = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("downfall", 0), "downfall"); if (temperature > 1 || temperature < 0) { - throw new IllegalArgumentException("Invalid temperature: " + temperature + ". Valid range 0~1"); + throw new LocalizedResourceConfigException("warning.config.item.model.tint.grass.invalid_temp", String.valueOf(temperature)); } if (downfall > 1 || downfall < 0) { - throw new IllegalArgumentException("Invalid downfall: " + downfall + ". Valid range 0~1"); + throw new LocalizedResourceConfigException("warning.config.item.model.tint.grass.invalid_downfall", String.valueOf(downfall)); } return new GrassTint(temperature, downfall); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java index 1b055abe3..a08c0daec 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java @@ -34,7 +34,7 @@ public class SimpleDefaultTint implements Tint { @Override public Tint create(Map arguments) { - Object value = arguments.get("default"); + Object value = arguments.getOrDefault("default", 0); Key type = Key.of(arguments.get("type").toString()); return new SimpleDefaultTint(parseTintValue(value), type); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java index e7dd6c16e..ee595ad49 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.tint; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import org.incendo.cloud.type.Either; import java.util.ArrayList; @@ -14,25 +15,23 @@ public interface TintFactory { if (value instanceof Number i) { return Either.ofPrimary(i.intValue()); } else if (value instanceof List list) { - if (list.size() != 3) { - throw new IllegalArgumentException("Invalid tint value list size: " + list.size() + " which is expected to be 3"); + if (list.size() == 3) { + List intList = new ArrayList<>(); + for (Object o : list) { + intList.add(Integer.parseInt(o.toString())); + } + return Either.ofFallback(intList); } - List intList = new ArrayList<>(); - for (Object o : list) { - intList.add(Integer.parseInt(o.toString())); - } - return Either.ofFallback(intList); } else if (value instanceof String s) { String[] split = s.split(","); - if (split.length != 3) { - throw new IllegalArgumentException("Invalid tint value list size: " + split.length + " which is expected to be 3"); + if (split.length == 3) { + List intList = new ArrayList<>(); + for (String string : split) { + intList.add(Integer.parseInt(string)); + } + return Either.ofFallback(intList); } - List intList = new ArrayList<>(); - for (String string : split) { - intList.add(Integer.parseInt(string)); - } - return Either.ofFallback(intList); } - throw new IllegalArgumentException("Invalid tint value: " + value); + throw new LocalizedResourceConfigException("warning.config.item.model.tint.invalid_value", value.toString()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index 24e0f3cda..07f9172dc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.pack.model.tint; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; @@ -37,14 +39,11 @@ public class Tints { } public static Tint fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { - throw new NullPointerException("tint type cannot be null"); - } + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.model.tint.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); TintFactory factory = BuiltInRegistries.TINT_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown tint type: " + type); + throw new LocalizedResourceConfigException("warning.config.item.model.tint.invalid_type", type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index a1020c555..e823d35e4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.entity.furniture.ColliderType; import net.momirealms.craftengine.core.pack.conflict.resolution.ConditionalResolution; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.PluginProperties; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -99,6 +100,7 @@ public class Config { protected boolean chunk_system$restore_custom_blocks_on_chunk_load; protected boolean chunk_system$sync_custom_blocks_on_chunk_load; protected int chunk_system$delay_serialization; + protected boolean chunk_system$fast_paletted_injection; protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable; protected Map furniture$handle_invalid_furniture_on_chunk_load$mapping; @@ -245,8 +247,11 @@ public class Config { Map args = MiscUtils.castToMap(it, false); return ConditionalResolution.FACTORY.create(args); }).toList(); + } catch (LocalizedResourceConfigException e) { + TranslationManager.instance().log(e.node(), e.arguments()); + resource_pack$duplicated_files_handler = List.of(); } catch (Exception e) { - this.plugin.logger().warn("Failed to load resource pack duplicated files handler", e); + this.plugin.logger().warn("Failed to load resource-pack.duplicated-files-handler", e); resource_pack$duplicated_files_handler = List.of(); } @@ -267,6 +272,7 @@ public class Config { chunk_system$restore_custom_blocks_on_chunk_load = config.getBoolean("chunk-system.restore-custom-blocks-on-chunk-load", true); chunk_system$sync_custom_blocks_on_chunk_load = config.getBoolean("chunk-system.sync-custom-blocks-on-chunk-load", false); chunk_system$delay_serialization = config.getInt("chunk-system.delay-serialization", 20); + chunk_system$fast_paletted_injection = config.getBoolean("chunk-system.fast-palette-injection", false); // furniture furniture$handle_invalid_furniture_on_chunk_load$enable = config.getBoolean("furniture.handle-invalid-furniture-on-chunk-load.enable", false); @@ -689,6 +695,10 @@ public class Config { return instance.chunk_system$delay_serialization; } + public static boolean fastPaletteInjection() { + return instance.chunk_system$fast_paletted_injection; + } + public YamlDocument loadOrCreateYamlData(String fileName) { File file = new File(this.plugin.dataFolderFile(), fileName); if (!file.exists()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java index 3b5f9ef92..0c7f94f94 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.plugin.config; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; @@ -11,7 +12,7 @@ public interface ConfigSectionParser extends Comparable { String[] sectionId(); - void parseSection(Pack pack, Path path, Key id, Map section); + void parseSection(Pack pack, Path path, Key id, Map section) throws LocalizedException; int loadingSequence(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java index 2b5daca92..0ed041519 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java @@ -1,10 +1,11 @@ package net.momirealms.craftengine.core.plugin.config.template; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import java.util.List; import java.util.Map; +import java.util.function.Supplier; public class ListTemplateArgument implements TemplateArgument { public static final Factory FACTORY = new Factory(); @@ -28,7 +29,16 @@ public class ListTemplateArgument implements TemplateArgument { @Override public TemplateArgument create(Map arguments) { - return new ListTemplateArgument(MiscUtils.castToList(arguments.getOrDefault("list", List.of()), false)); + Object list = arguments.getOrDefault("list", List.of()); + return new ListTemplateArgument(castToListOrThrow(list, () -> new LocalizedResourceConfigException("warning.config.template.argument.list.invalid_type", list.getClass().getSimpleName()))); + } + + @SuppressWarnings("unchecked") + private static List castToListOrThrow(Object obj, Supplier throwableSupplier) { + if (obj instanceof List list) { + return (List) list; + } + throw throwableSupplier.get(); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java index 89dab33a5..d99690814 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java @@ -1,7 +1,8 @@ package net.momirealms.craftengine.core.plugin.config.template; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -44,9 +45,9 @@ public class SelfIncreaseIntTemplateArgument implements TemplateArgument { public static class Factory implements TemplateArgumentFactory { @Override public TemplateArgument create(Map arguments) { - int from = MiscUtils.getAsInt(arguments.get("from")); - int to = MiscUtils.getAsInt(arguments.get("to")); - if (from > to) throw new IllegalArgumentException("from > to"); + int from = ResourceConfigUtils.getAsInt(arguments.get("from"), "from"); + int to = ResourceConfigUtils.getAsInt(arguments.get("to"), "to"); + if (from > to) throw new LocalizedResourceConfigException("warning.config.template.argument.self_increase_int.invalid_range", String.valueOf(from), String.valueOf(to)); return new SelfIncreaseIntTemplateArgument(from, to); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 11ca0db20..080dfa9ba 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.config.template; 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.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -15,8 +15,6 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.regex.Matcher; -import static net.momirealms.craftengine.core.util.MiscUtils.castToMap; - public class TemplateManagerImpl implements TemplateManager { private final Map templates = new HashMap<>(); private final static Set NON_TEMPLATE_KEY = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES)); @@ -63,8 +61,7 @@ public class TemplateManagerImpl implements TemplateManager { @Override public void addTemplate(Pack pack, Path path, Key id, Object obj) { if (this.templates.containsKey(id)) { - TranslationManager.instance().log("warning.config.template.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString()); } this.templates.put(id, obj); } @@ -184,7 +181,7 @@ public class TemplateManagerImpl implements TemplateManager { } // 将本节点下的参数与父参数合并 Map arguments = mergeArguments( - castToMap(input.getOrDefault(ARGUMENTS, Collections.emptyMap()), false), + MiscUtils.castToMap(input.getOrDefault(ARGUMENTS, Collections.emptyMap()), false), parentArguments ); // 对overrides参数应用 本节点 + 父节点 参数 diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index b8d61c3d7..6bed70f13 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -15,6 +15,7 @@ import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.util.context.PlayerContext; @@ -110,7 +111,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { String name = section.getOrDefault("name", id).toString(); List members = MiscUtils.getAsStringList(section.getOrDefault("list", List.of())); Key icon = Key.of(section.getOrDefault("icon", ItemKeys.STONE).toString()); - int priority = MiscUtils.getAsInt(section.getOrDefault("priority", 0)); + int priority = ResourceConfigUtils.getAsInt(section.getOrDefault("priority", 0), "priority"); Category category = new Category(id, name, MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())), icon, members.stream().distinct().toList(), priority, (boolean) section.getOrDefault("hidden", false)); if (ItemBrowserManagerImpl.this.byId.containsKey(id)) { ItemBrowserManagerImpl.this.byId.get(id).merge(category); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedException.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedException.java index 87c4dc8a3..39eee44b0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedException.java @@ -1,20 +1,94 @@ package net.momirealms.craftengine.core.plugin.locale; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.ArrayUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; + public class LocalizedException extends RuntimeException { private final String node; - private final String[] arguments; + private String[] arguments; - public LocalizedException(String node, String... arguments) { - super(node); + public LocalizedException( + @NotNull String node, + @Nullable Exception cause, + @Nullable String... arguments + ) { + super(node, cause); this.node = node; - this.arguments = arguments; + this.arguments = arguments != null + ? Arrays.copyOf(arguments, arguments.length) + : new String[0]; + } + + public LocalizedException(@NotNull String node, @Nullable String... arguments) { + this(node, (Exception) null, arguments); + } + + public LocalizedException( + @NotNull String node, + @Nullable String[] prefixArgs, + @Nullable String... suffixArgs + ) { + this( + node, + (Exception) null, + ArrayUtils.merge( + prefixArgs != null ? prefixArgs : new String[0], + suffixArgs != null ? suffixArgs : new String[0] + ) + ); } public String[] arguments() { - return arguments; + return Arrays.copyOf(arguments, arguments.length); } - public String node() { + public void setArgument(int index, @NotNull String argument) { + if (index < 0 || index >= arguments.length) { + throw new IndexOutOfBoundsException("Invalid argument index: " + index); + } + this.arguments[index] = argument; + } + + public void appendHeadArgument(@NotNull String argument) { + this.arguments = ArrayUtils.appendElementToArrayHead(arguments, argument); + } + + public void appendTailArgument(@NotNull String argument) { + this.arguments = ArrayUtils.appendElementToArrayTail(arguments, argument); + } + + public @NotNull String node() { return node; } -} + + @Override + public String getMessage() { + return generateLocalizedMessage(); + } + + private String generateLocalizedMessage() { + try { + String rawMessage = TranslationManager.instance() + .miniMessageTranslation(this.node); + String cleanMessage = AdventureHelper.miniMessage() + .stripTags(rawMessage); + for (int i = 0; i < arguments.length; i++) { + cleanMessage = cleanMessage.replace( + "", + arguments[i] != null ? arguments[i] : "null" + ); + } + return cleanMessage; + } catch (Exception e) { + return String.format( + "Failed to translate. Node: %s, Arguments: %s", + node, + Arrays.toString(arguments) + ); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java new file mode 100644 index 000000000..994e4c785 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java @@ -0,0 +1,43 @@ +package net.momirealms.craftengine.core.plugin.locale; + +import net.momirealms.craftengine.core.util.ArrayUtils; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; + +public class LocalizedResourceConfigException extends LocalizedException { + + public LocalizedResourceConfigException( + @NotNull String node, + @Nullable Exception cause, + @Nullable String... arguments + ) { + super(node, cause, ArrayUtils.merge(new String[] {"null", "null"}, arguments)); + } + + public LocalizedResourceConfigException( + @NotNull String node, + @Nullable String... arguments + ) { + super(node, (Exception) null, ArrayUtils.merge(new String[] {"null", "null"}, arguments)); + } + + public LocalizedResourceConfigException( + @NotNull String node, + @NotNull Path path, + @NotNull Key id, + @Nullable String... arguments + ) { + super(node, (Exception) null, ArrayUtils.merge(new String[] {path.toString(), id.toString()}, arguments)); + } + + public void setPath(Path path) { + super.setArgument(0, path.toString()); + } + + public void setId(String id) { + super.setArgument(1, id); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index a3cb5abff..1d540278e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -226,7 +226,7 @@ public class TranslationManagerImpl implements TranslationManager { @Override public void log(String id, String... args) { String translation = miniMessageTranslation(id); - if (translation == null) translation = id; + if (translation == null || translation.isEmpty()) translation = id; this.plugin.senderFactory().console().sendMessage(AdventureHelper.miniMessage().deserialize(translation, new IndexedArgumentTag(Arrays.stream(args).map(Component::text).toList()))); } @@ -294,8 +294,7 @@ public class TranslationManagerImpl implements TranslationManager { public void parseSection(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Map section) { Locale locale = TranslationManager.parseLocale(id.value()); if (locale == null) { - log("warning.config.i18n.unknown_locale", path.toString(), id.value()); - return; + throw new LocalizedResourceConfigException("warning.config.i18n.unknown_locale", path, id); } Map bundle = new HashMap<>(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index 38ce5197e..789aa65d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -5,11 +5,8 @@ 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.ConfigSectionParser; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; -import net.momirealms.craftengine.core.util.AdventureHelper; -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.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.*; import java.nio.file.Path; import java.util.*; @@ -78,18 +75,13 @@ public abstract class AbstractSoundManager implements SoundManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (AbstractSoundManager.this.songs.containsKey(id)) { - TranslationManager.instance().log("warning.config.jukebox_song.duplicated", path.toString(), id.toString()); - return; - } - String sound = (String) section.get("sound"); - if (sound == null) { - AbstractSoundManager.this.plugin.logger().warn(path, "No sound specified"); - return; + throw new LocalizedResourceConfigException("warning.config.jukebox_song.duplicate"); } + String sound = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("sound"), "warning.config.jukebox_song.missing_sound"); Component description = AdventureHelper.miniMessage().deserialize(section.getOrDefault("description", "").toString()); - float length = MiscUtils.getAsFloat(section.get("length")); - int comparatorOutput = MiscUtils.getAsInt(section.getOrDefault("comparator-output", 15)); - JukeboxSong song = new JukeboxSong(Key.of(sound), description, length, comparatorOutput, MiscUtils.getAsFloat(section.getOrDefault("range", 32f))); + float length = ResourceConfigUtils.getAsFloat(section.get("length"), "length"); + int comparatorOutput = ResourceConfigUtils.getAsInt(section.getOrDefault("comparator-output", 15), "comparator-output"); + JukeboxSong song = new JukeboxSong(Key.of(sound), description, length, comparatorOutput, ResourceConfigUtils.getAsFloat(section.getOrDefault("range", 32f), "range")); AbstractSoundManager.this.songs.put(id, song); } } @@ -110,12 +102,11 @@ public abstract class AbstractSoundManager implements SoundManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { if (AbstractSoundManager.this.byId.containsKey(id)) { - TranslationManager.instance().log("warning.config.sound.duplicated", path.toString(), id.toString()); - return; + throw new LocalizedResourceConfigException("warning.config.sound.duplicate"); } boolean replace = (boolean) section.getOrDefault("replace", false); String subtitle = (String) section.get("subtitle"); - List soundList = (List) section.get("sounds"); + List soundList = (List) ResourceConfigUtils.requireNonNullOrThrow(section.get("sounds"), "warning.config.sound.missing_sounds"); List sounds = new ArrayList<>(); for (Object sound : soundList) { if (sound instanceof String soundPath) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/Sound.java b/core/src/main/java/net/momirealms/craftengine/core/sound/Sound.java index df6de9576..c33b5b114 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/Sound.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/Sound.java @@ -3,7 +3,8 @@ package net.momirealms.craftengine.core.sound; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.HashMap; import java.util.Map; @@ -50,13 +51,11 @@ public interface Sound extends Supplier { } public static SoundFile fromMap(Map map) { - String name = (String) map.get("name"); - if (name == null) throw new IllegalArgumentException("Missing 'name' for sound"); - Builder builder = file(name); + Object name = map.get("name"); + if (name == null) throw new LocalizedResourceConfigException("warning.config.sound.missing_name"); + Builder builder = file(name.toString()); for (Map.Entry entry : map.entrySet()) { - Optional.ofNullable(Builder.MODIFIERS.get(entry.getKey())).ifPresent(modifier -> { - modifier.apply(builder, entry.getValue()); - }); + Optional.ofNullable(Builder.MODIFIERS.get(entry.getKey())).ifPresent(modifier -> modifier.apply(builder, entry.getValue())); } return builder.build(); } @@ -93,11 +92,11 @@ public interface Sound extends Supplier { public static final Map MODIFIERS = new HashMap<>(); static { - MODIFIERS.put("volume", (b, o) -> b.volume(MiscUtils.getAsFloat(o))); - MODIFIERS.put("pitch", (b, o) -> b.pitch(MiscUtils.getAsFloat(o))); - MODIFIERS.put("weight", (b, o) -> b.pitch(MiscUtils.getAsInt(o))); + MODIFIERS.put("volume", (b, o) -> b.volume(ResourceConfigUtils.getAsFloat(o, "volume"))); + MODIFIERS.put("pitch", (b, o) -> b.pitch(ResourceConfigUtils.getAsFloat(o, "pitch"))); + MODIFIERS.put("weight", (b, o) -> b.pitch(ResourceConfigUtils.getAsInt(o, "weight"))); MODIFIERS.put("stream", (b, o) -> b.stream((boolean) o)); - MODIFIERS.put("attenuation-distance", (b, o) -> b.attenuationDistance(MiscUtils.getAsInt(o))); + MODIFIERS.put("attenuation-distance", (b, o) -> b.attenuationDistance(ResourceConfigUtils.getAsInt(o, "attenuation-distance"))); MODIFIERS.put("preload", (b, o) -> b.preload((boolean) o)); MODIFIERS.put("type", (b, o) -> b.type(o.toString())); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index 5cdd8d198..457100e5d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.sound; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -13,8 +14,8 @@ public record SoundData(Key id, float volume, float pitch) { } else if (obj instanceof Map map) { Map data = MiscUtils.castToMap(map, false); Key id = Key.of((String) data.get("id")); - float volumeFloat = MiscUtils.getAsFloat(data.getOrDefault("volume", volume)); - float pitchFloat = MiscUtils.getAsFloat(data.getOrDefault("pitch", pitch)); + float volumeFloat = ResourceConfigUtils.getAsFloat(data.getOrDefault("volume", volume), "volume"); + float pitchFloat = ResourceConfigUtils.getAsFloat(data.getOrDefault("pitch", pitch), "pitch"); return new SoundData(id, volumeFloat, pitchFloat); } else { throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ArrayUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ArrayUtils.java index 85e8beddd..da87a2c53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ArrayUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ArrayUtils.java @@ -25,6 +25,26 @@ public class ArrayUtils { return subArray; } + @SuppressWarnings("unchecked") + public static T[] merge(T[] array1, T[] array2) { + if (array1 == null && array2 == null) { + return null; + } + if (array1 == null) { + return Arrays.copyOf(array2, array2.length); + } + if (array2 == null) { + return Arrays.copyOf(array1, array1.length); + } + T[] mergedArray = (T[]) Array.newInstance( + array1.getClass().getComponentType(), + array1.length + array2.length + ); + System.arraycopy(array1, 0, mergedArray, 0, array1.length); + System.arraycopy(array2, 0, mergedArray, array1.length, array2.length); + return mergedArray; + } + public static List splitArray(T[] array, int chunkSize) { List result = new ArrayList<>(); for (int i = 0; i < array.length; i += chunkSize) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java index fe8c26071..2d8693728 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.util; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; + import java.util.stream.IntStream; public class CharacterUtils { @@ -11,12 +13,17 @@ public class CharacterUtils { int length = processedString.length() / 4; char[] chars = new char[length]; for (int i = 0; i < length; i++) { - int codePoint = Integer.parseInt(processedString.substring(i * 4, i * 4 + 4), 16); - if (Character.isSupplementaryCodePoint(codePoint)) { - chars[i] = Character.highSurrogate(codePoint); - chars[++i] = Character.lowSurrogate(codePoint); - } else { - chars[i] = (char) codePoint; + String hex = processedString.substring(i * 4, i * 4 + 4); + try { + int codePoint = Integer.parseInt(hex, 16); + if (Character.isSupplementaryCodePoint(codePoint)) { + chars[i] = Character.highSurrogate(codePoint); + chars[++i] = Character.lowSurrogate(codePoint); + } else { + chars[i] = (char) codePoint; + } + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.image.invalid_hex_value", e, hex); } } return chars; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/EnumUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/EnumUtils.java new file mode 100644 index 000000000..982542b51 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/EnumUtils.java @@ -0,0 +1,16 @@ +package net.momirealms.craftengine.core.util; + +import java.util.StringJoiner; + +public final class EnumUtils { + + private EnumUtils() {} + + public static String toString(Enum[] enums) { + StringJoiner joiner = new StringJoiner(", "); + for (Enum e : enums) { + joiner.add(e.name()); + } + return joiner.toString(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index 12e27e285..c208c1c2f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -1,15 +1,12 @@ package net.momirealms.craftengine.core.util; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import org.joml.Quaternionf; -import org.joml.Vector3d; import org.joml.Vector3f; -import java.net.InetSocketAddress; -import java.net.ProxySelector; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.function.Supplier; public class MiscUtils { @@ -26,26 +23,6 @@ public class MiscUtils { throw new IllegalArgumentException("Expected Map, got: " + (obj == null ? null : obj.getClass().getSimpleName())); } - @SuppressWarnings("unchecked") - public static List> castToMapListOrThrow(Object obj, Supplier exceptionSupplier) { - if (obj instanceof List list) { - return (List>) list; - } else { - throw exceptionSupplier.get(); - } - } - - @SuppressWarnings("unchecked") - public static List castToList(Object obj, boolean allowNull) { - if (allowNull && obj == null) { - return null; - } - if (obj instanceof List list) { - return (List) list; - } - throw new IllegalArgumentException("Expected List, got: " + obj.getClass().getSimpleName()); - } - public static List getAsStringList(Object o) { List list = new ArrayList<>(); if (o instanceof List) { @@ -62,84 +39,8 @@ public class MiscUtils { return list; } - public static List getAsFloatList(Object o) { - List list = new ArrayList<>(); - if (o instanceof List) { - for (Object object : (List) o) { - if (object instanceof Number) { - list.add(((Number) object).floatValue()); - } else if (object instanceof String) { - try { - list.add(Float.parseFloat((String) object)); - } catch (NumberFormatException e) { - throw new RuntimeException("Cannot convert " + object + " to float"); - } - } else { - throw new RuntimeException("Cannot convert " + object + " to float"); - } - } - } else if (o instanceof Float) { - list.add((Float) o); - } else if (o instanceof String) { - try { - list.add(Float.parseFloat((String) o)); - } catch (NumberFormatException e) { - throw new RuntimeException("Cannot convert " + o + " to float"); - } - } else { - throw new RuntimeException("Cannot convert " + o + " to float"); - } - return list; - } - - public static int getAsInt(Object o) { - if (o instanceof Integer) { - return (Integer) o; - } else if (o instanceof String) { - try { - return Integer.parseInt((String) o); - } catch (NumberFormatException e) { - throw new RuntimeException("Cannot convert " + o + " to int"); - } - } else if (o instanceof Boolean) { - return (Boolean) o ? 1 : 0; - } else if (o instanceof Number) { - return ((Number) o).intValue(); - } - throw new RuntimeException("Cannot convert " + o + " to int"); - } - - public static double getAsDouble(Object o) { - if (o instanceof Double) { - return (Double) o; - } else if (o instanceof String) { - try { - return Double.parseDouble((String) o); - } catch (NumberFormatException e) { - throw new RuntimeException("Cannot convert " + o + " to double"); - } - } else if (o instanceof Number) { - return ((Number) o).doubleValue(); - } - throw new RuntimeException("Cannot convert " + o + " to double"); - } - - public static float getAsFloat(Object o) { - if (o instanceof Float) { - return (Float) o; - } else if (o instanceof String) { - try { - return Float.parseFloat((String) o); - } catch (NumberFormatException e) { - throw new RuntimeException("Cannot convert " + o + " to float"); - } - } else if (o instanceof Number) { - return ((Number) o).floatValue(); - } - throw new RuntimeException("Cannot convert " + o + " to float"); - } - - public static Vector3f getVector3f(Object o) { + public static Vector3f getAsVector3f(Object o, String option) { + if (o == null) return new Vector3f(); String stringFormat = o.toString(); String[] split = stringFormat.split(","); if (split.length == 3) { @@ -147,23 +48,12 @@ public class MiscUtils { } else if (split.length == 1) { return new Vector3f(Float.parseFloat(split[0])); } else { - throw new RuntimeException("Cannot convert " + o + " to Vector3f"); + throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); } } - public static Vector3d getVector3d(Object o) { - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 3) { - return new Vector3d(Double.parseDouble(split[0]), Double.parseDouble(split[1]), Double.parseDouble(split[2])); - } else if (split.length == 1) { - return new Vector3d(Double.parseDouble(split[0])); - } else { - throw new RuntimeException("Cannot convert " + o + " to Vector3d"); - } - } - - public static Quaternionf getQuaternionf(Object o) { + public static Quaternionf getAsQuaternionf(Object o, String option) { + if (o == null) return new Quaternionf(); String stringFormat = o.toString(); String[] split = stringFormat.split(","); if (split.length == 4) { @@ -171,22 +61,7 @@ public class MiscUtils { } else if (split.length == 1) { return QuaternionUtils.toQuaternionf(0, Math.toRadians(Float.parseFloat(split[0])), 0); } else { - throw new RuntimeException("Cannot convert " + o + " to Quaternionf"); + throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); } } - - public static ProxySelector getProxySelector(Object o) { - Map proxySetting = castToMap(o, true); - ProxySelector proxy = ProxySelector.getDefault(); - if (proxySetting != null) { - String proxyHost = (String) proxySetting.get("host"); - int proxyPort = (int) proxySetting.get("port"); - if (proxyHost == null || proxyHost.isEmpty() || proxyPort <= 0 || proxyPort > 65535) { - throw new IllegalArgumentException("Invalid proxy setting"); - } else { - proxy = ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort)); - } - } - return proxy; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java new file mode 100644 index 000000000..b81e56ca0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -0,0 +1,121 @@ +package net.momirealms.craftengine.core.util; + +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; + +import java.util.Map; +import java.util.function.Supplier; + +public final class ResourceConfigUtils { + + private ResourceConfigUtils() {} + + public static T requireNonNullOrThrow(T obj, String node) { + if (obj == null) + throw new LocalizedResourceConfigException(node); + return obj; + } + + public static T requireNonNullOrThrow(T obj, Supplier exceptionSupplier) { + if (obj == null) + throw exceptionSupplier.get(); + return obj; + } + + public static String requireNonEmptyStringOrThrow(Object obj, String node) { + Object o = requireNonNullOrThrow(obj, node); + String s = o.toString(); + if (s.isEmpty()) throw new LocalizedResourceConfigException(node); + return s; + } + + public static String requireNonEmptyStringOrThrow(Object obj, Supplier exceptionSupplier) { + Object o = requireNonNullOrThrow(obj, exceptionSupplier); + String s = o.toString(); + if (s.isEmpty()) throw exceptionSupplier.get(); + return s; + } + + public static Object get(Map arguments, String... keys) { + for (String key : keys) { + Object value = arguments.get(key); + if (value != null) { + return value; + } + } + return null; + } + + public static int getAsInt(Object o, String option) { + switch (o) { + case null -> { + return 0; + } + case Integer i -> { + return i; + } + case Number number -> { + return number.intValue(); + } + case String s -> { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option); + } + } + case Boolean b -> { + return b ? 1 : 0; + } + default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option); + } + } + + public static double getAsDouble(Object o, String option) { + switch (o) { + case null -> { + return 0.0; + } + case Double d -> { + return d; + } + case Number n -> { + return n.doubleValue(); + } + case String s -> { + try { + return Double.parseDouble(s); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option); + } + } + default -> { + throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option); + } + } + } + + public static float getAsFloat(Object o, String option) { + switch (o) { + case null -> { + return 0.0f; + } + case Float f -> { + return f; + } + case String s -> { + try { + return Float.parseFloat(s); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option); + } + } + case Number number -> { + return number.floatValue(); + } + default -> { + throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index 6817fd512..51bc86577 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -47,7 +47,11 @@ public abstract class CEWorld { this.loadedChunkMapLock.readLock().lock(); try { for (Map.Entry entry : this.loadedChunkMap.entrySet()) { - worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), entry.getValue(), true); + CEChunk chunk = entry.getValue(); + if (chunk.dirty()) { + worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk, true); + chunk.setDirty(false); + } } } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to save world chunks", e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index 41667fae2..1ae57e38b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -1,13 +1,14 @@ package net.momirealms.craftengine.core.world.chunk; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.BlockEntityState; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.world.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; public class CEChunk { private boolean loaded; @@ -15,21 +16,22 @@ public class CEChunk { private final ChunkPos chunkPos; private final CESection[] sections; private final WorldHeight worldHeightAccessor; - private final List entities; + private final Map blockEntities; + private boolean dirty; public CEChunk(CEWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.entities = new ArrayList<>(); + this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, List entities) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map blockEntities) { this.world = world; this.chunkPos = chunkPos; - this.entities = entities; + this.blockEntities = blockEntities; this.worldHeightAccessor = world.worldHeight(); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; @@ -44,8 +46,20 @@ public class CEChunk { this.fillEmptySection(); } + public Map blockEntities() { + return blockEntities; + } + + public boolean dirty() { + return dirty; + } + + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + public boolean isEmpty() { - if (!this.entities.isEmpty()) return false; + if (!this.blockEntities.isEmpty()) return false; for (CESection section : this.sections) { if (section != null && !section.statesContainer().isEmpty()) { return false; @@ -58,7 +72,7 @@ public class CEChunk { for (int i = 0; i < sections.length; ++i) { if (sections[i] == null) { sections[i] = new CESection(world.worldHeight().getSectionYFromSectionIndex(i), - new PalettedContainer<>(null, EmptyBlock.INSTANCE.defaultState(), PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE)); + new PalettedContainer<>(null, EmptyBlock.STATE, PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE)); } } } @@ -73,7 +87,10 @@ public class CEChunk { if (section == null) { return; } - section.setBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15, state); + ImmutableBlockState previous = section.setBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15, state); + if (previous != state) { + setDirty(true); + } } @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java index 92eb3dd3e..a7f9553ab 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.world.chunk; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.world.BlockPos; +import org.jetbrains.annotations.ApiStatus; public class CESection { public static final int SECTION_WIDTH = 16; @@ -16,30 +17,37 @@ public class CESection { this.statesContainer = statesContainer; } - public void setBlockState(BlockPos pos, ImmutableBlockState state) { - this.setBlockState(pos.x() & 15, pos.y() & 15, pos.z() & 15, state); + @ApiStatus.Internal + public ImmutableBlockState setBlockState(BlockPos pos, ImmutableBlockState state) { + return this.setBlockState(pos.x() & 15, pos.y() & 15, pos.z() & 15, state); } - public void setBlockState(int x, int y, int z, ImmutableBlockState state) { - this.statesContainer.set((y << 4 | z) << 4 | x, state); + @ApiStatus.Internal + public ImmutableBlockState setBlockState(int x, int y, int z, ImmutableBlockState state) { + return this.setBlockState((y << 4 | z) << 4 | x, state); } - public void setBlockState(int index, ImmutableBlockState state) { - this.statesContainer.set(index, state); + @ApiStatus.Internal + public ImmutableBlockState setBlockState(int index, ImmutableBlockState state) { + return this.statesContainer.getAndSet(index, state); } + @ApiStatus.Internal public ImmutableBlockState getBlockState(BlockPos pos) { return getBlockState(pos.x() & 15, pos.y() & 15, pos.z() & 15); } + @ApiStatus.Internal public ImmutableBlockState getBlockState(int x, int y, int z) { return statesContainer.get((y << 4 | z) << 4 | x); } + @ApiStatus.Internal public ImmutableBlockState getBlockState(int index) { return statesContainer.get(index); } + @ApiStatus.Internal public PalettedContainer statesContainer() { return statesContainer; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/EmptyPaletteStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/EmptyPaletteStorage.java index 262e5dc1d..610b89419 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/EmptyPaletteStorage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/EmptyPaletteStorage.java @@ -6,35 +6,48 @@ import java.util.function.IntConsumer; public record EmptyPaletteStorage(int size) implements PaletteStorage { public static final long[] EMPTY_DATA = new long[0]; + @Override public int swap(int index, int value) { return 0; } + @Override public void set(int index, int value) { } + @Override + public int getAndSet(int index, int value) { + return 0; + } + + @Override public int get(int index) { return 0; } + @Override public long[] getData() { return EMPTY_DATA; } + @Override public int getElementBits() { return 0; } + @Override public void forEach(IntConsumer action) { for (int i = 0; i < this.size; ++i) { action.accept(0); } } + @Override public void writePaletteIndices(int[] out) { Arrays.fill(out, 0, this.size, 0); } + @Override public PaletteStorage copy() { return this; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/InjectedPalettedContainerHolder.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/InjectedPalettedContainerHolder.java index a5cbc6e31..f8d67784d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/InjectedPalettedContainerHolder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/InjectedPalettedContainerHolder.java @@ -11,6 +11,10 @@ public interface InjectedPalettedContainerHolder { void ceSection(CESection section); + CEChunk ceChunk(); + + void ceChunk(CEChunk chunk); + CEWorld ceWorld(); void ceWorld(CEWorld world); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PackedIntegerArray.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PackedIntegerArray.java index 80aabd580..9601bf068 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PackedIntegerArray.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PackedIntegerArray.java @@ -81,7 +81,7 @@ public class PackedIntegerArray implements PaletteStorage { int i = this.getStorageIndex(index); long l = this.data[i]; int j = (index - i * this.elementsPerLong) * this.elementBits; - this.data[i] = l & ~(this.maxValue << j) | ((long)value & this.maxValue) << j; + this.data[i] = l & ~(this.maxValue << j) | ((long) value & this.maxValue) << j; } @Override @@ -92,6 +92,15 @@ public class PackedIntegerArray implements PaletteStorage { return (int)(l >> j & this.maxValue); } + @Override + public int getAndSet(int index, int value) { + int i = this.getStorageIndex(index); + long l = this.data[i]; + int j = (index - i * this.elementsPerLong) * this.elementBits; + this.data[i] = l & ~(this.maxValue << j) | ((long) value & this.maxValue) << j; + return (int)(l >> j & this.maxValue); + } + @Override public long[] getData() { return this.data; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PaletteStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PaletteStorage.java index 3ef4e4dba..d8843e56c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PaletteStorage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PaletteStorage.java @@ -8,6 +8,8 @@ public interface PaletteStorage { void set(int index, int value); + int getAndSet(int index, int value); + int get(int index); long[] getData(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java index 3a6fb0e5f..de064dbe6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java @@ -69,7 +69,7 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC public boolean isEmpty() { Data data = this.data; if (data.palette instanceof SingularPalette singularPalette) { - return singularPalette.get(0) == EmptyBlock.INSTANCE; + return singularPalette.get(0) == EmptyBlock.STATE; } return false; } @@ -125,6 +125,17 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC return data.palette.get(data.storage.get(index)); } + public T getAndSet(int index, T state) { + this.lock(); + try { + int i = this.data.palette.index(state); + int preIndex = this.data.storage.getAndSet(index, i); + return this.data.palette.get(preIndex); + } finally { + this.unlock(); + } + } + public void set(int x, int y, int z, T value) { this.lock(); try { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java new file mode 100644 index 000000000..8f8be31d9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.BlockEntityState; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Map; + +public final class DefaultBlockEntitySerializer { + + @ApiStatus.Experimental + public static ListTag serialize(Map tiles) { + ListTag result = new ListTag(); + Map nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); + for (Map.Entry entry : tiles.entrySet()) { + int pos = entry.getKey(); + CompoundTag tag = entry.getValue().nbt(); + int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos}); + int[] newPoses = new int[previous.length + 1]; + System.arraycopy(previous, 0, newPoses, 0, previous.length); + newPoses[previous.length] = pos; + nbtToPosMap.put(tag, newPoses); + } + for (Map.Entry entry : nbtToPosMap.entrySet()) { + CompoundTag blockEntityTag = new CompoundTag(); + blockEntityTag.put("data", entry.getKey()); + blockEntityTag.putIntArray("pos", entry.getValue()); + result.add(blockEntityTag); + } + return result; + } + + @ApiStatus.Experimental + public static Map deserialize(ListTag tag) { + Map result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f); + for (int i = 0; i < tag.size(); i++) { + CompoundTag blockEntityTag = tag.getCompound(i); + CompoundTag data = blockEntityTag.getCompound("data"); + int[] pos = blockEntityTag.getIntArray("pos"); + for (int j = 0; j < pos.length; j++) { + result.put(j, new BlockEntityState(data)); + } + } + return result; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 62dacbd48..1a44e0efc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -9,9 +9,9 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.Optional; -public class DefaultChunkSerializer { +public final class DefaultChunkSerializer { @Nullable public static CompoundTag serialize(@NotNull CEChunk chunk) { @@ -28,7 +28,7 @@ public class DefaultChunkSerializer { if (sections.isEmpty()) return null; CompoundTag chunkNbt = new CompoundTag(); chunkNbt.put("sections", sections); - chunkNbt.put("entities", new ListTag()); + chunkNbt.put("block_entities", DefaultBlockEntitySerializer.serialize(chunk.blockEntities())); return chunkNbt; } @@ -46,7 +46,7 @@ public class DefaultChunkSerializer { } } } - ListTag entities = chunkNbt.getList("entities"); - return new CEChunk(world, pos, sectionArray, List.of()); + ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); + return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java index 2505618b3..9e6b41426 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java @@ -24,14 +24,14 @@ import java.util.List; import java.util.Optional; import java.util.stream.LongStream; -public class DefaultSectionSerializer { +public final class DefaultSectionSerializer { @Nullable public static CompoundTag serialize(@NotNull CESection section) { ReadableContainer.Serialized serialized = section.statesContainer().serialize(null, PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE); ListTag palettes = new ListTag(); List states = serialized.paletteEntries(); - if (states.size() == 1 && states.get(0) == EmptyBlock.INSTANCE.defaultState()) { + if (states.size() == 1 && states.get(0) == EmptyBlock.STATE) { return null; } CompoundTag sectionNbt = new CompoundTag(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/MultiAABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/MultiAABB.java deleted file mode 100644 index 134b0be63..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/MultiAABB.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.momirealms.craftengine.core.world.collision; - -public class MultiAABB { -} diff --git a/gradle.properties b/gradle.properties index a38fd2bdd..97af92e9b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,14 +2,14 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.51 -config_version=30 -lang_version=7 +project_version=0.0.52 +config_version=31 +lang_version=9 project_group=net.momirealms latest_supported_version=1.21.5 # Supported languages -supported_languages=en,zh_cn,zh_tw,es +supported_languages=en,zh_cn,zh_tw,es,tr # Dependency settings paper_version=1.21.5 @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.15 -nms_helper_version=0.62 +nms_helper_version=0.64.3 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23 amazon_awssdk_eventstream_version=1.0.1 diff --git a/readme/README_zh-CN.md b/readme/README_zh-CN.md index 54db64bf7..4cf9e7563 100644 --- a/readme/README_zh-CN.md +++ b/readme/README_zh-CN.md @@ -135,7 +135,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.48") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.48") + compileOnly("net.momirealms:craft-engine-core:0.0.52") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.52") } ``` diff --git a/readme/README_zh-TW.md b/readme/README_zh-TW.md index a4281d278..a6dfbc189 100644 --- a/readme/README_zh-TW.md +++ b/readme/README_zh-TW.md @@ -135,7 +135,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.48") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.48") + compileOnly("net.momirealms:craft-engine-core:0.0.52") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.52") } ```