9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 01:49:30 +00:00

Merge branch 'new-tr.yml' of https://github.com/WhiteProject1/craft-engine into new-tr.yml

This commit is contained in:
WhiteProject1
2025-06-15 04:09:07 +03:00
168 changed files with 2260 additions and 1355 deletions

View File

@@ -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")
}
```

View File

@@ -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%]",

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
# Don't change this
lang-version: "${lang_version}"
# Commands
exception.invalid_syntax: "<red>Invalid syntax. Correct syntax: <white><arg:0></white></red>"
exception.invalid_argument: "<red>Invalid argument. Reason: <white><arg:0></white></red>"
exception.invalid_sender: "<red><arg:0> is not allowed to execute that command. Must be of type <arg:1></red>"
@@ -63,79 +64,213 @@ command.upload.failure.not_supported: "<red>Current hosting method '<arg:0>' doe
command.upload.on_progress: "<white>Started uploading progress. Check the console for more information.</white>"
command.send_resource_pack.success.single: "<white>Sent resource pack to <arg:0>.</white>"
command.send_resource_pack.success.multiple: "<white>Send resource packs to <arg:0> players.</white>"
warning.config.image.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'.</yellow>"
warning.config.image.lack_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
warning.config.image.height_smaller_than_ascent: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' should be no lower than 'ascent'.</yellow>"
warning.config.image.no_file: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'file' argument.</yellow>"
warning.config.image.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'file' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.image.invalid_font_name: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'font' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.image.lack_char: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'char' argument.</yellow>"
warning.config.image.codepoint_in_use: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character[<arg:3>(<arg:4>)] in font <arg:2> that has been used by another image '<arg:5>'.</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grind.</yellow>"
warning.config.image.file_not_exist: "<yellow>Issue found in file <arg:0> - PNG file '<arg:2>' not found for image '<arg:1>'.</yellow>"
warning.config.recipe.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'.</yellow>"
warning.config.pack.duplicated_files: "</red>Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section.</red>"
warning.config.type.int: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to integer type for option '<arg:3>'.</yellow>"
warning.config.type.float: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to float type for option '<arg:3>'.</yellow>"
warning.config.type.double: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to double type for option '<arg:3>'.</yellow>"
warning.config.type.quaternionf: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Quaternionf type for option '<arg:3>'.</yellow>"
warning.config.type.vector3f: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Vector3f type for option '<arg:3>'.</yellow>"
warning.config.structure.not_section: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is expected to be a config section while it's actually a(n) '<arg:2>'.</yellow>"
warning.config.image.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.image.missing_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
warning.config.image.height_ascent_conflict: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' argument '<arg:2>' should be no lower than 'ascent' argument '<arg:3>'.</yellow>"
warning.config.image.missing_file: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'file' argument.</yellow>"
warning.config.image.invalid_file_chars: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'file' argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.image.invalid_font_chars: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' has a 'font' argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.image.missing_char: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'char' argument.</yellow>"
warning.config.image.codepoint_conflict: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character '<arg:3>(<arg:4>)' in font <arg:2> that has been used by another image '<arg:5>'.</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grid.</yellow>"
warning.config.image.file_not_found: "<yellow>Issue found in file <arg:0> - PNG file '<arg:2>' not found for image '<arg:1>'.</yellow>"
warning.config.image.invalid_hex_value: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a unicode character '<arg:2>' that is not a valid hexadecimal (radix 16) value.</yellow>"
warning.config.recipe.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.recipe.missing_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required 'type' argument.</yellow>"
warning.config.recipe.invalid_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is using an invalid recipe type '<arg:2>'.</yellow>"
warning.config.recipe.invalid_item: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is using an invalid item '<arg:2>'.</yellow>"
warning.config.recipe.missing_ingredient: "<yellow>Issue found in file <arg:0> - The cooking recipe '<arg:1>' is missing the required 'ingredient' argument.</yellow>"
warning.config.recipe.missing_result: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required 'result' argument.</yellow>"
warning.config.recipe.result.missing_id: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required argument 'id' for recipe result.</yellow>"
warning.config.recipe.crafting.invalid_category: "<yellow>Issue found in file <arg:0> - The crafting recipe '<arg:1>' is using an invalid category '<arg:2>'. Allowed categories: [<arg:3>].</yellow>"
warning.config.recipe.cooking.invalid_category: "<yellow>Issue found in file <arg:0> - The cooking recipe '<arg:1>' is using an invalid category '<arg:2>'. Allowed categories: [<arg:3>].</yellow>"
warning.config.recipe.shaped.missing_pattern: "<yellow>Issue found in file <arg:0> - The shaped recipe '<arg:1>' is missing the required argument 'pattern'.</yellow>"
warning.config.recipe.shaped.invalid_pattern: "<yellow>Issue found in file <arg:0> - The shaped recipe '<arg:1>' is using an invalid pattern '<arg:2>'.</yellow>"
warning.config.recipe.shaped.invalid_symbol: "<yellow>Issue found in file <arg:0> - The shaped recipe '<arg:1>' is using an invalid symbol '<arg:2>' in pattern.</yellow>"
warning.config.recipe.smithing_transform.post_processor.missing_type: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'type' for one of the post-processors.</yellow>"
warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is using an invalid post processor type '<arg:2>'.</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'components' for post-processors 'keep_components'.</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>"
warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>"
warning.config.template.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'.</yellow>"
warning.config.vanilla_loot.type_not_exist: "<yellow>Issue found in file <arg:0> - 'type' not set for vanilla loot '<arg:1>'.</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>Issue found in file <arg:0> - Invalid block target [<arg:2>] in vanilla loot '<arg:1>'.</yellow>"
warning.config.sound.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated sound '<arg:1>'.</yellow>"
warning.config.jukebox_song.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated jukebox song '<arg:1>'.</yellow>"
warning.config.furniture.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated furniture '<arg:1>'.</yellow>"
warning.config.furniture.lack_placement: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'placement' argument.</yellow>"
warning.config.furniture.element.lack_item: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'item' argument for one of its elements.</yellow>"
warning.config.item.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'.</yellow>"
warning.config.item.lack_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>"
warning.config.template.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'from' '<arg:2>' larger than 'to' '<arg:3>' in 'self_increase_int' argument.</yellow>"
warning.config.template.argument.list.invalid_type: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) '<arg:2>'.</yellow>"
warning.config.vanilla_loot.missing_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is missing the required 'type' argument.</yellow>"
warning.config.vanilla_loot.invalid_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is using an invalid type '<arg:2>'. Allowed types: [<arg:3>].</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>Issue found in file <arg:0> - Invalid block target '<arg:2>' in vanilla loot '<arg:1>'.</yellow>"
warning.config.sound.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated sound '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.sound.missing_sounds: "<yellow>Issue found in file <arg:0> - The sound '<arg:1>' is missing the required 'sounds' argument.</yellow>"
warning.config.sound.missing_name: "<yellow>Issue found in file <arg:0> - The sound '<arg:1>' is missing the required 'name' argument.</yellow>"
warning.config.jukebox_song.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated jukebox song '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.jukebox_song.missing_sound: "<yellow>Issue found in file <arg:0> - The jukebox song '<arg:1>' is missing the required 'sound' argument.</yellow>"
warning.config.furniture.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated furniture '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.furniture.missing_placement: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'placement' argument.</yellow>"
warning.config.furniture.element.missing_item: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is missing the required 'item' argument for one of its elements.</yellow>"
warning.config.furniture.settings.unknown: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>"
warning.config.furniture.hitbox.invalid_type: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is using an invalid hitbox type '<arg:2>'.</yellow>"
warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is using a custom hitbox with invalid entity type '<arg:2>'.</yellow>"
warning.config.item.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.item.settings.unknown: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>"
warning.config.item.missing_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>"
warning.config.item.invalid_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid material type '<arg:2>'.</yellow>"
warning.config.item.bad_custom_model_data_value: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data [<arg:2>] that is too large. It's recommended to use a value lower than 16,777,216.</yellow>"
warning.config.item.custom_model_data_conflict: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data [<arg:2>] that has been occupied by item '<arg:3>'</yellow>"
warning.config.item.lack_model_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'custom-model-data' or 'item-model' argument.</yellow>"
warning.config.block.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated block '<arg:1>'.</yellow>"
warning.config.block.lack_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument.</yellow>"
warning.config.block.state.lack_real_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'id' argument for 'state'.</yellow>"
warning.config.block.state.lack_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument for 'state'.</yellow>"
warning.config.block.state.lack_properties: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'properties' section for 'states'.</yellow>"
warning.config.block.state.lack_appearances: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearances' section for 'states'.</yellow>"
warning.config.block.state.lack_variants: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'variants' section for 'states'.</yellow>"
warning.config.block.state.variant.lack_appearance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearance' argument for variant '<arg:2>'.</yellow>"
warning.config.item.bad_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that is too large. It's recommended to use a value lower than 16,777,216.</yellow>"
warning.config.item.custom_model_data_conflict: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that has been occupied by item '<arg:3>'.</yellow>"
warning.config.item.missing_model_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'custom-model-data' or 'item-model' argument.</yellow>"
warning.config.item.behavior.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for its item behavior.</yellow>"
warning.config.item.behavior.invalid_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid item behavior type '<arg:2>'.</yellow>"
warning.config.item.behavior.block.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'block_item' behavior.</yellow>"
warning.config.item.behavior.furniture.missing_furniture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'furniture' argument for 'furniture_item' behavior.</yellow>"
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'liquid_collision_block_item' behavior.</yellow>"
warning.config.item.model.invalid_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid model type '<arg:2>'.</yellow>"
warning.config.item.model.tint.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for tint.</yellow>"
warning.config.item.model.tint.invalid_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid tint type '<arg:2>'.</yellow>"
warning.config.item.model.tint.constant.missing_value: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'value' argument for constant tint.</yellow>"
warning.config.item.model.tint.grass.invalid_temp: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid temperature '<arg:2>' for grass tint, which is expected to be between 0 and 1.</yellow>"
warning.config.item.model.tint.grass.invalid_downfall: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid downfall '<arg:2>' for grass tint, which is expected to be between 0 and 1.</yellow>"
warning.config.item.model.tint.invalid_value: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid tint '<arg:2>'.</yellow>"
warning.config.item.model.base.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'path' argument for model 'minecraft:model'.</yellow>"
warning.config.item.model.base.invalid_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' has an invalid 'path' argument '<arg:2>' for model 'minecraft:model' which contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.item.model.condition.missing_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'property' argument for model 'minecraft:condition'.</yellow>"
warning.config.item.model.condition.invalid_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid property '<arg:2>' for model 'minecraft:condition'.</yellow>"
warning.config.item.model.condition.missing_on_true: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'on-true' argument for model 'minecraft:condition'.</yellow>"
warning.config.item.model.condition.missing_on_false: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'on-false' argument for model 'minecraft:condition'.</yellow>"
warning.config.item.model.condition.keybind.missing: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'keybind' argument for property 'minecraft:keybind_down'.</yellow>"
warning.config.item.model.condition.has_component.missing_component: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'component' argument for property 'minecraft:has_component'.</yellow>"
warning.config.item.model.composite.missing_models: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'models' argument for 'minecraft:composite' model.</yellow>"
warning.config.item.model.range_dispatch.missing_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'property' argument for model 'minecraft:range_dispatch'.</yellow>"
warning.config.item.model.range_dispatch.invalid_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid property '<arg:2>' for model 'minecraft:range_dispatch'.</yellow>"
warning.config.item.model.range_dispatch.missing_entries: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'entries' argument for model 'minecraft:composite'.</yellow>"
warning.config.item.model.range_dispatch.entry.missing_model: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'model' argument for one of the entries in model 'minecraft:composite'.</yellow>"
warning.config.item.model.range_dispatch.compass.missing_target: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'target' argument for property 'minecraft:compass'.</yellow>"
warning.config.item.model.range_dispatch.time.missing_source: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'source' argument for property 'minecraft:time'.</yellow>"
warning.config.item.model.select.missing_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'property' argument for model 'minecraft:select'.</yellow>"
warning.config.item.model.select.invalid_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid property '<arg:2>' for model 'minecraft:select'.</yellow>"
warning.config.item.model.select.missing_cases: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'cases' argument for model 'minecraft:select'.</yellow>"
warning.config.item.model.select.case.missing_when: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'when' argument for one of the cases in model 'minecraft:select'.</yellow>"
warning.config.item.model.select.case.missing_model: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'model' argument for one of the cases in model 'minecraft:select'.</yellow>"
warning.config.item.model.select.block_state.missing_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block-state-property' argument for property 'minecraft:block_state'.</yellow>"
warning.config.item.model.select.local_time.missing_pattern: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'pattern' argument for property 'minecraft:local_time'.</yellow>"
warning.config.item.model.special.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for model 'minecraft:special'.</yellow>"
warning.config.item.model.special.invalid_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid type '<arg:2>' for model 'minecraft:special'.</yellow>"
warning.config.item.model.special.banner.missing_color: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'color' argument for special model 'minecraft:banner'.</yellow>"
warning.config.item.model.special.bed.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:bed'.</yellow>"
warning.config.item.model.special.sign.missing_wood_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'wood-type' argument for special model 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
warning.config.item.model.special.sign.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
warning.config.item.model.special.chest.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:chest'.</yellow>"
warning.config.item.model.special.chest.invalid_openness: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid 'openness' value '<arg:2>' for special model 'minecraft:chest'. Valid range '0~1.'</yellow>"
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:shulker_box'.</yellow>"
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid 'openness' value '<arg:2>' for special model 'minecraft:shulker_box'. Valid range '0~1.'</yellow>"
warning.config.item.model.special.head.missing_kind: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'kind' argument for special model 'minecraft:head'.</yellow>"
warning.config.item.model.special.head.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:head'.</yellow>"
warning.config.block.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated block '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.block.missing_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument.</yellow>"
warning.config.block.state.property.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' argument for property '<arg:2>'.</yellow>"
warning.config.block.state.property.invalid_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using the invalid type argument '<arg:2>' for property '<arg:3>'.</yellow>"
warning.config.block.state.property.integer.invalid_range: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using the invalid 'range' argument '<arg:2>' for integer property '<arg:3>'. Correct syntax: 1~2.</yellow>"
warning.config.block.state.property.invalid_format: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid block state property format '<arg:2>'.</yellow>"
warning.config.block.state.missing_real_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' 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'.</yellow>"
warning.config.block.state.missing_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument for 'state'.</yellow>"
warning.config.block.state.missing_properties: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'properties' section for 'states'.</yellow>"
warning.config.block.state.missing_appearances: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearances' section for 'states'.</yellow>"
warning.config.block.state.missing_variants: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'variants' section for 'states'.</yellow>"
warning.config.block.state.variant.missing_appearance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'appearance' argument for variant '<arg:2>'.</yellow>"
warning.config.block.state.variant.invalid_appearance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has an error that the variant '<arg:2>' is using a non-existing appearance '<arg:3>'.</yellow>"
warning.config.block.state.invalid_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid vanilla block state '<arg:2>'."
warning.config.block.state.unavailable_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an unavailable vanilla block state '<arg:2>'."
warning.config.block.state.invalid_vanilla_state_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'."
warning.config.block.state.conflict: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that has been occupied by '<arg:3>'."
warning.config.block.state.bind_real_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' failed to bind real block state for '<arg:2>' as the state has been occupied by '<arg:3>'.</yellow>"
warning.config.block.state.invalid_property_structure: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has an invalid property structure '<arg:2>'."
warning.config.block.state.invalid_property: "<yellow>Issue found in file <arg:0> - Failed to create property '<arg:2>' for block '<arg:1>': <arg:3>."
warning.config.block.state.no_model_set: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'model' or 'models' argument.</yellow>"
warning.config.block.state.invalid_real_state_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a real block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up.</yellow>"
warning.config.block.state.model.lack_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'path' option for 'model'.</yellow>"
warning.config.block.state.model.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has a 'path' argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'</yellow>"
warning.config.model.generation.texture.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a '<arg:2>' texture argument [<arg:3>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.model.generation.parent.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.emoji.lack_keywords: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' is missing the required 'keywords' argument.</yellow>"
warning.config.emoji.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated emoji '<arg:1>'.</yellow>"
warning.config.block.state.invalid_vanilla: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid vanilla block state '<arg:2>'.</yellow>"
warning.config.block.state.unavailable_vanilla: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an unavailable vanilla block state '<arg:2>'. Please free that state in mappings.yml.</yellow>"
warning.config.block.state.invalid_vanilla_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'.</yellow>"
warning.config.block.state.conflict: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that has been occupied by '<arg:3>'.</yellow>"
warning.config.block.state.bind_failed: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' failed to bind real block state for '<arg:2>' as the state has been occupied by '<arg:3>'.</yellow>"
warning.config.block.state.missing_model: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'model' or 'models' argument.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a real block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up.</yellow>"
warning.config.block.state.model.missing_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'path' option for 'model'.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has a 'path' argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.block.settings.unknown: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>"
warning.config.block.behavior.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' argument for its block behavior.</yellow>"
warning.config.block.behavior.invalid_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid block behavior type '<arg:2>'.</yellow>"
warning.config.block.behavior.concrete.missing_solid: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'solid-block' option for 'concrete_block' behavior.</yellow>"
warning.config.block.behavior.crop.missing_age: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'age' property for 'crop_block' behavior.</yellow>"
warning.config.block.behavior.sugar_cane.missing_age: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'age' property for 'sugar_cane_block' behavior.</yellow>"
warning.config.block.behavior.leaves.missing_persistent: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'persistent' property for 'leaves_block' behavior.</yellow>"
warning.config.block.behavior.leaves.missing_distance: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'distance' property for 'leaves_block' behavior.</yellow>"
warning.config.block.behavior.sapling.missing_stage: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'stage' property for 'sapling_block' behavior.</yellow>"
warning.config.block.behavior.sapling.missing_feature: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'feature' argument for 'sapling_block' behavior.</yellow>"
warning.config.block.behavior.strippable.missing_stripped: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'stripped' argument for 'strippable_block' behavior.</yellow>"
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'.</yellow>"
warning.config.model.generation.texture.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a texture '<arg:2>' with path '<arg:3>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.model.generation.parent.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.emoji.missing_keywords: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' is missing the required 'keywords' argument.</yellow>"
warning.config.emoji.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated emoji '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.emoji.invalid_image: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' has an invalid 'image' argument '<arg:2>'.</yellow>"
warning.config.advancement.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated advancement '<arg:1>'.</yellow>"
warning.config.host.lack_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'type' for host.</yellow>"
warning.config.host.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Host 'type' [<arg:0>] is invalid. Please read https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host</yellow>"
warning.config.host.external.lack_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'url' for external host.</yellow>"
warning.config.host.alist.lack_api_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-url' for alist host.</yellow>"
warning.config.host.alist.lack_username: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'username' or environment variable 'CE_ALIST_USERNAME' for alist host.</yellow>"
warning.config.host.alist.lack_password: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'password' or environment variable 'CE_ALIST_PASSWORD' for alist host.</yellow>"
warning.config.host.alist.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for alist host.</yellow>"
warning.config.host.dropbox.lack_app_key: "<yellow>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.</yellow>"
warning.config.host.dropbox.lack_app_secret: "<yellow>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.</yellow>"
warning.config.host.dropbox.lack_refresh_token: "<yellow>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.</yellow>"
warning.config.host.dropbox.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for dropbox host.</yellow>"
warning.config.host.lobfile.lack_api_key: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-key' for lobfile host.</yellow>"
warning.config.host.onedrive.lack_client_id: "<yellow>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.</yellow>"
warning.config.host.onedrive.lack_client_secret: "<yellow>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.</yellow>"
warning.config.host.onedrive.lack_refresh_token: "<yellow>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.</yellow>"
warning.config.host.onedrive.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for onedrive host.</yellow>"
warning.config.host.s3.lack_endpoint: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'endpoint' for s3 host.</yellow>"
warning.config.host.s3.lack_bucket: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'bucket' for s3 host.</yellow>"
warning.config.host.s3.lack_access_key_id: "<yellow>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.</yellow>"
warning.config.host.s3.lack_access_key_secret: "<yellow>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.</yellow>"
warning.config.host.s3.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for s3 host.</yellow>"
warning.config.host.self.lack_ip: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'ip' for self host.</yellow>"
warning.config.host.self.invalid_port: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid 'port' [<arg:0>] for self host.</yellow>"
warning.config.advancement.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated advancement '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.loot_table.missing_pools: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table which is missing the required 'pools' argument.</yellow>"
warning.config.loot_table.invalid_pools_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'pools' should be a string/map list, current type: '<arg:2>'.</yellow>"
warning.config.loot_table.invalid_conditions_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'conditions' should be a map list, current type: '<arg:2>'.</yellow>"
warning.config.loot_table.invalid_functions_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'functions' should be a map list, current type: '<arg:2>'.</yellow>"
warning.config.loot_table.invalid_entries_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'entries' should be a map list, current type: '<arg:2>'.</yellow>"
warning.config.loot_table.function.missing_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the functions is missing the required 'type' argument.</yellow>"
warning.config.loot_table.function.invalid_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the functions is using an invalid function type '<arg:2>'.</yellow>"
warning.config.loot_table.function.apply_bonus.missing_enchantment: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'apply_bonus' is missing the required 'enchantment' argument.</yellow>"
warning.config.loot_table.function.apply_bonus.missing_formula: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'apply_bonus' is missing the required 'formula' argument.</yellow>"
warning.config.loot_table.function.drop_exp.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'drop_exp' is missing the required 'count' argument.</yellow>"
warning.config.loot_table.function.set_count.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'set_count' is missing the required 'count' argument.</yellow>"
warning.config.loot_table.entry.missing_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the entries is missing the required 'type' argument.</yellow>"
warning.config.loot_table.entry.invalid_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the entries is using an invalid entry type '<arg:2>'.</yellow>"
warning.config.loot_table.entry.exp.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, entry 'exp' is missing the required 'count' argument.</yellow>"
warning.config.loot_table.entry.item.missing_item: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, entry 'item' is missing the required 'item' argument.</yellow>"
warning.config.loot_table.condition.missing_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the conditions is missing the required 'type' argument.</yellow>"
warning.config.loot_table.condition.invalid_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the conditions is using an invalid condition type '<arg:2>'.</yellow>"
warning.config.loot_table.condition.table_bonus.missing_enchantment: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'table_bonus' condition is missing the required 'enchantment' argument.</yellow>"
warning.config.loot_table.condition.table_bonus.missing_chances: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, 'table_bonus' condition is missing the required 'chances' argument.</yellow>"
warning.config.loot_table.number.missing_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the numbers is missing the required 'type' argument.</yellow>"
warning.config.loot_table.number.invalid_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the numbers is using an invalid number type '<arg:2>'.</yellow>"
warning.config.host.missing_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'type' argument for host.</yellow>"
warning.config.host.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Host type '<arg:0>' is invalid. Please read https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host.</yellow>"
warning.config.host.external.missing_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'url' argument for external host.</yellow>"
warning.config.host.alist.missing_api_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'api-url' argument for alist host.</yellow>"
warning.config.host.alist.missing_username: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'username' argument or environment variable 'CE_ALIST_USERNAME' for alist host.</yellow>"
warning.config.host.alist.missing_password: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'password' argument or environment variable 'CE_ALIST_PASSWORD' for alist host.</yellow>"
warning.config.host.alist.missing_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for alist host.</yellow>"
warning.config.host.dropbox.missing_app_key: "<yellow>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.</yellow>"
warning.config.host.dropbox.missing_app_secret: "<yellow>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.</yellow>"
warning.config.host.dropbox.missing_refresh_token: "<yellow>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.</yellow>"
warning.config.host.dropbox.missing_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for dropbox host.</yellow>"
warning.config.host.lobfile.missing_api_key: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'api-key' argument for lobfile host.</yellow>"
warning.config.host.onedrive.missing_client_id: "<yellow>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.</yellow>"
warning.config.host.onedrive.missing_client_secret: "<yellow>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.</yellow>"
warning.config.host.onedrive.missing_refresh_token: "<yellow>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.</yellow>"
warning.config.host.onedrive.missing_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for onedrive host.</yellow>"
warning.config.host.s3.missing_endpoint: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'endpoint' argument for s3 host.</yellow>"
warning.config.host.s3.missing_bucket: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'bucket' argument for s3 host.</yellow>"
warning.config.host.s3.missing_access_key: "<yellow>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.</yellow>"
warning.config.host.s3.missing_secret: "<yellow>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.</yellow>"
warning.config.host.s3.missing_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'upload-path' argument for s3 host.</yellow>"
warning.config.host.self.missing_ip: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'ip' argument for self host.</yellow>"
warning.config.host.self.invalid_port: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid port '<arg:0>' for self host.</yellow>"
warning.config.host.gitlab.missing_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'gitlab-url' argument for gitlab host.</yellow>"
warning.config.host.gitlab.missing_token: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'access-token' argument for gitlab host.</yellow>"
warning.config.host.gitlab.missing_project: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'project-id' argument for gitlab host.</yellow>"
warning.config.host.proxy.missing_host: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'host' argument for proxy.</yellow>"
warning.config.host.proxy.missing_port: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'port' argument for proxy.</yellow>"
warning.config.host.proxy.missing_scheme: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing required 'scheme' argument for proxy.</yellow>"
warning.config.host.proxy.invalid: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid proxy '<arg:0>'.</yellow>"
warning.config.conflict_matcher.missing_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the handlers.</yellow>"
warning.config.conflict_matcher.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the terms is using the invalid type '<arg:0>'.</yellow>"
warning.config.conflict_matcher.exact.missing_path: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'exact' matcher.</yellow>"
warning.config.conflict_matcher.contains.missing_path: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'contains' matcher.</yellow>"
warning.config.conflict_matcher.filename.missing_name: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'path' argument for 'filename' matcher.</yellow>"
warning.config.conflict_matcher.pattern.missing_pattern: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'pattern' argument for 'pattern' matcher.</yellow>"
warning.config.conflict_matcher.parent_prefix.missing_prefix: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'prefix' argument for 'parent_path_prefix' matcher.</yellow>"
warning.config.conflict_matcher.parent_suffix.missing_suffix: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'suffix' argument for 'parent_path_suffix' matcher.</yellow>"
warning.config.conflict_matcher.inverted.missing_term: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'term' argument for 'inverted' matcher.</yellow>"
warning.config.conflict_matcher.all_of.missing_terms: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'all_of' matcher.</yellow>"
warning.config.conflict_matcher.any_of.missing_terms: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'any_of' matcher.</yellow>"
warning.config.conflict_resolution.missing_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the resolutions.</yellow>"
warning.config.conflict_resolution.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the resolutions is using the invalid type '<arg:0>'.</yellow>"

View File

@@ -1,6 +1,7 @@
# 别动这个
lang-version: "${lang_version}"
# 命令
exception.invalid_syntax: "<red>无效语法. 正确语法: <white><arg:0></white></red>"
exception.invalid_argument: "<red>无效参数. 原因: <white><arg:0></white></red>"
exception.invalid_sender: "<red><arg:0> 不允许执行该命令. 执行者必须是 <arg:1></red>"
@@ -63,56 +64,213 @@ command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息.</white>"
command.send_resource_pack.success.single: "<white>发送资源包给 <arg:0></white>"
command.send_resource_pack.success.multiple: "<white>发送资源包给 <arg:0> 个玩家</white>"
warning.config.image.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 重复定义</yellow>"
warning.config.image.lack_height: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必要的 'height' 高度参数</yellow>"
warning.config.image.height_smaller_than_ascent: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度</yellow>"
warning.config.image.no_file: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必要的 'file' 文件参数</yellow>"
warning.config.image.invalid_resource_location: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'file' 参数 [<arg:2>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.image.invalid_font_name: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'font' 字体参数 [<arg:2>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.image.lack_char: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必要的 'char' 字符参数</yellow>"
warning.config.image.codepoint_in_use: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 使用的字体 <arg:2> 字符 [<arg:3>(<arg:4>)] 已被其他图片 '<arg:5>' 占用</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'chars' 码位网格配置无效</yellow>"
warning.config.image.file_not_exist: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 对应的PNG文件 <arg:2> 不存在</yellow>"
warning.config.recipe.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 重复定义</yellow>"
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言区域 '<arg:1>'</yellow>"
warning.config.template.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 重复定义</yellow>"
warning.config.vanilla_loot.type_not_exist: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 未设置 'type' 类型参数</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 中的方块目标 [<arg:2>] 无效</yellow>"
warning.config.sound.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 音效 '<arg:1>' 重复定义</yellow>"
warning.config.jukebox_song.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 唱片机歌曲 '<arg:1>' 重复定义</yellow>"
warning.config.furniture.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 重复定义</yellow>"
warning.config.furniture.lack_placement: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 缺少必要的 'placement' 放置参数</yellow>"
warning.config.furniture.element.lack_item: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 的某个元素缺少必的 'item' 物品参数</yellow>"
warning.config.item.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 重复定义</yellow>"
warning.config.item.lack_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必要的 'material' 材料参数</yellow>"
warning.config.item.invalid_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的材料类型 '<arg:2>'</yellow>"
warning.config.item.bad_custom_model_data_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的自定义模型数据值 [<arg:2>] 过大建议使用低于16,777,216的值</yellow>"
warning.config.item.custom_model_data_conflict: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的自定义模型数据 [<arg:2>] 与物品 '<arg:3>' 发生冲突</yellow>"
warning.config.item.lack_model_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必要的 'custom-model-data' 或 'item-model' 模型标识参数</yellow>"
warning.config.block.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 重复定义</yellow>"
warning.config.block.lack_state: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 缺少必的 'state' 状态参数</yellow>"
warning.config.block.state.lack_real_id: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 'state' 配置缺少必要的 'id' 标识参数</yellow>"
warning.config.block.state.lack_state: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 'state' 配置缺少必要的 'state' 状态参数</yellow>"
warning.config.block.state.lack_properties: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 'states' 配置缺少必的 'properties' 属性配置</yellow>"
warning.config.block.state.lack_appearances: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 'states' 配置缺少必要的 'appearances' 外观配置</yellow>"
warning.config.block.state.lack_variants: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 'states' 配置缺少必的 'variants' 变体配置</yellow>"
warning.config.block.state.variant.lack_appearance: "<yellow>在文件 <arg:0> 发现问题 - 方 '<arg:1>' 的 '<arg:2>' 变体配置缺少必的 'appearance' 外观参数</yellow>"
warning.config.block.state.variant.invalid_appearance: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 '<arg:2>' 变体引用了不存在的外观配置 '<arg:3>'</yellow>"
warning.config.block.state.invalid_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的原版方块状态 '<arg:2>'</yellow>"
warning.config.block.state.unavailable_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了不可用的原版方块状态 '<arg:2>'</yellow>"
warning.config.block.state.invalid_vanilla_state_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'</yellow>"
warning.config.block.state.conflict: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.bind_real_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 未能绑定真实方块状态 '<arg:2>',该状态已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.invalid_property_structure: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性结构 '<arg:2>' 配置无效</yellow>"
warning.config.block.state.invalid_property: "<yellow>在文件 <arg:0> 发现问题 - 无法为方块 '<arg:1>' 创建属性 '<arg:2>'<arg:3></yellow>"
warning.config.block.state.no_model_set: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必的 'model' 或 'models' 模型参数</yellow>"
warning.config.block.state.invalid_real_state_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的真实方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'。若槽位已用尽,请考虑在 additional-real-blocks.yml 中添加更多真实状态</yellow>"
warning.config.block.state.model.lack_path: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'model' 配置缺少必要的 'path' 路径参数</yellow>"
warning.config.block.state.model.invalid_resource_location: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'path' 路径参数 [<arg:2>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型,多个配置尝试用相同路径 '<arg:2>' 生成不同的JSON模型</yellow>"
warning.config.model.generation.texture.invalid_resource_location: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 '<arg:2>' 纹理参数 [<arg:3>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.model.generation.parent.invalid_resource_location: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的父模型参数 [<arg:2>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.emoji.lack_keywords: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 缺少必要的 'keywords' 配置</yellow>"
warning.config.emoji.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 重复定义</yellow>"
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 使用了无效的 'image' 图片参数 '<arg:2>'.</yellow>"
warning.config.advancement.duplicated: "<yellow>在文件 <arg:0> 发现问题 - 进度 '<arg:1>' 重复定义</yellow>"
warning.config.pack.duplicated_files: "</red>发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决</red>"
warning.config.type.int: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为整数类型 (选项 '<arg:3>')</yellow>"
warning.config.type.float: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为浮点数类型 (选项 '<arg:3>')</yellow>"
warning.config.type.double: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为双精度类型 (选项 '<arg:3>')</yellow>"
warning.config.type.quaternionf: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为四元数类型 (选项 '<arg:3>')</yellow>"
warning.config.type.vector3f: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为三维向量类型 (选项 '<arg:3>')</yellow>"
warning.config.structure.not_section: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 应为配置段落 但实际类型为 '<arg:2>'</yellow>"
warning.config.image.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的图片配置 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.image.missing_height: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'height' 参数</yellow>"
warning.config.image.height_ascent_conflict: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 违反位图规则: 'height' 参数 '<arg:2>' 必须不小于 'ascent' 参数 '<arg:3>'</yellow>"
warning.config.image.missing_file: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'file' 参数</yellow>"
warning.config.image.invalid_file_chars: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'file' 参数 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.image.invalid_font_chars: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'font' 参数 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.image.missing_char: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'char' 参数</yellow>"
warning.config.image.codepoint_conflict: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 在字体 <arg:2> 中使用的字符 '<arg:3>(<arg:4>)' 已被其他图片 '<arg:5>' 占用</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 'chars' 码位网格无效</yellow>"
warning.config.image.file_not_found: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 的 PNG 文件 '<arg:2>' 未找到</yellow>"
warning.config.image.invalid_hex_value: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 使用的 Unicode 字符 '<arg:2>' 不是有效的十六进制值</yellow>"
warning.config.recipe.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的配方 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.recipe.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 缺少必的 'type' 参数</yellow>"
warning.config.recipe.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 使用了无效的配方类型 '<arg:2>'</yellow>"
warning.config.recipe.invalid_item: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 使用了无效的物品 '<arg:2>'</yellow>"
warning.config.recipe.missing_ingredient: "<yellow>在文件 <arg:0> 发现问题 - 烧炼配方 '<arg:1>' 缺少必需的 'ingredient' 参数</yellow>"
warning.config.recipe.missing_result: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 缺少必需的 'result' 参数</yellow>"
warning.config.recipe.result.missing_id: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 的结果缺少必需的 'id' 参数</yellow>"
warning.config.recipe.crafting.invalid_category: "<yellow>在文件 <arg:0> 发现问题 - 合成配方 '<arg:1>' 使用了无效的分类 '<arg:2>' 允许的分类: [<arg:3>]</yellow>"
warning.config.recipe.cooking.invalid_category: "<yellow>在文件 <arg:0> 发现问题 - 烧炼配方 '<arg:1>' 使用了无效的分类 '<arg:2>' 允许的分类: [<arg:3>]</yellow>"
warning.config.recipe.shaped.missing_pattern: "<yellow>在文件 <arg:0> 发现问题 - 有序合成配方 '<arg:1>' 缺少必的 'pattern' 参数</yellow>"
warning.config.recipe.shaped.invalid_pattern: "<yellow>在文件 <arg:0> 发现问题 - 有序合成配方 '<arg:1>' 使用了无效的图案 '<arg:2>'</yellow>"
warning.config.recipe.shaped.invalid_symbol: "<yellow>在文件 <arg:0> 发现问题 - 有序合成配方 '<arg:1>' 在图案中使用了无效符号 '<arg:2>'</yellow>"
warning.config.recipe.smithing_transform.post_processor.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的后处理器缺少必的 'type' 参数</yellow>"
warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 使用了无效的后处理器类型 '<arg:2>'</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_components' 后处理器缺少必的 'components' 参数</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_tags' 后处理器缺少必的 'tags' 参数</yellow>"
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言环境 '<arg:1>'</yellow>"
warning.config.template.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的模板 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 在 'self_increase_int' 参数中使用了一个起始值 '<arg:2>' 大于终止值 '<arg:3>'</yellow>"
warning.config.template.argument.list.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 的 'list' 参数需要列表类型 但输入参数类型为 '<arg:2>'</yellow>"
warning.config.vanilla_loot.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
warning.config.vanilla_loot.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 使用了无效类型 '<arg:2>' 允许的类型: [<arg:3>]</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 中存在无效的方块目标 '<arg:2>'</yellow>"
warning.config.sound.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的音效 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.sound.missing_sounds: "<yellow>在文件 <arg:0> 发现问题 - 音效 '<arg:1>' 缺少必的 'sounds' 参数</yellow>"
warning.config.sound.missing_name: "<yellow>在文件 <arg:0> 发现问题 - 音效 '<arg:1>' 缺少必需的 'name' 参数</yellow>"
warning.config.jukebox_song.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的唱片机歌曲 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.jukebox_song.missing_sound: "<yellow>在文件 <arg:0> 发现问题 - 唱片机歌曲 '<arg:1>' 缺少必需的 'sound' 参数</yellow>"
warning.config.furniture.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的家具 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.furniture.missing_placement: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 缺少必需的 'placement' 参数</yellow>"
warning.config.furniture.element.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 的某个元素缺少必需的 'item' 参数</yellow>"
warning.config.furniture.settings.unknown: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 使用了未知的设置类型 '<arg:2>'</yellow>"
warning.config.furniture.hitbox.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 使用了无效的碰撞箱类型 '<arg:2>'</yellow>"
warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>在文件 <arg:0> 发现问题 - 家具 '<arg:1>' 的自定义碰撞箱使用了无效的实体类型 '<arg:2>'</yellow>"
warning.config.item.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的物品 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.item.settings.unknown: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了未知的设置类型 '<arg:2>'</yellow>"
warning.config.item.missing_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必需的 'material' 参数</yellow>"
warning.config.item.invalid_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的材料类型 '<arg:2>'</yellow>"
warning.config.item.bad_custom_model_data: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用的自定义模型数据 '<arg:2>' 数值过大 建议使用小于 16,777,216 的值</yellow>"
warning.config.item.custom_model_data_conflict: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用的自定义模型数据 '<arg:2>' 已被物品 '<arg:3>' 占用</yellow>"
warning.config.item.missing_model_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必需的 'custom-model-data' 或 'item-model' 参数</yellow>"
warning.config.item.behavior.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的行为配置缺少必需的 'type' 参数</yellow>"
warning.config.item.behavior.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的行为类型 '<arg:2>'</yellow>"
warning.config.item.behavior.block.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'block_item' 行为缺少必需的 'block' 参数</yellow>"
warning.config.item.behavior.furniture.missing_furniture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'furniture_item' 行为缺少必需的 'furniture' 参数</yellow>"
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数</yellow>"
warning.config.item.model.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的模型类型 '<arg:2>'</yellow>"
warning.config.item.model.tint.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的染色配置缺少必需的 'type' 参数</yellow>"
warning.config.item.model.tint.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的染色类型 '<arg:2>'</yellow>"
warning.config.item.model.tint.constant.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的固定染色配置缺少必需的 'value' 参数</yellow>"
warning.config.item.model.tint.grass.invalid_temp: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的草地染色使用了无效的温度值 '<arg:2>' 有效范围应为 0 至 1</yellow>"
warning.config.item.model.tint.grass.invalid_downfall: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的草地染色使用了无效的降水值 '<arg:2>' 有效范围应为 0 至 1</yellow>"
warning.config.item.model.tint.invalid_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的染色值 '<arg:2>'</yellow>"
warning.config.item.model.base.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:model' 模型缺少必需的 'path' 参数</yellow>"
warning.config.item.model.base.invalid_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:model' 模型路径 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.item.model.condition.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:condition' 模型缺少必需的 'property' 参数</yellow>"
warning.config.item.model.condition.invalid_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:condition' 模型使用了无效属性 '<arg:2>'</yellow>"
warning.config.item.model.condition.missing_on_true: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:condition' 模型缺少必需的 'on-true' 参数</yellow>"
warning.config.item.model.condition.missing_on_false: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:condition' 模型缺少必需的 'on-false' 参数</yellow>"
warning.config.item.model.condition.keybind.missing: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数</yellow>"
warning.config.item.model.condition.has_component.missing_component: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:has_component' 属性缺少必需的 'component' 参数</yellow>"
warning.config.item.model.composite.missing_models: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:composite' 模型缺少必需的 'models' 参数</yellow>"
warning.config.item.model.range_dispatch.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:range_dispatch' 模型缺少必需的 'property' 参数</yellow>"
warning.config.item.model.range_dispatch.invalid_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:range_dispatch' 模型使用了无效属性 '<arg:2>'</yellow>"
warning.config.item.model.range_dispatch.missing_entries: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:composite' 模型缺少必需的 'entries' 参数</yellow>"
warning.config.item.model.range_dispatch.entry.missing_model: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:composite' 模型的某个条目缺少必需的 'model' 参数</yellow>"
warning.config.item.model.range_dispatch.compass.missing_target: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:compass' 属性缺少必需的 'target' 参数</yellow>"
warning.config.item.model.range_dispatch.time.missing_source: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:time' 属性缺少必需的 'source' 参数</yellow>"
warning.config.item.model.select.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:select' 模型缺少必需的 'property' 参数</yellow>"
warning.config.item.model.select.invalid_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:select' 模型使用了无效属性 '<arg:2>'</yellow>"
warning.config.item.model.select.missing_cases: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:select' 模型缺少必需的 'cases' 参数</yellow>"
warning.config.item.model.select.case.missing_when: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:select' 模型的某个 case 缺少必需的 'when' 参数</yellow>"
warning.config.item.model.select.case.missing_model: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:select' 模型的某个 case 缺少必需的 'model' 参数</yellow>"
warning.config.item.model.select.block_state.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:block_state' 属性缺少必需的 'block-state-property' 参数</yellow>"
warning.config.item.model.select.local_time.missing_pattern: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:local_time' 属性缺少必需的 'pattern' 参数</yellow>"
warning.config.item.model.special.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型缺少必需的 'type' 参数</yellow>"
warning.config.item.model.special.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型使用了无效类型 '<arg:2>'</yellow>"
warning.config.item.model.special.banner.missing_color: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:banner' 特殊模型缺少必需的 'color' 参数</yellow>"
warning.config.item.model.special.bed.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:bed' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.item.model.special.sign.missing_wood_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:hanging_sign'/'minecraft:standing_sign' 特殊模型缺少必需的 'wood-type' 参数</yellow>"
warning.config.item.model.special.sign.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:hanging_sign'/'minecraft:standing_sign' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.item.model.special.chest.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:chest' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.item.model.special.chest.invalid_openness: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:chest' 特殊模型使用了无效的 'openness' 值 '<arg:2>' 有效范围应为 0~1</yellow>"
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:shulker_box' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:shulker_box' 特殊模型使用了无效的 'openness' 值 '<arg:2>' 有效范围应为 0~1</yellow>"
warning.config.item.model.special.head.missing_kind: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:head' 特殊模型缺少必需的 'kind' 参数</yellow>"
warning.config.item.model.special.head.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:head' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.block.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的方块 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.block.missing_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必需的 'state' 参数</yellow>"
warning.config.block.state.property.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:2>' 缺少必需的 'type' 参数</yellow>"
warning.config.block.state.property.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:3>' 使用了无效的类型参数 '<arg:2>'</yellow>"
warning.config.block.state.property.integer.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的整数属性 '<arg:3>' 使用了无效的范围参数 '<arg:2>' 正确语法: 1~2</yellow>"
warning.config.block.state.property.invalid_format: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的方块状态属性格式 '<arg:2>'.</yellow>"
warning.config.block.state.missing_real_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'state' 缺少必需的 'id' 参数 该 ID 是服务端方块 ID 用于唯一标识每种方块状态类型</yellow>"
warning.config.block.state.missing_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'state' 缺少必需的 'state' 参数</yellow>"
warning.config.block.state.missing_properties: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'states' 缺少必需的 'properties' 段落</yellow>"
warning.config.block.state.missing_appearances: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'states' 缺少必需的 'appearances' 段落</yellow>"
warning.config.block.state.missing_variants: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'states' 缺少必需的 'variants' 段落</yellow>"
warning.config.block.state.variant.missing_appearance: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的变体 '<arg:2>' 缺少必需的 'appearance' 参数</yellow>"
warning.config.block.state.variant.invalid_appearance: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的变体 '<arg:2>' 使用了不存在的 appearance '<arg:3>'</yellow>"
warning.config.block.state.invalid_vanilla: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的原版方块状态 '<arg:2>'</yellow>"
warning.config.block.state.unavailable_vanilla: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了不可用的原版方块状态 '<arg:2>' 请在 mappings.yml 中释放该状态</yellow>"
warning.config.block.state.invalid_vanilla_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'</yellow>"
warning.config.block.state.conflict: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.bind_failed: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 无法为 '<arg:2>' 绑定真实方块状态 因该状态已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.missing_model: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必需的 'model' 或 'models' 参数</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的真实方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态</yellow>"
warning.config.block.state.model.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'model' 缺少必需的 'path' 选项</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'path' 参数 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.block.settings.unknown: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了未知的设置类型 '<arg:2>'</yellow>"
warning.config.block.behavior.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的行为配置缺少必需的 'type' 参数</yellow>"
warning.config.block.behavior.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的行为类型 '<arg:2>'</yellow>"
warning.config.block.behavior.concrete.missing_solid: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'concrete_block' 行为缺少必需的 'solid-block' 选项</yellow>"
warning.config.block.behavior.crop.missing_age: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'crop_block' 行为缺少必需的 'age' 属性</yellow>"
warning.config.block.behavior.sugar_cane.missing_age: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'sugar_cane_block' 行为缺少必需的 'age' 属性</yellow>"
warning.config.block.behavior.leaves.missing_persistent: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'leaves_block' 行为缺少必需的 'persistent' 属性</yellow>"
warning.config.block.behavior.leaves.missing_distance: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'leaves_block' 行为缺少必需的 'distance' 属性</yellow>"
warning.config.block.behavior.sapling.missing_stage: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'sapling_block' 行为缺少必需的 'stage' 属性</yellow>"
warning.config.block.behavior.sapling.missing_feature: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'sapling_block' 行为缺少必需的 'feature' 参数</yellow>"
warning.config.block.behavior.strippable.missing_stripped: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'strippable_block' 行为缺少必需的 'stripped' 参数</yellow>"
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
warning.config.model.generation.texture.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的纹理 '<arg:2>' 路径 '<arg:3>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.model.generation.parent.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的父级参数 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.emoji.missing_keywords: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 缺少必需的 'keywords' 参数</yellow>"
warning.config.emoji.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的表情 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 的 'image' 参数 '<arg:2>' 无效</yellow>"
warning.config.advancement.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的进度 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.loot_table.missing_pools: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 缺少必需的 'pools' 参数</yellow>"
warning.config.loot_table.invalid_pools_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'pools' 应为字符串/映射列表 当前类型: '<arg:2>'</yellow>"
warning.config.loot_table.invalid_conditions_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'conditions' 应为映射列表 当前类型: '<arg:2>'</yellow>"
warning.config.loot_table.invalid_functions_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'functions' 应为映射列表 当前类型: '<arg:2>'</yellow>"
warning.config.loot_table.invalid_entries_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'entries' 应为映射列表 当前类型: '<arg:2>'</yellow>"
warning.config.loot_table.function.missing_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个函数缺少必需的 'type' 参数</yellow>"
warning.config.loot_table.function.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个函数使用了无效的函数类型 '<arg:2>'</yellow>"
warning.config.loot_table.function.apply_bonus.missing_enchantment: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'enchantment' 参数</yellow>"
warning.config.loot_table.function.apply_bonus.missing_formula: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'formula' 参数</yellow>"
warning.config.loot_table.function.drop_exp.missing_count: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'drop_exp' 函数缺少必需的 'count' 参数</yellow>"
warning.config.loot_table.function.set_count.missing_count: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'set_count' 函数缺少必需的 'count' 参数</yellow>"
warning.config.loot_table.entry.missing_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个条目缺少必需的 'type' 参数</yellow>"
warning.config.loot_table.entry.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个条目使用了无效的条目类型 '<arg:2>'</yellow>"
warning.config.loot_table.entry.exp.missing_count: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'exp' 条目缺少必需的 'count' 参数</yellow>"
warning.config.loot_table.entry.item.missing_item: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'item' 条目缺少必需的 'item' 参数</yellow>"
warning.config.loot_table.condition.missing_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个条件缺少必需的 'type' 参数</yellow>"
warning.config.loot_table.condition.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个条件使用了无效的条件类型 '<arg:2>'</yellow>"
warning.config.loot_table.condition.table_bonus.missing_enchantment: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'table_bonus' 条件缺少必需的 'enchantment' 参数</yellow>"
warning.config.loot_table.condition.table_bonus.missing_chances: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 'table_bonus' 条件缺少必需的 'chances' 参数</yellow>"
warning.config.loot_table.number.missing_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个数值缺少必需的 'type' 参数</yellow>"
warning.config.loot_table.number.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - '<arg:1>' 的战利品表配置错误 某个数值使用了无效的数值类型 '<arg:2>'</yellow>"
warning.config.host.missing_type: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 缺少必需的 'type' 参数</yellow>"
warning.config.host.invalid_type: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 无效的托管类型 '' 请参考 https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host</yellow>"
warning.config.host.external.missing_url: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 外部托管缺少必需的 'url' 参数</yellow>"
warning.config.host.alist.missing_api_url: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'api-url' 参数</yellow>"
warning.config.host.alist.missing_username: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'username' 参数或环境变量 'CE_ALIST_USERNAME'</yellow>"
warning.config.host.alist.missing_password: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'password' 参数或环境变量 'CE_ALIST_PASSWORD'</yellow>"
warning.config.host.alist.missing_upload_path: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Alist 托管缺少必需的 'upload-path' 参数</yellow>"
warning.config.host.dropbox.missing_app_key: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'app-key' 参数或环境变量 'CE_DROPBOX_APP_KEY'</yellow>"
warning.config.host.dropbox.missing_app_secret: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'app-secret' 参数或环境变量 'CE_DROPBOX_APP_SECRET'</yellow>"
warning.config.host.dropbox.missing_refresh_token: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'refresh-token' 参数或环境变量 'CE_DROPBOX_REFRESH_TOKEN'</yellow>"
warning.config.host.dropbox.missing_upload_path: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Dropbox 托管缺少必需的 'upload-path' 参数</yellow>"
warning.config.host.lobfile.missing_api_key: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - Lobfile 托管缺少必需的 'api-key' 参数</yellow>"
warning.config.host.onedrive.missing_client_id: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'client-id' 参数或环境变量 'CE_ONEDRIVE_CLIENT_ID'</yellow>"
warning.config.host.onedrive.missing_client_secret: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'client-secret' 参数或环境变量 'CE_ONEDRIVE_CLIENT_SECRET'</yellow>"
warning.config.host.onedrive.missing_refresh_token: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'refresh-token' 参数或环境变量 'CE_ONEDRIVE_REFRESH_TOKEN'</yellow>"
warning.config.host.onedrive.missing_upload_path: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - OneDrive 托管缺少必需的 'upload-path' 参数</yellow>"
warning.config.host.s3.missing_endpoint: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'endpoint' 参数</yellow>"
warning.config.host.s3.missing_bucket: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'bucket' 参数</yellow>"
warning.config.host.s3.missing_access_key: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'access-key-id' 参数或环境变量 'CE_S3_ACCESS_KEY_ID'</yellow>"
warning.config.host.s3.missing_secret: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'access-key-secret' 参数或环境变量 'CE_S3_ACCESS_KEY_SECRET'</yellow>"
warning.config.host.s3.missing_upload_path: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - S3 托管缺少必需的 'upload-path' 参数</yellow>"
warning.config.host.self.missing_ip: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 自托管托管缺少必需的 'ip' 参数</yellow>"
warning.config.host.self.invalid_port: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 自托管托管的端口 '<arg:0>' 无效</yellow>"
warning.config.host.gitlab.missing_url: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'gitlab-url' 参数</yellow>"
warning.config.host.gitlab.missing_token: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'access-token' 参数</yellow>"
warning.config.host.gitlab.missing_project: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - GitLab 托管缺少必需的 'project-id' 参数</yellow>"
warning.config.host.proxy.missing_host: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'host' 参数</yellow>"
warning.config.host.proxy.missing_port: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'port' 参数</yellow>"
warning.config.host.proxy.missing_scheme: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 代理配置缺少必需的 'scheme' 参数</yellow>"
warning.config.host.proxy.invalid: "<yellow>在 config.yml 的 'resource-pack.delivery.hosting' 处发现问题 - 无效的代理配置 '<arg:0>'</yellow>"
warning.config.conflict_matcher.missing_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个匹配器缺少必需的 'type' 参数</yellow>"
warning.config.conflict_matcher.invalid_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个匹配器使用了无效类型 '<arg:0>'</yellow>"
warning.config.conflict_matcher.exact.missing_path: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 精确路径匹配器缺少必需的 'path' 参数</yellow>"
warning.config.conflict_matcher.contains.missing_path: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 路径包含匹配器缺少必需的 'path' 参数</yellow>"
warning.config.conflict_matcher.filename.missing_name: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件名匹配器缺少必需的 'path' 参数</yellow>"
warning.config.conflict_matcher.pattern.missing_pattern: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 正则表达式匹配器缺少必需的 'pattern' 参数</yellow>"
warning.config.conflict_matcher.parent_prefix.missing_prefix: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 父路径前缀匹配器缺少必需的 'prefix' 参数</yellow>"
warning.config.conflict_matcher.parent_suffix.missing_suffix: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 父路径后缀匹配器缺少必需的 'suffix' 参数</yellow>"
warning.config.conflict_matcher.inverted.missing_term: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 反向匹配器缺少必需的 'term' 参数</yellow>"
warning.config.conflict_matcher.all_of.missing_terms: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 全匹配器缺少必需的 'terms' 参数</yellow>"
warning.config.conflict_matcher.any_of.missing_terms: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 任一匹配器缺少必需的 'terms' 参数</yellow>"
warning.config.conflict_resolution.missing_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案缺少必需的 'type' 参数</yellow>"
warning.config.conflict_resolution.invalid_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 '<arg:0>'</yellow>"

View File

@@ -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<String, Object> 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);

View File

@@ -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<String, Object> 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<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, VariantState> variants;
Map<String, Object> stateSection = MiscUtils.castToMap(section.get("state"), true);
if (stateSection != null) {
Object stateObj = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state");
Map<String, Object> 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<Key, Integer> pair = parseAppearanceSection(pack, path, id, stateSection);
Pair<Key, Integer> 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<String, Object> statesSection = MiscUtils.castToMap(section.get("states"), true);
if (statesSection == null) {
TranslationManager.instance().log("warning.config.block.lack_state", path.toString(), id.toString());
return;
}
// properties
Map<String, Object> propertySection = MiscUtils.castToMap(statesSection.get("properties"), true);
Map<String, Object> 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<String, Object> appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true);
Map<String, Object> 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<String, Key> tempTypeMap = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
Pair<Key, Integer> pair = parseAppearanceSection(pack, path, id, MiscUtils.castToMap(appearanceSection, false));
Pair<Key, Integer> 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<String, Object> variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true);
Map<String, Object> 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<String, Object> 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<String, Object> 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<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
// create block
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable);
Map<String, Object> 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<String, Property<?>> parseProperties(Path path, Key id, Map<String, Object> propertiesSection) {
private Map<String, Property<?>> parseProperties(Map<String, Object> propertiesSection) {
Map<String, Property<?>> properties = new HashMap<>();
for (Map.Entry<String, Object> 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<Key, Integer> parseAppearanceSection(Pack pack, Path path, Key id, Map<String, Object> section) {
private Pair<Key, Integer> parseAppearanceSection(Key id, Map<String, Object> 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<JsonObject> 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<JsonObject> variants, Map<String, Object> singleModelMap) {
private void loadVariantModel(List<JsonObject> variants, Map<String, Object> singleModelMap) {
JsonObject json = new JsonObject();
String modelPath = (String) singleModelMap.get("path");
if (modelPath == null) {
TranslationManager.instance().log("warning.config.block.state.model.lack_path", path.toString(), id.toString());
return;
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<String, Object> 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<Integer> 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<Object, Material> magicMap = (Map<Object, Material>) 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<Material, Object> factories = (Map<Material, Object>) 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);
}
}
}
}

View File

@@ -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<CustomBlock> holder,
Map<String, Property<?>> properties,
Map<String, Integer> appearances,
Map<String, VariantState> variantMapper,
BlockSettings settings,
Map<String, Object> behaviorSettings,
Map<String, Object> 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<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable);
}
}
}

View File

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

View File

@@ -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<String, Object> 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));
}
}

View File

@@ -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<String, Object> arguments) {
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
Property<Integer> ageProperty = (Property<Integer>) 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<Integer> ageProperty = (Property<Integer>) 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);

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> arguments) {
return new GrassBlockBehavior(block);
}
}
}

View File

@@ -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<String, Object> arguments) {
Property<Boolean> persistent = (Property<Boolean>) block.getProperty("persistent");
if (persistent == null) {
throw new NullPointerException("persistent property not set for block " + block.id());
}
Property<Integer> distance = (Property<Integer>) block.getProperty("distance");
if (distance == null) {
throw new NullPointerException("distance not set for block " + block.id());
}
Property<Boolean> persistent = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("persistent"), "warning.config.block.behavior.leaves.missing_persistent");
Property<Integer> distance = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("distance"), "warning.config.block.behavior.leaves.missing_distance");
Property<Boolean> waterlogged = (Property<Boolean>) block.getProperty("waterlogged");
int actual = distance.possibleValues().get(distance.possibleValues().size() - 1);
return new LeavesBlockBehavior(block, actual, distance, persistent, waterlogged);

View File

@@ -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<String, Object> arguments) {
String feature = (String) arguments.get("feature");
if (feature == null) {
throw new IllegalArgumentException("feature is null");
}
Property<Integer> stageProperty = (Property<Integer>) 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<Integer> stageProperty = (Property<Integer>) 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<List<Object>, Set<Object>, Set<String>> 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"));
}
}
}

View File

@@ -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<String, Object> 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));
}
}

View File

@@ -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<String, Object> arguments) {
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
Property<Integer> ageProperty = (Property<Integer>) 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<Integer> ageProperty = (Property<Integer>) 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<String> 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"));
}
}
}

View File

@@ -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<String, Object> 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<String, Object> lootMap = MiscUtils.castToMap(section.get("loot"), true);
Map<String, Object> settingsMap = MiscUtils.castToMap(section.get("settings"), true);
Map<String, Object> placementMap = MiscUtils.castToMap(section.get("placement"), true);
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
if (placementMap == null) {
TranslationManager.instance().log("warning.config.furniture.lack_placement", path.toString(), id.toString());
return;
throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement", path, id);
}
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
for (Map.Entry<String, Object> 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<String, Object> element : elementConfigs) {
String key = (String) element.get("item");
if (key == null) {
TranslationManager.instance().log("warning.config.furniture.element.lack_item", path.toString(), id.toString());
return;
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<ItemStack> 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;

View File

@@ -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<String, Object> 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);

View File

@@ -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<String, Object> 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
);
}

View File

@@ -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<String, Object> 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);

View File

@@ -272,9 +272,9 @@ public class ShulkerHitBox extends AbstractHitBox {
@Override
public HitBox create(Map<String, Object> 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);

View File

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

View File

@@ -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<ItemStack> {
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (customItems.containsKey(id)) {
TranslationManager.instance().log("warning.config.item.duplicated", path.toString(), id.toString());
return;
throw new LocalizedResourceConfigException("warning.config.item.duplicate");
}
// just register for recipes
// register for recipes
Holder.Reference<Key> holder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(id)
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
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<ItemStack> itemBuilder = BukkitCustomItem.builder().id(id).material(materialId);
@@ -342,13 +339,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
}
}
ItemSettings itemSettings;
if (section.containsKey("settings")) {
Map<String, Object> 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<ItemStack> {
return;
}
ItemModel model = ItemModels.fromMap(modelSection);
boolean hasModel = false;
if (customModelData != 0) {
hasModel= true;
@@ -399,20 +391,18 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
// check conflict
Map<Integer, Key> conflict = cmdConflictChecker.computeIfAbsent(materialId, k -> new HashMap<>());
if (conflict.containsKey(customModelData)) {
TranslationManager.instance().log("warning.config.item.custom_model_data_conflict", path.toString(), id.toString(), String.valueOf(customModelData), conflict.get(customModelData).toString());
return;
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<ItemStack> {
}
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<ItemStack> {
}
}
if (!hasModel) {
TranslationManager.instance().log("warning.config.item.lack_model_id", path.toString(), id.toString());
throw new LocalizedResourceConfigException("warning.config.item.missing_model_id");
}
}
}

View File

@@ -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<String, Object> 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())) {

View File

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

View File

@@ -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<String, Object> 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())) {

View File

@@ -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<String, Object> 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())) {
// 防呆

View File

@@ -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<String, Object> section) {
String type = (String) section.get("type");
if (type == null) {
TranslationManager.instance().log("warning.config.vanilla_loot.type_not_exist", path.toString(), id.toString());
return;
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<String> 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);

View File

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

View File

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

View File

@@ -2286,4 +2286,19 @@ public class PacketConsumers {
CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> 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);
}
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<CustomBlock> holder,
@NotNull Map<String, Property<?>> properties,
@NotNull Map<String, Integer> appearances,
@NotNull Map<String, VariantState> variantMapper,
@NotNull BlockSettings settings,
@Nullable Map<String, Object> behaviorSettings,
@Nullable Map<String, Object> 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<String, VariantState> 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<String, Property<?>> properties;
protected Map<String, Integer> appearances;
protected Map<String, VariantState> variantMapper;
protected BlockSettings settings;
protected Map<String, Object> behavior;
protected LootTable<?> lootTable;
protected Builder(Key id) {
this.id = id;
}
public Builder appearances(Map<String, Integer> appearances) {
this.appearances = appearances;
return this;
}
public Builder behavior(Map<String, Object> behavior) {
this.behavior = behavior;
return this;
}
public Builder lootTable(LootTable<?> lootTable) {
this.lootTable = lootTable;
return this;
}
public Builder properties(Map<String, Property<?>> properties) {
this.properties = properties;
return this;
}
public Builder settings(BlockSettings settings) {
this.settings = settings;
return this;
}
public Builder variantMapper(Map<String, VariantState> variantMapper) {
this.variantMapper = variantMapper;
return this;
}
public Map<String, Integer> appearances() {
return appearances;
}
public Map<String, Object> behavior() {
return behavior;
}
public Key id() {
return id;
}
public LootTable<?> lootTable() {
return lootTable;
}
public Map<String, Property<?>> properties() {
return properties;
}
public BlockSettings settings() {
return settings;
}
public Map<String, VariantState> variantMapper() {
return variantMapper;
}
public abstract CustomBlock build();
}
}

View File

@@ -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<CustomBlock> holder) {
super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), null, null);
INSTANCE = this;
STATE = defaultState();
}
@Override

View File

@@ -51,7 +51,7 @@ public class ImmutableBlockState extends BlockStateHolder {
}
public boolean isEmpty() {
return this == EmptyBlock.INSTANCE.defaultState();
return this == EmptyBlock.STATE;
}
@Override

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<T extends Enum<T>> extends Property<T> {
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);
}
}

View File

@@ -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<Integer> {
public Property<?> create(String name, Map<String, Object> 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);
}
}
}
}

View File

@@ -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<String, Object> 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);
}

View File

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

View File

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

View File

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

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<String, Object> 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<String> 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", "<arg:emoji>").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<String, Object> section) {
if (images.containsKey(id)) {
TranslationManager.instance().log("warning.config.image.duplicated", path.toString(), id.toString());
return;
}
Object heightObj = section.get("height");
if (heightObj == null) {
TranslationManager.instance().log("warning.config.image.lack_height", path.toString(), id.toString());
return;
}
int height = MiscUtils.getAsInt(heightObj);
int ascent = MiscUtils.getAsInt(section.getOrDefault("ascent", height - 1));
if (height < ascent) {
TranslationManager.instance().log("warning.config.image.height_smaller_than_ascent", path.toString(), id.toString());
return;
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<char[]> 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);
}
}
}

View File

@@ -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<JsonObject> {
private final Key id;
private final Key font;
private final int height;

View File

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

View File

@@ -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<String, Object> 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<Map<String, Object>> materials = (List<Map<String, Object>>) value;
List<AnvilRepairItem> anvilRepairItemList = new ArrayList<>();
for (Map<String, Object> 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);
}));

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<T> implements RecipeFactory<T> {
protected List<String> ingredients(Map<String, Object> arguments) {
return MiscUtils.getAsStringList(getIngredientOrThrow(arguments));
}
protected Map<String, Object> ingredientMap(Map<String, Object> arguments) {
return MiscUtils.castToMap(getIngredientOrThrow(arguments), true);
}
protected Set<Holder<Key>> ingredientHolders(Map<String, Object> arguments) {
Set<Holder<Key>> 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<String, Object> 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<String, Object> 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<String, Object> 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;
}
}

View File

@@ -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<T> implements RecipeManager<T> {
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (!Config.enableRecipeSystem()) return;
if (AbstractRecipeManager.this.byId.containsKey(id)) {
TranslationManager.instance().log("warning.config.recipe.duplicated", path.toString(), id.toString());
return;
}
Recipe<T> recipe;
try {
recipe = RecipeTypes.fromMap(id, section);
} catch (Exception e) {
CraftEngine.instance().logger().warn(path, "Failed to create recipe: " + id, e);
return;
throw new LocalizedResourceConfigException("warning.config.recipe.duplicate", path, id);
}
Recipe<T> recipe = RecipeTypes.fromMap(id, section);
try {
markAsCustomRecipe(id);
registerInternalRecipe(id, recipe);

View File

@@ -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<T> extends CustomCookingRecipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
@@ -21,33 +20,16 @@ public class CustomBlastingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.BLASTING;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> 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<String> items = MiscUtils.getAsStringList(arguments.get("ingredient"));
Set<Holder<Key>> 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<Holder<Key>> holders = ingredientHolders(arguments);
return new CustomBlastingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments));
}
}
}

View File

@@ -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<T> extends CustomCookingRecipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
@@ -21,33 +20,16 @@ public class CustomCampfireRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.CAMPFIRE_COOKING;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> 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<String> items = MiscUtils.getAsStringList(arguments.get("ingredient"));
Set<Holder<Key>> 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<Holder<Key>> holders = ingredientHolders(arguments);
return new CustomCampfireRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments));
}
}
}

View File

@@ -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<T> extends CustomCraftingTableRecipe<T> {
}
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> arguments) {
List<String> 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<String, Object> 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<Character, Ingredient<A>> ingredients = new HashMap<>();
for (Map.Entry<String, Object> entry : ingredientMap.entrySet()) {
for (Map.Entry<String, Object> 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<String> items = MiscUtils.getAsStringList(entry.getValue());
@@ -164,18 +161,13 @@ public class CustomShapedRecipe<T> extends CustomCraftingTableRecipe<T> {
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<String> pattern) {

View File

@@ -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<T> extends CustomCraftingTableRecipe<T> {
return RecipeTypes.SHAPELESS;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> arguments) {
Map<String, Object> 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<Ingredient<A>> ingredients = new ArrayList<>();
for (Map.Entry<String, Object> entry : ingredientMap.entrySet()) {
List<String> items = MiscUtils.getAsStringList(entry.getValue());
Set<Holder<Key>> 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<String, Object> entry : (MiscUtils.castToMap(map, false)).entrySet()) {
List<String> items = MiscUtils.getAsStringList(entry.getValue());
Set<Holder<Key>> 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<Holder<Key>> 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<Holder<Key>> 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<Holder<Key>> 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));
}
}
}

View File

@@ -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<T> extends CustomCookingRecipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
@@ -21,33 +20,16 @@ public class CustomSmeltingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.SMELTING;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> 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<String> items = MiscUtils.getAsStringList(arguments.get("ingredient"));
Set<Holder<Key>> 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<Holder<Key>> holders = ingredientHolders(arguments);
return new CustomSmeltingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments));
}
}
}

View File

@@ -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<T> implements Recipe<T> {
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<T> implements Recipe<T> {
public static ItemDataProcessor fromMap(Map<String, Object> 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<T> implements Recipe<T> {
@Override
public ItemDataProcessor create(Map<String, Object> arguments) {
List<String> 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<String> components = MiscUtils.getAsStringList(componentsObj);
return new KeepComponents(components.stream().map(Key::of).toList());
}
}
@@ -266,7 +272,11 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
@Override
public ItemDataProcessor create(Map<String, Object> arguments) {
List<String> 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<String> tags = MiscUtils.getAsStringList(tagsObj);
return new KeepTags(tags.stream().map(it -> it.split("\\.")).toList());
}
}

View File

@@ -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<T> extends CustomCookingRecipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
@@ -21,33 +20,16 @@ public class CustomSmokingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.SMOKING;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> 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<String> items = MiscUtils.getAsStringList(arguments.get("ingredient"));
Set<Holder<Key>> 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<Holder<Key>> holders = ingredientHolders(arguments);
return new CustomSmokingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments));
}
}
}

View File

@@ -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<T> extends AbstractGroupedRecipe<T> {
return ingredient;
}
public static class Factory<A> implements RecipeFactory<A> {
public static class Factory<A> extends AbstractRecipeFactory<A> {
@SuppressWarnings({"DuplicatedCode"})
@Override
public Recipe<A> create(Key id, Map<String, Object> arguments) {
String group = arguments.containsKey("group") ? arguments.get("group").toString() : null;
List<String> items = MiscUtils.getAsStringList(arguments.get("ingredient"));
Set<Holder<Key>> 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<Holder<Key>> holders = ingredientHolders(arguments);
return new CustomStoneCuttingRecipe<>(id, group, Ingredient.of(holders), parseResult(arguments));
}
}
}

View File

@@ -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<T> {
default CustomRecipeResult<T> parseResult(Map<String, Object> arguments) {
Map<String, Object> 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
);
}

View File

@@ -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 <T> Recipe<T> fromMap(Key id, Map<String, Object> 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<T> factory = (RecipeFactory<T>) 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);
}

View File

@@ -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<T> {
private final List<LootPool<T>> pools;
@@ -38,25 +41,40 @@ public class LootTable<T> {
@SuppressWarnings("unchecked")
public static <T> LootTable<T> fromMap(Map<String, Object> map) {
if (map == null || map.isEmpty()) return null;
List<Map<String, Object>> poolList = (List<Map<String, Object>>) 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<Object> poolList = (List<Object>) map.get("pools");
List<LootPool<T>> lootPools = new ArrayList<>();
for (Map<String, Object> pool : poolList) {
NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1));
NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0));
List<LootCondition> 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<LootEntryContainer<T>> containers = Optional.ofNullable(pool.get("entries"))
.map(it -> (List<LootEntryContainer<T>>) new ArrayList<LootEntryContainer<T>>(LootEntryContainers.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'entries' should be a map list, current type: " + it.getClass().getSimpleName())))))
.orElse(Lists.newArrayList());
List<LootFunction<T>> functions = Optional.ofNullable(pool.get("functions"))
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(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<String, Object> pool = MiscUtils.castToMap(rawPoolMap, false);
NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1));
NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0));
List<LootCondition> 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<LootEntryContainer<T>> containers = Optional.ofNullable(pool.get("entries"))
.map(it -> (List<LootEntryContainer<T>>) new ArrayList<LootEntryContainer<T>>(LootEntryContainers.fromMapList(castToMapListOrThrow(it,
() -> new LocalizedResourceConfigException("warning.config.loot_table.invalid_entries_type", it.getClass().getSimpleName())))))
.orElse(Lists.newArrayList());
List<LootFunction<T>> functions = Optional.ofNullable(pool.get("functions"))
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(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<T> lootPool = readFlatFormatLootPool(string);
if (lootPool != null)
lootPools.add(lootPool);
}
}
return new LootTable<>(lootPools,
Optional.ofNullable(map.get("functions"))
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName())))))
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(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<T> {
pool.addRandomItems(consumer, context);
}
}
// TODO https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/loot-system/flat-format
public static <T> LootPool<T> readFlatFormatLootPool(String pool) {
return null;
}
@SuppressWarnings("unchecked")
private static List<Map<String, Object>> castToMapListOrThrow(Object obj, Supplier<RuntimeException> exceptionSupplier) {
if (obj instanceof List<?> list) {
return (List<Map<String, Object>>) list;
} else {
throw exceptionSupplier.get();
}
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> arguments) {
float chance = MiscUtils.getAsFloat(arguments.getOrDefault("value", 0.5f));
float chance = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("value", 0.5f), "value");
return new RandomCondition(chance);
}
}

View File

@@ -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<String, Object> arguments) {
Key enchantmentType = Key.of((String) arguments.get("enchantment"));
List<Float> 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<Float> 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");
}
}
}

View File

@@ -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<T> extends AbstractLootEntryContainer<T> {
@SuppressWarnings("unchecked")
@Override
public LootEntryContainer<A> create(Map<String, Object> 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<LootCondition> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
.orElse(Collections.emptyList());

View File

@@ -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 <T> LootEntryContainer<T> fromMap(Map<String, Object> 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<T> factory = (LootEntryContainerFactory<T>) 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);
}

View File

@@ -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<T> 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<T> extends AbstractSingleLootEntryCont
@SuppressWarnings("unchecked")
@Override
public LootEntryContainer<A> create(Map<String, Object> 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<LootCondition> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
.orElse(Collections.emptyList());

View File

@@ -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<T> extends AbstractLootConditionalFunction<
@SuppressWarnings("unchecked")
@Override
public LootFunction<T> create(Map<String, Object> 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<String, Object> 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<LootCondition> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
@@ -166,8 +161,8 @@ public class ApplyBonusCountFunction<T> extends AbstractLootConditionalFunction<
@Override
public Formula create(Map<String, Object> 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);
}
}

View File

@@ -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<T> extends AbstractLootConditionalFunction<T> {
@SuppressWarnings("unchecked")
@Override
public LootFunction<T> create(Map<String, Object> 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<LootCondition> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
.orElse(Collections.emptyList());

View File

@@ -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 <T> LootFunction<T> fromMap(Map<String, Object> 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<T> factory = (LootFunctionFactory<T>) 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);
}

View File

@@ -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<T> extends AbstractLootConditionalFunction<T> {
@SuppressWarnings("unchecked")
@Override
public LootFunction<A> create(Map<String, Object> 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<LootCondition> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<byte[]> 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<String, Object> data = MiscUtils.castToMap(dataRaw, false);
Map<String, Object> data = castToMap(dataRaw, false);
for (Map.Entry<String, Object> 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<String, Object> 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<String, Object> 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<Path> 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<Pair<Path, List<Path>>> 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, List<Path>> 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<Path> conflicts = conflictChecker.computeIfAbsent(relative, k -> new ArrayList<>());

View File

@@ -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<String, Object> config;
public CachedConfig(Map<String, Object> config, Path filePath, Pack pack) {
public CachedConfig(String prefix, Map<String, Object> config, Path filePath, Pack pack) {
this.config = config;
this.filePath = filePath;
this.pack = pack;
this.prefix = prefix;
}
public Map<String, Object> config() {
@@ -25,4 +27,8 @@ public class CachedConfig {
public Pack pack() {
return pack;
}
public String prefix() {
return prefix;
}
}

View File

@@ -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<String, Object> arguments) {
List<Map<String, Object>> terms = (List<Map<String, Object>>) arguments.get("terms");
return new AllOfPathMatcher(PathMatchers.fromMapList(terms));
Object termsObj = arguments.get("terms");
if (termsObj instanceof List<?> list) {
List<Map<String, Object>> terms = (List<Map<String, Object>>) list;
return new AllOfPathMatcher(PathMatchers.fromMapList(terms));
} else if (termsObj instanceof Map<?, ?>) {
Map<String, Object> 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");
}
}
}
}

View File

@@ -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<String, Object> arguments) {
List<Map<String, Object>> terms = (List<Map<String, Object>>) arguments.get("terms");
return new AnyOfPathMatcher(PathMatchers.fromMapList(terms));
Object termsObj = arguments.get("terms");
if (termsObj instanceof List<?> list) {
List<Map<String, Object>> terms = (List<Map<String, Object>>) list;
return new AnyOfPathMatcher(PathMatchers.fromMapList(terms));
} else if (termsObj instanceof Map<?, ?>) {
Map<String, Object> 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");
}
}
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> arguments) {
Map<String, Object> term = (Map<String, Object>) arguments.get("term");
Object inverted = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("term"), () -> new LocalizedException("warning.config.conflict_matcher.inverted.missing_term"));
Map<String, Object> term = MiscUtils.castToMap(inverted, false);
return new InvertedPathMatcher(PathMatchers.fromMap(term));
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -207,7 +207,7 @@ public class MergePackMcMetaResolution implements Resolution {
public static class Factory implements ResolutionFactory {
@Override
public Resolution create(Map<String, Object> arguments) {
String description = (String) arguments.getOrDefault("description", "<gray>CraftEngine ResourcePack");
String description = arguments.getOrDefault("description", "<gray>CraftEngine ResourcePack</gray>").toString();
return new MergePackMcMetaResolution(description);
}
}

View File

@@ -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<String, Object> 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);
}

View File

@@ -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<String, Object> arguments);
default ProxySelector getProxySelector(Map<String, Object> 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;
}
}

View File

@@ -43,7 +43,7 @@ public class ResourcePackHosts {
public static ResourcePackHost fromMap(Map<String, Object> 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);

View File

@@ -292,27 +292,21 @@ public class AlistHost implements ResourcePackHost {
@Override
public ResourcePackHost create(Map<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -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<String, Object> 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));
}
}

View File

@@ -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<String, Object> 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);
}
}

View File

@@ -272,11 +272,11 @@ public class LobFileHost implements ResourcePackHost {
@Override
public ResourcePackHost create(Map<String, Object> 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);
}
}

View File

@@ -232,23 +232,23 @@ public class OneDriveHost implements ResourcePackHost {
@Override
public ResourcePackHost create(Map<String, Object> 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);
}
}

Some files were not shown because too many files have changed in this diff Show More