diff --git a/README.md b/README.md index fefff6da4..941fceae5 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,14 @@

- - Gitbook - - Scc Count Badge + Scc Count Badge + + + Ask DeepWiki + + + Gitbook

diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 607fff3b3..0b9d4ae93 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" id("maven-publish") } @@ -95,6 +95,8 @@ tasks { relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml") relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick") relocate("com.ezylang.evalex", "net.momirealms.craftengine.libraries.evalex") + relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") + relocate("org.apache.commons.imaging", "net.momirealms.craftengine.libraries.imaging") } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 4de975ecd..c4888be8c 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -192,6 +192,11 @@ public class BukkitCompatibilityManager implements CompatibilityManager { return PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player.platformPlayer(), text); } + @Override + public String parse(Player player1, Player player2, String text) { + return PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player1.platformPlayer(), (org.bukkit.entity.Player) player2.platformPlayer(), text); + } + @Override public int getPlayerProtocolVersion(UUID uuid) { return ViaVersionUtils.getPlayerProtocolVersion(uuid); diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bettermodel/BetterModelModel.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bettermodel/BetterModelModel.java index aeec3c863..5dfa9c816 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bettermodel/BetterModelModel.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bettermodel/BetterModelModel.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.compatibility.bettermodel; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel; public class BetterModelModel extends AbstractExternalModel { @@ -15,7 +15,7 @@ public class BetterModelModel extends AbstractExternalModel { } @Override - public void bindModel(Entity entity) { + public void bindModel(AbstractEntity entity) { org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject(); BetterModelUtils.bindModel(bukkitEntity, id()); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/modelengine/ModelEngineModel.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/modelengine/ModelEngineModel.java index cba3b00ce..8ce03f0d6 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/modelengine/ModelEngineModel.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/modelengine/ModelEngineModel.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.compatibility.modelengine; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel; public class ModelEngineModel extends AbstractExternalModel { @@ -15,7 +15,7 @@ public class ModelEngineModel extends AbstractExternalModel { } @Override - public void bindModel(Entity entity) { + public void bindModel(AbstractEntity entity) { org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject(); ModelEngineUtils.bindModel(bukkitEntity, id()); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java index 824236dda..e96cb46dc 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.papi; import me.clip.placeholderapi.PlaceholderAPI; import net.momirealms.craftengine.core.plugin.CraftEngine; import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; public class PlaceholderAPIUtils { @@ -12,6 +13,10 @@ public class PlaceholderAPIUtils { return PlaceholderAPI.setPlaceholders(player, text); } + public static String parse(Player player1, Player player2, String text) { + return PlaceholderAPI.setRelationalPlaceholders(player1, player2, text); + } + public static void registerExpansions(CraftEngine plugin) { new ImageExpansion(plugin).register(); new ShiftExpansion(plugin).register(); diff --git a/bukkit/legacy/build.gradle.kts b/bukkit/legacy/build.gradle.kts index ae80f6f46..da89d9df8 100644 --- a/bukkit/legacy/build.gradle.kts +++ b/bukkit/legacy/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" } repositories { diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index dae694cd2..5085f47dc 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta11" - id("net.minecrell.plugin-yml.bukkit") version "0.6.0" + id("com.gradleup.shadow") version "9.0.0-beta13" + id("de.eldoria.plugin-yml.bukkit") version "0.7.1" } repositories { @@ -60,7 +60,7 @@ artifacts { tasks { shadowJar { - archiveFileName = "${rootProject.name}-plugin-${rootProject.properties["project_version"]}.jar" + archiveFileName = "${rootProject.name}-bukkit-plugin-${rootProject.properties["project_version"]}.jar" destinationDirectory.set(file("$rootDir/target")) relocate("net.kyori", "net.momirealms.craftengine.libraries") relocate("net.momirealms.sparrow.nbt", "net.momirealms.craftengine.libraries.nbt") @@ -78,5 +78,7 @@ tasks { relocate("net.jpountz", "net.momirealms.craftengine.libraries.jpountz") relocate("software.amazon.awssdk", "net.momirealms.craftengine.libraries.awssdk") relocate("software.amazon.eventstream", "net.momirealms.craftengine.libraries.eventstream") + relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") + relocate("org.apache.commons.imaging", "net.momirealms.craftengine.libraries.imaging") } } diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 297be7fe4..e42bbb4be 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -65,6 +65,8 @@ resource-pack: - CustomNameplates/ResourcePack - BetterModel/build - BetterHud/build + merge-external-zip-files: + - CraftEngine/external_packs/example.zip delivery: # Send the resource pack on joining the server send-on-join: true diff --git a/bukkit/loader/src/main/resources/craft-engine.properties b/bukkit/loader/src/main/resources/craft-engine.properties index c94d83fa0..9e875e126 100644 --- a/bukkit/loader/src/main/resources/craft-engine.properties +++ b/bukkit/loader/src/main/resources/craft-engine.properties @@ -20,6 +20,7 @@ caffeine=${caffeine_version} slf4j-api=${slf4j_version} zstd-jni=${zstd_version} commons-io=${commons_io_version} +commons-imaging=${commons_imaging_version} byte-buddy=${byte_buddy_version} snake-yaml=${snake_yaml_version} adventure-text-minimessage=${adventure_bundle_version} @@ -33,4 +34,5 @@ netty-codec-http2=${netty_version} reactive-streams=${reactive_streams_version} amazon-sdk-s3=${amazon_awssdk_version} amazon-sdk-eventstream=${amazon_awssdk_eventstream_version} -evalex=${evalex_version} \ No newline at end of file +evalex=${evalex_version} +jimfs=${jimfs_version} \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml b/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml index 3c8d218d6..fd442e990 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml @@ -38,6 +38,7 @@ categories: - default:topaz_bow - default:topaz_crossbow - default:topaz_rod + - default:topaz_trident - default:topaz_helmet - default:topaz_chestplate - default:topaz_leggings @@ -62,4 +63,6 @@ categories: - default:flame_cane - default:gunpowder_block - default:solid_gunpowder_block - - default:ender_pearl_flower_seeds \ No newline at end of file + - default:ender_pearl_flower_seeds + - default:gui_head_size_1 + - default:gui_head_size_4 \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/fix_client_visual.yml b/bukkit/loader/src/main/resources/resources/default/configuration/fix_client_visual.yml index 795eae21a..9e18aabaf 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/fix_client_visual.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/fix_client_visual.yml @@ -1,5 +1,5 @@ -# client-bound-data requires CraftEngine mod to apply items: + # client-bound-data requires CraftEngine mod to apply minecraft:string: client-bound-data: components: @@ -17,4 +17,11 @@ items: minecraft:block_state: instrument: "harp" powered: "false" - note: "0" \ No newline at end of file + note: "0" + +blocks: + minecraft:note_block: + settings: + client-bound-tags: + - minecraft:beacon_base_blocks + - minecraft:mineable/axe \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/furniture.yml b/bukkit/loader/src/main/resources/resources/default/configuration/furniture.yml index 05578bdbc..2816c5458 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/furniture.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/furniture.yml @@ -17,6 +17,7 @@ items: place: minecraft:block.bamboo_wood.place placement: ground: + loot-spawn-offset: 0.5,0.5,0 rules: # ANY / FOUR / EIGHT / SIXTEEN / NORTH / EAST / WEST / SOUTH rotation: FOUR @@ -40,7 +41,7 @@ items: - 0,0,-0.1 0 - 1,0,-0.1 0 loot: - template: "default:loot_table/basic" + template: "default:loot_table/furniture" arguments: item: default:bench default:table_lamp: @@ -61,6 +62,7 @@ items: place: minecraft:block.lantern.place placement: ground: + loot-spawn-offset: 0,0.2,0 rules: rotation: ANY alignment: QUARTER @@ -90,7 +92,7 @@ items: height: 0.4 interactive: true loot: - template: "default:loot_table/basic" + template: "default:loot_table/furniture" arguments: item: default:table_lamp default:wooden_chair: @@ -111,6 +113,7 @@ items: place: minecraft:block.bamboo_wood.place placement: ground: + loot-spawn-offset: 0,0.4,0 rules: rotation: ANY alignment: ANY @@ -129,6 +132,6 @@ items: seats: - 0,0,-0.1 0 loot: - template: "default:loot_table/basic" + template: "default:loot_table/furniture" arguments: item: default:wooden_chair \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml index fe6db7c36..99e6d9705 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml @@ -20,6 +20,7 @@ i18n: item.topaz_chestplate: "Topaz Chestplate" item.topaz_leggings: "Topaz Leggings" item.topaz_boots: "Topaz Boots" + item.topaz_trident: "Topaz Trident" item.topaz_ore: "Topaz Ore" item.deepslate_topaz_ore: "Deepslate Topaz Ore" item.topaz: "Topaz" @@ -63,6 +64,7 @@ i18n: item.topaz_chestplate: "黄玉胸甲" item.topaz_leggings: "黄玉护腿" item.topaz_boots: "黄玉靴子" + item.topaz_trident: "黄玉三叉戟" item.topaz_ore: "黄玉矿石" item.deepslate_topaz_ore: "深层黄玉矿石" item.topaz: "黄玉" diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/items.yml b/bukkit/loader/src/main/resources/resources/default/configuration/items.yml index 02fa220c0..923daec69 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/items.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/items.yml @@ -1,4 +1,37 @@ -items: +items#gui_head: + default:gui_head_size_1: + material: player_head + custom-model-data: 1000 + model: + type: minecraft:special + path: minecraft:item/custom/gui_head_size_1 + generation: + parent: minecraft:item/template_skull + gui-light: front + display: + gui: + translation: 0,8,0 + scale: 2,2,2 + model: + type: minecraft:head + kind: player + default:gui_head_size_4: + material: player_head + custom-model-data: 1001 + model: + type: minecraft:special + path: minecraft:item/custom/gui_head_size_4 + generation: + parent: minecraft:item/template_skull + gui-light: front + display: + gui: + translation: 9,7,0 + scale: 4,4,4 + model: + type: minecraft:head + kind: player +items#topaz_gears: default:topaz_rod: material: fishing_rod custom-model-data: 1000 @@ -142,7 +175,42 @@ items: arguments: part: boots slot: feet - + default:topaz_trident: + material: trident + custom-model-data: 1000 + settings: + projectile: + item: default:topaz_trident + translation: 0,0,0 + rotation: 1,1,1,1 + display-transform: NONE + scale: 0.5 + tags: + - "default:topaz_tools" + data: + item-name: "<#FF8C00>" + tooltip-style: minecraft:topaz + model: + type: minecraft:select + property: minecraft:display_context + cases: + - when: ["gui", "ground", "fixed"] + model: + type: minecraft:model + path: minecraft:item/custom/topaz_trident + generation: + parent: minecraft:item/generated + textures: + layer0: minecraft:item/custom/topaz_trident + fallback: + type: minecraft:condition + property: minecraft:using_item + on-true: + type: minecraft:model + path: minecraft:item/custom/topaz_trident_throwing + on-false: + type: minecraft:model + path: minecraft:item/custom/topaz_trident_in_hand templates: default:armor/topaz: material: "chainmail_{part}" @@ -395,4 +463,12 @@ recipes#11: template-type: default:topaz result: id: default:topaz_rod + count: 1 + default:topaz_trident: + type: smithing_transform + base: minecraft:trident + addition: default:topaz + template-type: default:topaz + result: + id: default:topaz_trident count: 1 \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml index b2f321624..a281f7e41 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml @@ -968,6 +968,18 @@ templates#loot_tables: - type: item item: "{item}" + # drop the original furniture item or a fallback item + + # template: default:loot_table/furniture + # arguments: + # item: the fallback item + default:loot_table/furniture: + pools: + - rolls: 1 + entries: + - type: furniture_item + item: "{item}" + # drop with silk touch # template: default:loot_table/silk_touch diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/ender_pearl_flower_stage_3.json b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/ender_pearl_flower_stage_3.json deleted file mode 100644 index c65bc426f..000000000 --- a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/ender_pearl_flower_stage_3.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "ambientocclusion": false, - "textures": { - "1": "item/ender_pearl", - "particle": "block/custom/ender_pearl_flower_stage_3", - "cross": "block/custom/ender_pearl_flower_stage_3" - }, - "elements": [ - { - "from": [0.8, 0, 8], - "to": [15.2, 16, 8], - "shade": false, - "rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true}, - "faces": { - "north": {"uv": [0, 0, 16, 16], "texture": "#cross"}, - "south": {"uv": [0, 0, 16, 16], "texture": "#cross"} - } - }, - { - "from": [8, 0, 0.8], - "to": [8, 16, 15.2], - "shade": false, - "rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true}, - "faces": { - "east": {"uv": [0, 0, 16, 16], "texture": "#cross"}, - "west": {"uv": [0, 0, 16, 16], "texture": "#cross"} - } - }, - { - "from": [8, 5, -1], - "to": [8, 21, 15], - "rotation": {"angle": 0, "axis": "y", "origin": [7, 5, 7]}, - "faces": { - "north": {"uv": [0, 0, 0, 16], "texture": "#1"}, - "east": {"uv": [0, 0, 16, 16], "texture": "#1"}, - "south": {"uv": [0, 0, 0, 16], "texture": "#1"}, - "west": {"uv": [0, 0, 16, 16], "texture": "#1"}, - "up": {"uv": [0, 0, 0, 16], "texture": "#1"}, - "down": {"uv": [0, 0, 0, 16], "texture": "#1"} - } - } - ] -} \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json new file mode 100644 index 000000000..a56ed9681 --- /dev/null +++ b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json @@ -0,0 +1,108 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "item/custom/topaz_trident_3d", + "particle": "item/custom/topaz_trident_3d" + }, + "elements": [ + { + "from": [7.75, -5.5, 7.75], + "to": [8.25, 10, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "east": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "south": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "west": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "up": {"uv": [3, 0, 3.5, 0.5], "texture": "#0"}, + "down": {"uv": [0.5, 15.5, 1, 16], "texture": "#0"} + } + }, + { + "from": [7.25, 7, 7.75], + "to": [7.75, 8, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"}, + "down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"} + } + }, + { + "from": [6.75, 7.5, 7.75], + "to": [7.25, 9.5, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "east": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "south": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "west": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"}, + "down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"} + } + }, + { + "from": [8.75, 7.5, 7.75], + "to": [9.25, 9.5, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "east": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "south": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "west": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"}, + "down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"} + } + }, + { + "from": [8.25, 7, 7.75], + "to": [8.75, 8, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"}, + "down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"} + } + } + ], + "gui_light": "front", + "display": { + "thirdperson_righthand": { + "rotation": [0, 60, 0], + "translation": [0.25, 9, 1.5], + "scale": [2, 2, 2] + }, + "thirdperson_lefthand": { + "rotation": [0, 60, 0], + "translation": [0.25, 9, 1.5], + "scale": [2, 2, 2] + }, + "firstperson_righthand": { + "rotation": [0, -90, 25], + "translation": [-2.5, 7.5, 4.75] + }, + "firstperson_lefthand": { + "rotation": [0, 90, -25], + "translation": [-2.5, 7.5, 4.75] + }, + "ground": { + "rotation": [0, 0, 45], + "translation": [-2, 2, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [0, 0, -45], + "translation": [5, 5, 0], + "scale": [1.25, 1.25, 1.25] + }, + "fixed": { + "translation": [0, 6, 0] + } + } +} \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json new file mode 100644 index 000000000..90b814746 --- /dev/null +++ b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json @@ -0,0 +1,110 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "item/custom/topaz_trident_3d", + "particle": "item/custom/topaz_trident_3d" + }, + "elements": [ + { + "from": [7.75, -5.5, 7.75], + "to": [8.25, 10, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "east": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "south": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "west": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"}, + "up": {"uv": [3, 0, 3.5, 0.5], "texture": "#0"}, + "down": {"uv": [0.5, 15.5, 1, 16], "texture": "#0"} + } + }, + { + "from": [7.25, 7, 7.75], + "to": [7.75, 8, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"}, + "down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"} + } + }, + { + "from": [6.75, 7.5, 7.75], + "to": [7.25, 9.5, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "east": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "south": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "west": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"}, + "down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"} + } + }, + { + "from": [8.75, 7.5, 7.75], + "to": [9.25, 9.5, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "east": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "south": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "west": {"uv": [11.5, 1, 12, 3], "texture": "#0"}, + "up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"}, + "down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"} + } + }, + { + "from": [8.25, 7, 7.75], + "to": [8.75, 8, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]}, + "faces": { + "north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"}, + "up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"}, + "down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"} + } + } + ], + "gui_light": "front", + "display": { + "thirdperson_righthand": { + "rotation": [0, 90, 180], + "translation": [0.25, -9, 1.5], + "scale": [2, 2, 2] + }, + "thirdperson_lefthand": { + "rotation": [0, 90, 180], + "translation": [0.25, -9, 1.5], + "scale": [2, 2, 2] + }, + "firstperson_righthand": { + "rotation": [0, -90, 25], + "translation": [0.75, -5, -0.75], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [0, 90, -25], + "translation": [0.75, -5, -0.75], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "rotation": [0, 0, 45], + "translation": [-2, 2, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [0, 0, -45], + "translation": [5, 5, 0], + "scale": [1.25, 1.25, 1.25] + }, + "fixed": { + "translation": [0, 6, 0] + } + } +} \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_3.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_3.png deleted file mode 100644 index 358b7874c..000000000 Binary files a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_3.png and /dev/null differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident.png new file mode 100644 index 000000000..a4b1ec38c Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident_3d.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident_3d.png new file mode 100644 index 000000000..692a34f0f Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident_3d.png differ diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index ff18b0826..2a6d99bac 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -90,6 +90,10 @@ warning.config.condition.match_block_property.missing_properties: "Issue warning.config.condition.match_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_item' condition." warning.config.condition.table_bonus.missing_enchantment: "Issue found in file - The config '' is missing the required 'enchantment' argument for 'table_bonus' condition." warning.config.condition.table_bonus.missing_chances: "Issue found in file - The config '' is missing the required 'chances' argument for 'table_bonus' condition." +warning.config.condition.permission.missing_permission: "Issue found in file - The config '' is missing the required 'permission' argument for 'permission' condition." +warning.config.condition.equals.missing_value1: "Issue found in file - The config '' is missing the required 'value1' argument for 'equals' condition." +warning.config.condition.equals.missing_value2: "Issue found in file - The config '' is missing the required 'value2' argument for 'equals' condition." +warning.config.condition.expression.missing_expression: "Issue found in file - The config '' is missing the required 'expression' argument for 'expression' condition." warning.config.structure.not_section: "Issue found in file - The config '' is expected to be a config section while it's actually a(n) ''." warning.config.image.duplicate: "Issue found in file - Duplicated image ''. Please check if there is the same configuration in other files." warning.config.image.missing_height: "Issue found in file - The image '' is missing the required 'height' argument." @@ -179,6 +183,8 @@ warning.config.item.model.select.case.missing_model: "Issue found in fil warning.config.item.model.select.block_state.missing_property: "Issue found in file - The item '' is missing the required 'block-state-property' argument for property 'minecraft:block_state'." warning.config.item.model.select.local_time.missing_pattern: "Issue found in file - The item '' is missing the required 'pattern' argument for property 'minecraft:local_time'." warning.config.item.model.special.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for model 'minecraft:special'." +warning.config.item.model.special.missing_path: "Issue found in file - The item '' is missing the required 'path' argument for model 'minecraft:special'." +warning.config.item.model.special.invalid_path: "Issue found in file - The item '' has an invalid 'path' argument '' for model 'minecraft:special' which contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.item.model.special.invalid_type: "Issue found in file - The item '' is using an invalid type '' for model 'minecraft:special'." warning.config.item.model.special.banner.missing_color: "Issue found in file - The item '' is missing the required 'color' argument for special model 'minecraft:banner'." warning.config.item.model.special.bed.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:bed'." @@ -189,7 +195,6 @@ warning.config.item.model.special.chest.invalid_openness: "Issue found i warning.config.item.model.special.shulker_box.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:shulker_box'." warning.config.item.model.special.shulker_box.invalid_openness: "Issue found in file - The item '' is using an invalid 'openness' value '' for special model 'minecraft:shulker_box'. Valid range '0~1.'" warning.config.item.model.special.head.missing_kind: "Issue found in file - The item '' is missing the required 'kind' argument for special model 'minecraft:head'." -warning.config.item.model.special.head.missing_texture: "Issue found in file - The item '' is missing the required 'texture' argument for special model 'minecraft:head'." warning.config.block.duplicate: "Issue found in file - Duplicated block ''. Please check if there is the same configuration in other files." warning.config.block.missing_state: "Issue found in file - The block '' is missing the required 'state' argument." warning.config.block.state.property.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for property ''." @@ -223,9 +228,9 @@ warning.config.block.behavior.leaves.missing_distance: "Issue found in f warning.config.block.behavior.sapling.missing_stage: "Issue found in file - The block '' is missing the required 'stage' property for 'sapling_block' behavior." warning.config.block.behavior.sapling.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'sapling_block' behavior." warning.config.block.behavior.strippable.missing_stripped: "Issue found in file - The block '' is missing the required 'stripped' argument for 'strippable_block' behavior." -warning.config.block.event.condition.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for event condition." -warning.config.block.event.condition.invalid_type: "Issue found in file - The block '' is using an invalid 'type' argument '' for event condition." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." +warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" +warning.config.model.generation.invalid_gui_light: "Issue found in file - The config '' is using an invalid gui-light option '' in 'generation' section. Allowed gui light options: []" warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.texture.invalid: "Issue found in file - The config '' has a texture '' with path '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.model.generation.parent.invalid: "Issue found in file - The config '' has a parent argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." @@ -294,4 +299,18 @@ warning.config.conflict_matcher.all_of.missing_terms: "Issue found in co warning.config.conflict_matcher.any_of.missing_terms: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'any_of' matcher." warning.config.conflict_resolution.missing_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the resolutions." warning.config.conflict_resolution.invalid_type: "Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the resolutions is using the invalid type ''." -warning.config.function.command.missing_command: "Issue found in file - The config '' is missing the required 'command' argument for 'command' function." \ No newline at end of file +warning.config.event.missing_trigger: "Issue found in file - The config '' is missing the required 'on' argument for event triggers." +warning.config.event.invalid_trigger: "Issue found in file - The config '' is using an invalid event trigger ''." +warning.config.event.condition.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for event condition." +warning.config.event.condition.invalid_type: "Issue found in file - The config '' is using an invalid 'type' argument '' for event condition." +warning.config.function.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for function." +warning.config.function.invalid_type: "Issue found in file - The config '' is using an invalid function type ''." +warning.config.function.command.missing_command: "Issue found in file - The config '' is missing the required 'command' argument for 'command' function." +warning.config.function.actionbar.missing_actionbar: "Issue found in file - The config '' is missing the required 'actionbar' argument for 'actionbar' function." +warning.config.function.message.missing_message: "Issue found in file - The config '' is missing the required 'message' argument for 'message' function." +warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector." +warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''." +warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''." +warning.config.resource_pack.item_model.conflict.vanilla: "Failed to generate item model for '' because this item model has been occupied by a vanilla item." +warning.config.resource_pack.item_model.already_exist: "Failed to generate item model for '' because the file '' already exists." +warning.config.resource_pack.model.generation.already_exist: "Failed to generate model because the model file '' already exists." \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/tr.yml b/bukkit/loader/src/main/resources/translations/tr.yml index def783519..7c1c475b5 100644 --- a/bukkit/loader/src/main/resources/translations/tr.yml +++ b/bukkit/loader/src/main/resources/translations/tr.yml @@ -167,7 +167,6 @@ warning.config.item.model.special.chest.invalid_openness: " dosya warning.config.item.model.special.shulker_box.missing_texture: " dosyasında sorun bulundu - '' eşyası, 'minecraft:shulker_box' özel modeli için gerekli 'texture' argümanı eksik." warning.config.item.model.special.shulker_box.invalid_openness: " dosyasında sorun bulundu - '' eşyası, 'minecraft:shulker_box' özel modeli için geçersiz bir 'openness' değeri '' kullanıyor. Geçerli aralık '0~1.'" warning.config.item.model.special.head.missing_kind: " dosyasında sorun bulundu - '' eşyası, 'minecraft:head' özel modeli için gerekli 'kind' argümanı eksik." -warning.config.item.model.special.head.missing_texture: " dosyasında sorun bulundu - '' eşyası, 'minecraft:head' özel modeli için gerekli 'texture' argümanı eksik." warning.config.block.duplicate: " dosyasında sorun bulundu - Yinelenen blok ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." warning.config.block.missing_state: " dosyasında sorun bulundu - '' bloğu gerekli 'state' argümanı eksik." warning.config.block.state.property.missing_type: " dosyasında sorun bulundu - '' bloğu, '' özelliği için gerekli 'type' argümanı eksik." diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index d2e6ed5f9..f0f71519f 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -90,6 +90,8 @@ warning.config.condition.match_block_property.missing_properties: "在 warning.config.condition.match_item.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_item' 条件所需的 'id' 参数" warning.config.condition.table_bonus.missing_enchantment: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'enchantment' 参数" warning.config.condition.table_bonus.missing_chances: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'chances' 参数" + + warning.config.structure.not_section: "在文件 发现问题 - 配置项 '' 应为配置段落 但实际类型为 ''" warning.config.image.duplicate: "在文件 发现问题 - 重复的图片配置 '' 请检查其他文件中是否存在相同配置" warning.config.image.missing_height: "在文件 发现问题 - 图片 '' 缺少必需的 'height' 参数" @@ -179,6 +181,8 @@ warning.config.item.model.select.case.missing_model: "在文件 warning.config.item.model.select.block_state.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:block_state' 属性缺少必需的 'block-state-property' 参数" warning.config.item.model.select.local_time.missing_pattern: "在文件 发现问题 - 物品 '' 的 'minecraft:local_time' 属性缺少必需的 'pattern' 参数" warning.config.item.model.special.missing_type: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型缺少必需的 'type' 参数" +warning.config.item.model.special.missing_path: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型缺少必需的模型 'path' 参数" +warning.config.item.model.special.invalid_path: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型路径 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.item.model.special.invalid_type: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型使用了无效类型 ''" warning.config.item.model.special.banner.missing_color: "在文件 发现问题 - 物品 '' 的 'minecraft:banner' 特殊模型缺少必需的 'color' 参数" warning.config.item.model.special.bed.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:bed' 特殊模型缺少必需的 'texture' 参数" @@ -189,7 +193,6 @@ warning.config.item.model.special.chest.invalid_openness: "在文件 在文件 发现问题 - 物品 '' 的 'minecraft:shulker_box' 特殊模型缺少必需的 'texture' 参数" warning.config.item.model.special.shulker_box.invalid_openness: "在文件 发现问题 - 物品 '' 的 'minecraft:shulker_box' 特殊模型使用了无效的 'openness' 值 '' 有效范围应为 0~1" warning.config.item.model.special.head.missing_kind: "在文件 发现问题 - 物品 '' 的 'minecraft:head' 特殊模型缺少必需的 'kind' 参数" -warning.config.item.model.special.head.missing_texture: "在文件 发现问题 - 物品 '' 的 'minecraft:head' 特殊模型缺少必需的 'texture' 参数" warning.config.block.duplicate: "在文件 发现问题 - 重复的方块 '' 请检查其他文件中是否存在相同配置" warning.config.block.missing_state: "在文件 发现问题 - 方块 '' 缺少必需的 'state' 参数" warning.config.block.state.property.missing_type: "在文件 发现问题 - 方块 '' 的属性 '' 缺少必需的 'type' 参数" @@ -223,10 +226,10 @@ warning.config.block.behavior.leaves.missing_distance: "在文件 在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'stage' 属性" warning.config.block.behavior.sapling.missing_feature: "在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'feature' 参数" warning.config.block.behavior.strippable.missing_stripped: "在文件 发现问题 - 方块 '' 的 'strippable_block' 行为缺少必需的 'stripped' 参数" -warning.config.block.event.condition.missing_type: "在文件 - 方块 '' 的事件条件缺少 'type' 参数" -warning.config.block.event.condition.invalid_type: "在文件 - 方块 '' 使用了无效的事件条件类型 ''" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" +warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" +warning.config.model.generation.invalid_gui_light: "在文件 发现问题 - 配置项 '' 在 'generation' 区域使用了无效的GUI光照 ''. 可用GUI光照: []" warning.config.model.generation.texture.invalid: "在文件 发现问题 - 配置项 '' 的纹理 '' 路径 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.model.generation.parent.invalid: "在文件 发现问题 - 配置项 '' 的父级参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.emoji.missing_keywords: "在文件 发现问题 - 表情 '' 缺少必需的 'keywords' 参数" @@ -293,4 +296,11 @@ warning.config.conflict_matcher.inverted.missing_term: "在 config.yml warning.config.conflict_matcher.all_of.missing_terms: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 全匹配器缺少必需的 'terms' 参数" warning.config.conflict_matcher.any_of.missing_terms: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 任一匹配器缺少必需的 'terms' 参数" warning.config.conflict_resolution.missing_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案缺少必需的 'type' 参数" -warning.config.conflict_resolution.invalid_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 ''" \ No newline at end of file +warning.config.conflict_resolution.invalid_type: "在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 ''" +warning.config.event.missing_trigger: "Issue found in file - The config '' is missing the required 'on' argument for event triggers." +warning.config.event.invalid_trigger: "Issue found in file - The config '' is using an invalid event trigger ''." +warning.config.event.condition.missing_type: "在文件 - 配置项 '' 的事件条件缺少 'type' 参数" +warning.config.event.condition.invalid_type: "在文件 - 配置项 '' 使用了无效的事件条件类型 ''" +warning.config.function.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for function." +warning.config.function.invalid_type: "Issue found in file - The config '' is using an invalid function type ''." +warning.config.function.command.missing_command: "Issue found in file - The config '' is missing the required 'command' argument for 'command' function." \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java index 74e2a5916..8df8c971d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; 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.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; @@ -31,11 +31,11 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.advancementParser; } - public class AdvancementParser implements ConfigSectionParser { + public class AdvancementParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"}; @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index 17a8d21b4..aac68bb9e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -12,11 +12,11 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; import org.bukkit.Material; @@ -169,20 +169,20 @@ public final class CraftEngineBlocks { if (state == null || state.isEmpty()) return false; World world = new BukkitWorld(block.getWorld()); Location location = block.getLocation(); - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); + WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); if (dropLoot) { - ContextHolder.Builder builder = new ContextHolder.Builder().withParameter(CommonParameters.WORLD, world).withParameter(CommonParameters.LOCATION, vec3d); + ContextHolder.Builder builder = new ContextHolder.Builder() + .withParameter(DirectContextParameters.POSITION, position); BukkitServerPlayer serverPlayer = BukkitCraftEngine.instance().adapt(player); if (player != null) { - builder.withParameter(CommonParameters.PLAYER, serverPlayer); - //mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND)); + builder.withParameter(DirectContextParameters.PLAYER, serverPlayer); } for (Item item : state.getDrops(builder, world, serverPlayer)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } } if (playSound) { - world.playBlockSound(vec3d, state.sounds().breakSound()); + world.playBlockSound(position, state.sounds().breakSound()); } if (sendParticles) { FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 134fda9a6..aa9616867 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -5,17 +5,17 @@ import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -41,7 +41,7 @@ public final class CraftEngineFurniture { } /** - * Places furniture at the certain location + * Places furniture at certain location * * @param location location * @param furnitureId furniture to place @@ -55,7 +55,7 @@ public final class CraftEngineFurniture { } /** - * Places furniture at the certain location + * Places furniture at certain location * * @param location location * @param furnitureId furniture to place @@ -66,11 +66,11 @@ public final class CraftEngineFurniture { public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType) { CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(furniture, location, anchorType, true); + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); } /** - * Places furniture at the certain location + * Places furniture at certain location * * @param location location * @param furniture furniture to place @@ -79,11 +79,11 @@ public final class CraftEngineFurniture { */ @NotNull public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) { - return BukkitFurnitureManager.instance().place(furniture, location, anchorType, true); + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); } /** - * Places furniture at the certain location + * Places furniture at certain location * * @param location location * @param furnitureId furniture to place @@ -95,11 +95,11 @@ public final class CraftEngineFurniture { public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(furniture, location, anchorType, playSound); + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); } /** - * Places furniture at the certain location + * Places furniture at certain location * * @param location location * @param furniture furniture to place @@ -109,7 +109,7 @@ public final class CraftEngineFurniture { */ @NotNull public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) { - return BukkitFurnitureManager.instance().place(furniture, location, anchorType, playSound); + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); } /** @@ -222,7 +222,7 @@ public final class CraftEngineFurniture { } /** - * Removes furniture by providing a plugin furniture instance + * Removes furniture by providing furniture instance * * @param loadedFurniture loaded furniture * @param dropLoot whether to drop loots @@ -235,7 +235,7 @@ public final class CraftEngineFurniture { } /** - * Removes furniture by providing a plugin furniture instance + * Removes furniture by providing furniture instance * * @param loadedFurniture loaded furniture * @param player the player who removes the furniture @@ -251,7 +251,7 @@ public final class CraftEngineFurniture { } /** - * Removes furniture by providing a plugin furniture instance + * Removes furniture by providing furniture instance * * @param loadedFurniture loaded furniture * @param player the player who removes the furniture @@ -263,26 +263,26 @@ public final class CraftEngineFurniture { @Nullable net.momirealms.craftengine.core.entity.player.Player player, boolean dropLoot, boolean playSound) { - Location location = loadedFurniture.location(); + Location location = loadedFurniture.dropLocation(); loadedFurniture.destroy(); LootTable lootTable = (LootTable) loadedFurniture.config().lootTable(); - Vec3d vec3d = LocationUtils.toVec3d(location); World world = new BukkitWorld(location.getWorld()); + WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ()); if (dropLoot && lootTable != null) { - ContextHolder.Builder builder = ContextHolder.builder(); - builder.withParameter(CommonParameters.LOCATION, vec3d); - builder.withParameter(CommonParameters.WORLD, world); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.FURNITURE, loadedFurniture) + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, loadedFurniture.extraData().item().orElse(null)); if (player != null) { - builder.withParameter(CommonParameters.PLAYER, player); - //mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, player.getItemInHand(InteractionHand.MAIN_HAND)); + builder.withParameter(DirectContextParameters.PLAYER, player); } List> items = lootTable.getRandomItems(builder.build(), world, player); for (Item item : items) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } } if (playSound) { - world.playBlockSound(vec3d, loadedFurniture.config().settings().sounds().breakSound()); + world.playBlockSound(position, loadedFurniture.config().settings().sounds().breakSound()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index a31e0023a..31b16f093 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -6,22 +6,27 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; +import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -33,10 +38,12 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.world.GenericGameEvent; import org.bukkit.inventory.ItemStack; import java.util.List; +import java.util.Optional; public class BlockEventListener implements Listener { private final BukkitCraftEngine plugin; @@ -49,6 +56,14 @@ public class BlockEventListener implements Listener { this.enableNoteBlockCheck = enableNoteBlockCheck; } + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Object packet = this.manager.cachedUpdateTagsPacket; + if (packet != null) { + this.plugin.networkManager().sendPacket(event.getPlayer(), packet); + } + } + @EventHandler(ignoreCancelled = true) public void onPlayerAttack(EntityDamageByEntityEvent event) { if (!VersionHelper.isOrAbove1_20_5()) { @@ -108,17 +123,40 @@ public class BlockEventListener implements Listener { Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); int stateId = BlockStateUtils.blockStateToId(blockState); Player player = event.getPlayer(); + Location location = block.getLocation(); + BukkitServerPlayer serverPlayer = this.plugin.adapt(player); + net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld()); + WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); + Item itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); + + if (itemInHand != null) { + Optional> optionalCustomItem = itemInHand.getCustomItem(); + if (optionalCustomItem.isPresent()) { + Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); + optionalCustomItem.get().execute( + PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.PLAYER, serverPlayer) + .withParameter(DirectContextParameters.EVENT, cancellable) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + ), EventTrigger.BREAK + ); + if (cancellable.isCancelled()) { + return; + } + } + } + if (!BlockStateUtils.isVanillaBlock(stateId)) { ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId); if (!state.isEmpty()) { - Location location = block.getLocation(); - BukkitServerPlayer serverPlayer = this.plugin.adapt(player); // double check adventure mode to prevent dupe if (!FastNMS.INSTANCE.mayBuild(serverPlayer.serverPlayer()) && !serverPlayer.canBreak(LocationUtils.toBlockPos(location), null)) { return; } - // trigger event + // trigger api event CustomBlockBreakEvent customBreakEvent = new CustomBlockBreakEvent(serverPlayer, location, block, state); boolean isCancelled = EventUtils.fireAndCheckCancel(customBreakEvent); if (isCancelled) { @@ -126,7 +164,20 @@ public class BlockEventListener implements Listener { return; } - net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld()); + // execute functions + Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK_STATE, state) + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.POSITION, position) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + ); + state.owner().value().execute(context, EventTrigger.BREAK); + if (cancellable.isCancelled()) { + return; + } + // handle waterlogged blocks @SuppressWarnings("unchecked") Property waterloggedProperty = (Property) state.owner().value().getProperty("waterlogged"); @@ -136,27 +187,26 @@ public class BlockEventListener implements Listener { location.getWorld().setBlockData(location, Material.WATER.createBlockData()); } } + // play sound - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); - world.playBlockSound(vec3d, state.sounds().breakSound()); + world.playBlockSound(position, state.sounds().breakSound()); if (player.getGameMode() == GameMode.CREATIVE || !customBreakEvent.dropItems()) { return; } - Item itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); // do not drop if it's not the correct tool if (!BlockStateUtils.isCorrectTool(state, itemInHand)) { return; } // drop items - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(CommonParameters.WORLD, world) - .withParameter(CommonParameters.LOCATION, vec3d) - .withParameter(CommonParameters.PLAYER, serverPlayer); - //mark item .withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, itemInHand); - for (Item item : state.getDrops(builder, world, serverPlayer)) { - world.dropItemNaturally(vec3d, item); + ContextHolder.Builder lootContext = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.PLAYER, serverPlayer) + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand); + for (Item item : state.getDrops(lootContext, world, serverPlayer)) { + world.dropItemNaturally(position, item); } } } else { @@ -167,23 +217,19 @@ public class BlockEventListener implements Listener { event.setDropItems(false); event.setExpToDrop(0); } - Location location = block.getLocation(); - BukkitServerPlayer serverPlayer = this.plugin.adapt(player); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld()); - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(CommonParameters.WORLD, world) - .withParameter(CommonParameters.LOCATION, vec3d) - .withParameter(CommonParameters.PLAYER, serverPlayer); - //mark item .withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND)); - ContextHolder contextHolder = builder.build(); + ContextHolder lootContext = ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.PLAYER, serverPlayer) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand).build(); for (LootTable lootTable : it.lootTables()) { - for (Item item : lootTable.getRandomItems(contextHolder, world, serverPlayer)) { - world.dropItemNaturally(vec3d, item); + for (Item item : lootTable.getRandomItems(lootContext, world, serverPlayer)) { + world.dropItemNaturally(position, item); } } }); } + // sound system if (Config.enableSoundSystem()) { Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); @@ -212,12 +258,11 @@ public class BlockEventListener implements Listener { if (!immutableBlockState.isEmpty()) { Location location = block.getLocation(); net.momirealms.craftengine.core.world.World world = new BukkitWorld(block.getWorld()); - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(CommonParameters.WORLD, world) - .withParameter(CommonParameters.LOCATION, vec3d); - for (Item item : immutableBlockState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); + for (Item item : immutableBlockState.getDrops(ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)), world, null)) { + world.dropItemNaturally(position, item); } } } else { @@ -227,43 +272,52 @@ public class BlockEventListener implements Listener { event.getDrops().clear(); event.setExpToDrop(0); } - Location location = block.getLocation(); - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld()); - ContextHolder.Builder builder = ContextHolder.builder(); - builder.withParameter(CommonParameters.WORLD, world); - builder.withParameter(CommonParameters.LOCATION, vec3d); - ContextHolder contextHolder = builder.build(); + WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)); for (LootTable lootTable : it.lootTables()) { - for (Item item : lootTable.getRandomItems(contextHolder, world, null)) { - world.dropItemNaturally(vec3d, item); + for (Item item : lootTable.getRandomItems(builder.build(), world, null)) { + world.dropItemNaturally(position, item); } } }); } } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void onStep(GenericGameEvent event) { if (event.getEvent() != GameEvent.STEP) return; Entity entity = event.getEntity(); if (!(entity instanceof Player player)) return; BlockPos pos = EntityUtils.getOnPos(player); - Location playerLocation = player.getLocation(); - BlockData blockData = player.getWorld().getBlockData(pos.x(), pos.y(), pos.z()); - Object blockState = BlockStateUtils.blockDataToBlockState(blockData); + Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z()); + Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); int stateId = BlockStateUtils.blockStateToId(blockState); if (!BlockStateUtils.isVanillaBlock(stateId)) { + Location location = player.getLocation(); ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId); - player.playSound(playerLocation, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch()); + Cancellable cancellable = Cancellable.dummy(); + state.owner().value().execute(PlayerOptionalContext.of(this.plugin.adapt(player), ContextHolder.builder() + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location))) + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK_STATE, state) + ), EventTrigger.STEP); + if (cancellable.isCancelled()) { + event.setCancelled(true); + return; + } + player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch()); } else if (Config.enableSoundSystem()) { Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); if (manager.isBlockSoundRemoved(ownerBlock)) { try { Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock); Object stepSound = Reflections.field$SoundType$stepSound.get(soundType); - player.playSound(playerLocation, FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f); + player.playSound(player.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to get sound type", e); } @@ -271,39 +325,6 @@ public class BlockEventListener implements Listener { } } -// Use BlockBreakBlock event -// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) -// public void onPistonRetract(BlockPistonRetractEvent event) { -// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock()); -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) -// public void onPistonExtend(BlockPistonExtendEvent event) { -// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock()); -// } -// -// private void handlePistonEvent(BlockFace face, List blocksList, Block piston) { -// int blocks = blocksList.size(); -// net.momirealms.craftengine.core.world.World world = new BukkitWorld(piston.getWorld()); -// for (int i = blocks - 1; i >= 0; --i) { -// Location oldLocation = blocksList.get(i).getLocation(); -// BlockPos oldPos = new BlockPos(oldLocation.getBlockX(), oldLocation.getBlockY(), oldLocation.getBlockZ()); -// Block block = blocksList.get(i); -// ImmutableBlockState blockState = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); -// if (blockState != null && blockState.pushReaction() == PushReaction.DESTROY) { -// // break actions -// ContextHolder.Builder builder = ContextHolder.builder(); -// Vec3d vec3d = Vec3d.atCenterOf(oldPos); -// builder.withParameter(LootParameters.LOCATION, vec3d); -// builder.withParameter(LootParameters.WORLD, world); -// for (Item item : blockState.getDrops(builder, world)) { -// world.dropItemNaturally(vec3d, item); -// } -// world.playBlockSound(vec3d, blockState.sounds().breakSound()); -// } -// } -// } - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onEntityExplode(EntityExplodeEvent event) { if (VersionHelper.isOrAbove1_21()) { @@ -327,17 +348,17 @@ public class BlockEventListener implements Listener { BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); ImmutableBlockState state = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); if (state != null && !state.isEmpty()) { - ContextHolder.Builder builder = ContextHolder.builder(); - Vec3d vec3d = Vec3d.atCenterOf(blockPos); - builder.withParameter(CommonParameters.LOCATION, vec3d); - builder.withParameter(CommonParameters.WORLD, world); + WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(blockPos)); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)); if (yield < 1f) { - builder.withParameter(CommonParameters.EXPLOSION_RADIUS, 1.0f / yield); + builder.withParameter(DirectContextParameters.EXPLOSION_RADIUS, 1.0f / yield); } for (Item item : state.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } - world.playBlockSound(vec3d, state.sounds().breakSound()); + world.playBlockSound(position, state.sounds().breakSound()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 6142cbed6..7d77e45fe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -11,10 +11,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.bukkit.util.RegistryUtils; +import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.properties.Properties; import net.momirealms.craftengine.core.block.properties.Property; @@ -25,7 +22,11 @@ import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.registry.BuiltInRegistries; @@ -35,7 +36,9 @@ import net.momirealms.craftengine.core.util.*; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -88,6 +91,10 @@ public class BukkitBlockManager extends AbstractBlockManager { private final BlockEventListener blockEventListener; private final FallingBlockRemoveListener fallingBlockRemoveListener; + private Map> clientBoundTags = Map.of(); + private Map> previousTags = Map.of(); + protected Object cachedUpdateTagsPacket; + public BukkitBlockManager(BukkitCraftEngine plugin) { super(plugin); this.plugin = plugin; @@ -142,6 +149,8 @@ public class BukkitBlockManager extends AbstractBlockManager { this.modBlockStates.clear(); if (EmptyBlock.STATE != null) Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE); + this.previousTags = this.clientBoundTags; + this.clientBoundTags = new HashMap<>(); } @Override @@ -161,6 +170,26 @@ public class BukkitBlockManager extends AbstractBlockManager { initSuggestions(); resetPacketConsumers(); clearCache(); + resendTags(); + } + + private void resendTags() { + // if there's no change + if (this.clientBoundTags.equals(this.previousTags)) return; + List list = new ArrayList<>(); + for (Map.Entry> entry : this.clientBoundTags.entrySet()) { + list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue())); + } + Object packet = TagUtils.createUpdateTagsPacket(Map.of(Reflections.instance$Registries$BLOCK, list)); + for (Player player : Bukkit.getOnlinePlayers()) { + this.plugin.networkManager().sendPacket(player, packet); + } + // 如果空,那么新来的玩家就没必要收到更新包了 + if (list.isEmpty()) { + this.cachedUpdateTagsPacket = null; + } else { + this.cachedUpdateTagsPacket = packet; + } } private void clearCache() { @@ -193,7 +222,7 @@ public class BukkitBlockManager extends AbstractBlockManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.blockParser; } @@ -221,7 +250,7 @@ public class BukkitBlockManager extends AbstractBlockManager { for (int i = 0; i < size; i++) { states[i] = new PackedBlockState(BlockStateUtils.idToBlockState(i), i); } - BlockRegistryMirror.init(states); + BlockRegistryMirror.init(states, new PackedBlockState(Reflections.instance$Blocks$STONE$defaultState, BlockStateUtils.blockStateToId(Reflections.instance$Blocks$STONE$defaultState))); } private void registerEmptyBlock() { @@ -314,7 +343,7 @@ public class BukkitBlockManager extends AbstractBlockManager { } } - public class BlockParser implements ConfigSectionParser { + public class BlockParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"blocks", "block"}; @Override @@ -329,10 +358,18 @@ public class BukkitBlockManager extends AbstractBlockManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { - // check duplicated config - if (byId.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.block.duplicate"); + if (id.namespace().equals("minecraft") && Registry.MATERIAL.get(KeyUtils.toNamespacedKey(id)) != null) { + parseVanillaBlock(pack, path, id, section); + } else { + // check duplicated config + if (byId.containsKey(id)) { + throw new LocalizedResourceConfigException("warning.config.block.duplicate"); + } + parseCustomBlock(pack, path, id, section); } + } + + private void parseCustomBlock(Pack pack, Path path, Key id, Map section) { // read block settings BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false)); @@ -421,15 +458,19 @@ public class BukkitBlockManager extends AbstractBlockManager { } } + Object eventsObj = ResourceConfigUtils.get(section, "events", "event"); + EnumMap>> events = EventFunctions.parseEvents(eventsObj); + Map behaviors = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false); CustomBlock block = BukkitCustomBlock.builder(id) - .appearances(appearances) - .variantMapper(variants) - .lootTable(lootTable) - .properties(properties) - .settings(settings) - .behavior(behaviors) - .build(); + .appearances(appearances) + .variantMapper(variants) + .lootTable(lootTable) + .properties(properties) + .settings(settings) + .behavior(behaviors) + .events(events) + .build(); // bind appearance and real state for (ImmutableBlockState state : block.variantProvider().states()) { @@ -443,7 +484,7 @@ public class BukkitBlockManager extends AbstractBlockManager { appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId()); } - byId.put(id, block); + BukkitBlockManager.this.byId.put(id, block); // generate mod assets if (Config.generateModAssets()) { @@ -453,6 +494,23 @@ public class BukkitBlockManager extends AbstractBlockManager { } } } + + private void parseVanillaBlock(Pack pack, Path path, Key id, Map section) { + Map settings = MiscUtils.castToMap(section.get("settings"), true); + if (settings != null) { + Object clientBoundTags = settings.get("client-bound-tags"); + if (clientBoundTags instanceof List list) { + List clientSideTags = MiscUtils.getAsStringList(list).stream().filter(ResourceLocation::isValid).toList(); + try { + Object nmsBlock = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, KeyUtils.toResourceLocation(id)); + FastNMS.INSTANCE.method$IdMap$getId(Reflections.instance$BuiltInRegistries$BLOCK, nmsBlock).ifPresent(i -> + BukkitBlockManager.this.clientBoundTags.put(i, clientSideTags)); + } catch (ReflectiveOperationException e) { + BukkitBlockManager.this.plugin.logger().warn("Unable to get block " + id, e); + } + } + } + } } private Map> parseProperties(Map propertiesSection) { @@ -537,7 +595,7 @@ public class BukkitBlockManager extends AbstractBlockManager { if (singleModelMap.containsKey("weight")) json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight")); Map generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true); if (generationMap != null) { - prepareModelGeneration(new ModelGeneration(Key.of(modelPath), generationMap)); + prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap)); } variants.add(json); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 209e34662..17891cded 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -9,6 +9,9 @@ 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.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; @@ -19,12 +22,11 @@ import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.shared.ObjectHolder; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class BukkitCustomBlock extends CustomBlock { @@ -35,10 +37,11 @@ public class BukkitCustomBlock extends CustomBlock { Map appearances, Map variantMapper, BlockSettings settings, - Map behavior, + @NotNull EnumMap>> events, + @Nullable Map behavior, @Nullable LootTable lootTable ) { - super(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable); + super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable); } @SuppressWarnings("unchecked") @@ -158,7 +161,7 @@ public class BukkitCustomBlock extends CustomBlock { // create or get block holder Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() -> ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id))); - return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable); + return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java index 20e17db9d..546ef9ad4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java @@ -7,19 +7,14 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; -import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.entity.FallingBlock; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; public class FallingBlockRemoveListener implements Listener { - /* - * This is not an event that would be removed - * Paper mistakenly marked it as deprecated in 1.20.4 - */ - @SuppressWarnings("removal") @EventHandler public void onFallingBlockBreak(org.bukkit.event.entity.EntityRemoveEvent event) { if (event.getCause() == org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP && event.getEntity() instanceof FallingBlock fallingBlock) { @@ -31,22 +26,18 @@ public class FallingBlockRemoveListener implements Listener { int stateId = BlockStateUtils.blockStateToId(blockState); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null || immutableBlockState.isEmpty()) return; - ContextHolder.Builder builder = ContextHolder.builder(); - builder.withParameter(CommonParameters.FALLING_BLOCK, true); - double x = Reflections.field$Entity$xo.getDouble(fallingBlockEntity); - double y = Reflections.field$Entity$yo.getDouble(fallingBlockEntity); - double z = Reflections.field$Entity$zo.getDouble(fallingBlockEntity); - Vec3d vec3d = new Vec3d(x, y, z); net.momirealms.craftengine.core.world.World world = new BukkitWorld(fallingBlock.getWorld()); - builder.withParameter(CommonParameters.LOCATION, vec3d); - builder.withParameter(CommonParameters.WORLD, world); + WorldPosition position = new WorldPosition(world, Reflections.field$Entity$xo.getDouble(fallingBlockEntity), Reflections.field$Entity$yo.getDouble(fallingBlockEntity), Reflections.field$Entity$zo.getDouble(fallingBlockEntity)); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.FALLING_BLOCK, true) + .withParameter(DirectContextParameters.POSITION, position); for (Item item : immutableBlockState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } Object entityData = Reflections.field$Entity$entityData.get(fallingBlockEntity); boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT); if (!isSilent) { - world.playBlockSound(vec3d, immutableBlockState.sounds().destroySound()); + world.playBlockSound(position, immutableBlockState.sounds().destroySound()); } } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to handle EntityRemoveEvent", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java index 06b6bac69..381c9152f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java @@ -12,7 +12,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.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.Tuple; @@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -67,16 +68,15 @@ public class BushBlockBehavior extends BukkitBlockBehavior { int stateId = BlockStateUtils.blockStateToId(state); ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (previousState != null && !previousState.isEmpty()) { - ContextHolder.Builder builder = ContextHolder.builder(); BlockPos pos = LocationUtils.fromBlockPos(blockPos); - Vec3d vec3d = Vec3d.atCenterOf(pos); net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - builder.withParameter(CommonParameters.LOCATION, vec3d); - builder.withParameter(CommonParameters.WORLD, world); + WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position); for (Item item : previousState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } - world.playBlockSound(vec3d, previousState.sounds().breakSound()); + world.playBlockSound(position, previousState.sounds().breakSound()); FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId); } return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index e5681c5fb..29b2f7e4e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -16,12 +16,13 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.SimpleContext; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; 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.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3i; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.World; @@ -148,8 +149,8 @@ public class CropBlockBehavior extends BushBlockBehavior { int i = this.getAge(immutableBlockState) + this.boneMealBonus.getInt( SimpleContext.of( ContextHolder.builder() - .withParameter(CommonParameters.WORLD, new BukkitWorld(world)) - .withParameter(CommonParameters.LOCATION, Vec3d.atCenterOf(new Vec3i(x, y, z))) + .withParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState) + .withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(world), Vec3d.atCenterOf(new Vec3i(x, y, z)))) .build() ) ); @@ -159,7 +160,7 @@ public class CropBlockBehavior extends BushBlockBehavior { } FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); if (sendParticles) { - world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); + world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java index f90956c5f..fb98a3e5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java @@ -11,10 +11,11 @@ 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.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.Map; @@ -90,22 +91,18 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { int stateId = BlockStateUtils.blockStateToId(blockState); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null || immutableBlockState.isEmpty()) return; - ContextHolder.Builder builder = ContextHolder.builder(); - builder.withParameter(CommonParameters.FALLING_BLOCK, true); - double x = Reflections.field$Entity$xo.getDouble(fallingBlockEntity); - double y = Reflections.field$Entity$yo.getDouble(fallingBlockEntity); - double z = Reflections.field$Entity$zo.getDouble(fallingBlockEntity); - Vec3d vec3d = new Vec3d(x, y, z); net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - builder.withParameter(CommonParameters.LOCATION, vec3d); - builder.withParameter(CommonParameters.WORLD, world); + WorldPosition position = new WorldPosition(world, Reflections.field$Entity$xo.getDouble(fallingBlockEntity), Reflections.field$Entity$yo.getDouble(fallingBlockEntity), Reflections.field$Entity$zo.getDouble(fallingBlockEntity)); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.FALLING_BLOCK, true) + .withParameter(DirectContextParameters.POSITION, position); for (Item item : immutableBlockState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } Object entityData = Reflections.field$Entity$entityData.get(fallingBlockEntity); boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT); if (!isSilent) { - world.playBlockSound(vec3d, immutableBlockState.sounds().destroySound()); + world.playBlockSound(position, immutableBlockState.sounds().destroySound()); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java index 3b0c868bc..14b4efb72 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -52,7 +52,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { 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); + world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 1.5, z + 0.5, 20, 2, 0, 2); } return true; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index cf88d726f..0da9cba81 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -14,13 +14,14 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; 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.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -122,13 +123,12 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior { if (isWaterLogged(immutableBlockState)) { bukkitWorld.setBlockData(pos.x(), pos.y(), pos.z(), Material.WATER.createBlockData()); } - Vec3d vec3d = Vec3d.atCenterOf(pos); net.momirealms.craftengine.core.world.World world = new BukkitWorld(bukkitWorld); + WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(CommonParameters.LOCATION, vec3d) - .withParameter(CommonParameters.WORLD, world); + .withParameter(DirectContextParameters.POSITION, position); for (Item item : immutableBlockState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 06d41970e..6242f1103 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -120,7 +120,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior { 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 + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); + world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); } return success; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java index 1deb5712f..7606c343c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java @@ -14,11 +14,12 @@ 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.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.List; @@ -58,15 +59,14 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { if (currentState != null && !currentState.isEmpty()) { // break the sugar cane FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false); - Vec3d vec3d = Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos)); net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); + WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos))); ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(CommonParameters.LOCATION, vec3d) - .withParameter(CommonParameters.WORLD, world); + .withParameter(DirectContextParameters.POSITION, position); for (Item item : currentState.getDrops(builder, world, null)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } - world.playBlockSound(vec3d, currentState.sounds().breakSound()); + world.playBlockSound(position, currentState.sounds().breakSound()); FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java index d8bc859de..ffe08c8fd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java @@ -2,14 +2,15 @@ package net.momirealms.craftengine.bukkit.entity; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.World; import java.lang.ref.WeakReference; +import java.util.UUID; -public class BukkitEntity extends Entity { +public class BukkitEntity extends AbstractEntity { private final WeakReference entity; public BukkitEntity(org.bukkit.entity.Entity entity) { @@ -18,17 +19,17 @@ public class BukkitEntity extends Entity { @Override public double x() { - return literalObject().getLocation().getX(); + return literalObject().getX(); } @Override public double y() { - return literalObject().getLocation().getY(); + return literalObject().getY(); } @Override public double z() { - return literalObject().getLocation().getZ(); + return literalObject().getZ(); } @Override @@ -42,12 +43,12 @@ public class BukkitEntity extends Entity { @Override public float getXRot() { - return literalObject().getLocation().getYaw(); + return literalObject().getYaw(); } @Override public float getYRot() { - return literalObject().getLocation().getPitch(); + return literalObject().getPitch(); } @Override @@ -69,4 +70,14 @@ public class BukkitEntity extends Entity { public Key type() { return KeyUtils.namespacedKey2Key(literalObject().getType().getKey()); } + + @Override + public String name() { + return literalObject().getName(); + } + + @Override + public UUID uuid() { + return literalObject().getUniqueId(); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/InteractionEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/InteractionEntityData.java index 250f58909..c571504aa 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/InteractionEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/InteractionEntityData.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.entity.data; import net.momirealms.craftengine.core.util.VersionHelper; public class InteractionEntityData extends BaseEntityData { - // Interaction only public static final InteractionEntityData Width = of(8, EntityDataValue.Serializers$FLOAT, 1F); public static final InteractionEntityData Height = of(9, EntityDataValue.Serializers$FLOAT, 1F); public static final InteractionEntityData Responsive = of(10, EntityDataValue.Serializers$BOOLEAN, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java index 403fbb075..cc3324f6d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.entity.data; import net.momirealms.craftengine.bukkit.util.Reflections; public class TextDisplayEntityData extends DisplayEntityData { - // Text display only public static final DisplayEntityData Text = of(23, EntityDataValue.Serializers$COMPONENT, Reflections.instance$Component$empty); public static final DisplayEntityData LineWidth = of(24, EntityDataValue.Serializers$INT, 200); public static final DisplayEntityData BackgroundColor = of(25, EntityDataValue.Serializers$INT, 0x40000000); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java new file mode 100644 index 000000000..4575d61aa --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.bukkit.entity.data; + +import net.momirealms.craftengine.bukkit.util.Reflections; + +public class ThrowableItemProjectileData extends BaseEntityData { + public static final ThrowableItemProjectileData ItemStack = new ThrowableItemProjectileData<>(8, EntityDataValue.Serializers$ITEM_STACK, Reflections.instance$ItemStack$Air); + + public ThrowableItemProjectileData(int id, Object serializer, T defaultValue) { + super(id, serializer, defaultValue); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java index f1b88b0fb..e2d2a29f8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java @@ -4,15 +4,16 @@ import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement; -import net.momirealms.craftengine.core.entity.furniture.Billboard; -import net.momirealms.craftengine.core.entity.furniture.ItemDisplayContext; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -22,7 +23,7 @@ import java.util.UUID; import java.util.function.Consumer; public class BukkitFurnitureElement extends AbstractFurnitureElement { - private List cachedValues; + private final List commonValues; public BukkitFurnitureElement(Key item, Billboard billboard, @@ -30,35 +31,40 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement { Vector3f scale, Vector3f translation, Vector3f offset, - Quaternionf rotation) { - super(item, billboard, transform, scale, translation, offset, rotation); + Quaternionf rotation, + boolean applyDyedColor) { + super(item, billboard, transform, scale, translation, offset, rotation, applyDyedColor); + this.commonValues = new ArrayList<>(); + ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues); + ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues); + ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues); + ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues); + ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues); } @Override - public void initPackets(int entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer packets) { + public void initPackets(int entityId, @NotNull WorldPosition position, @NotNull Quaternionf conjugated, Integer dyedColor, Consumer packets) { Vector3f offset = conjugated.transform(new Vector3f(position())); packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, + entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(), Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0 )); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues())); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(dyedColor))); } - private synchronized List getCachedValues() { - if (this.cachedValues == null) { - this.cachedValues = new ArrayList<>(); - Item item = BukkitItemManager.instance().createWrappedItem(item(), null); - if (item == null) { - CraftEngine.instance().logger().warn("Failed to create furniture element for " + item() + " because item " + item() + " not found"); - item = BukkitItemManager.instance().wrap(new ItemStack(Material.STONE)); + private synchronized List getCachedValues(Integer color) { + List cachedValues = new ArrayList<>(this.commonValues); + Item item = BukkitItemManager.instance().createWrappedItem(item(), null); + if (item == null) { + CraftEngine.instance().debug(() -> "Failed to create furniture element because item " + item() + " not found"); + item = BukkitItemManager.instance().wrap(new ItemStack(Material.BARRIER)); + } else { + if (color != null) { + item.dyedColor(color); + item.load(); } - ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), this.cachedValues); - ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.cachedValues); - ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.cachedValues); - ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.cachedValues); - ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.cachedValues); - ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.cachedValues); } - return this.cachedValues; + ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), cachedValues); + return cachedValues; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index faad973b1..5d35be647 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -4,20 +4,29 @@ import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitB import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler; import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; 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; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.*; import org.bukkit.entity.*; import org.bukkit.event.HandlerList; @@ -28,13 +37,16 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3f; import javax.annotation.Nullable; +import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_id")); - public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type")); + // DEPRECATED + // public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type")); + public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_extra_data")); public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_to_base_entity")); public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_vector")); public static final NamespacedKey FURNITURE_COLLISION = Objects.requireNonNull(NamespacedKey.fromString("craftengine:collision")); @@ -63,19 +75,23 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public Furniture place(CustomFurniture furniture, Vec3d vec3d, net.momirealms.craftengine.core.world.World world, AnchorType anchorType, boolean playSound) { - return this.place(furniture, new Location((World) world.platformWorld(), vec3d.x(), vec3d.y(), vec3d.z()), anchorType, playSound); + public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { + return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound); } - public LoadedFurniture place(CustomFurniture furniture, Location location, AnchorType anchorType, boolean playSound) { - if (furniture.isAllowedPlacement(anchorType)) { - anchorType = furniture.getAnyPlacement(); + public LoadedFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { + Optional optionalAnchorType = extraData.anchorType(); + if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { + extraData.anchorType(furniture.getAnyPlacement()); } - AnchorType finalAnchorType = anchorType; Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { ItemDisplay display = (ItemDisplay) entity; display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); - display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, finalAnchorType.name()); + try { + display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); + } catch (IOException e) { + this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); + } handleBaseEntityLoadEarly(display); }); if (playSound) { @@ -86,11 +102,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.furnitureParser; } - public class FurnitureParser implements ConfigSectionParser { + public class FurnitureParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; @Override @@ -124,6 +140,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); Map placementArguments = MiscUtils.castToMap(entry.getValue(), true); + Optional optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> MiscUtils.getAsVector3f(it, "loot-spawn-offset")); + // furniture display elements List elements = new ArrayList<>(); List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); @@ -138,7 +156,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { 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") + MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation"), + (boolean) element.getOrDefault("apply-dyed-color", true) ); elements.add(furnitureElement); } @@ -178,7 +197,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { hitboxes.toArray(new HitBox[0]), rotationRule, alignmentRule, - externalModel + externalModel, + optionalLootSpawnOffset )); } else { placements.put(anchorType, new CustomFurniture.Placement( @@ -186,7 +206,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { hitboxes.toArray(new HitBox[0]), RotationRule.ANY, AlignmentRule.CENTER, - externalModel + externalModel, + optionalLootSpawnOffset )); } } @@ -196,7 +217,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { // get loot table LootTable lootTable = lootMap == null ? null : LootTable.fromMap(lootMap); - CustomFurniture furniture = new CustomFurniture(id, settings, placements, lootTable); + EnumMap>> events = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")); + CustomFurniture furniture = new CustomFurniture(id, settings, placements, events, lootTable); byId.put(id, furniture); } } @@ -217,9 +239,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { handleCollisionEntityLoadOnEntitiesLoad(interaction); } else if (entity instanceof Boat boat) { handleCollisionEntityLoadOnEntitiesLoad(boat); - } else if (entity instanceof Shulker shulker) { - // TODO 移除这一行,预计过一个月 - handleCollisionEntityLoadOnEntitiesLoad(shulker); } } } @@ -294,17 +313,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { boolean preventChange = FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); if (above1_20_1) { if (!preventChange) { - LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture)); + LoadedFurniture furniture = addNewFurniture(display, customFurniture); furniture.initializeColliders(); for (Player player : display.getTrackedPlayers()) { - this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds()); + this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player)); } } } else { - LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture)); + LoadedFurniture furniture = addNewFurniture(display, customFurniture); for (Player player : display.getTrackedPlayers()) { - this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds()); + this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player)); } if (preventChange) { @@ -366,7 +385,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { CustomFurniture customFurniture = optionalFurniture.get(); LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); if (previous != null) return; - LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture)); + LoadedFurniture furniture = addNewFurniture(display, customFurniture); furniture.initializeColliders(); // safely do it here } } @@ -387,24 +406,37 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { collisionEntity.remove(); } - private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) { - String anchorType = baseEntity.getPersistentDataContainer().get(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING); - if (anchorType != null) { - try { - AnchorType unverified = AnchorType.valueOf(anchorType); - if (furniture.isAllowedPlacement(unverified)) { - return unverified; - } - } catch (IllegalArgumentException ignored) { - } - } - AnchorType anchorTypeEnum = furniture.getAnyPlacement(); - baseEntity.getPersistentDataContainer().set(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, anchorTypeEnum.name()); - return anchorTypeEnum; + private FurnitureExtraData getFurnitureExtraData(Entity baseEntity) throws IOException { + byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); + if (extraData == null) return FurnitureExtraData.builder().build(); + return FurnitureExtraData.fromBytes(extraData); } - private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture, AnchorType anchorType) { - LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, anchorType); +// private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) { +// String anchorType = baseEntity.getPersistentDataContainer().get(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING); +// if (anchorType != null) { +// try { +// AnchorType unverified = AnchorType.valueOf(anchorType); +// if (furniture.isAllowedPlacement(unverified)) { +// return unverified; +// } +// } catch (IllegalArgumentException ignored) { +// } +// } +// AnchorType anchorTypeEnum = furniture.getAnyPlacement(); +// baseEntity.getPersistentDataContainer().set(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, anchorTypeEnum.name()); +// return anchorTypeEnum; +// } + + private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) { + FurnitureExtraData extraData; + try { + extraData = getFurnitureExtraData(display); + } catch (IOException e) { + extraData = FurnitureExtraData.builder().build(); + plugin.logger().warn("Furniture extra data could not be loaded", e); + } + LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, extraData); this.furnitureByRealEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture); for (int entityId : loadedFurniture.entityIds()) { this.furnitureByEntityId.put(entityId, loadedFurniture); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java index 40505ab8d..daaeddaf6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; import org.bukkit.attribute.Attribute; @@ -24,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.io.IOException; import java.lang.ref.WeakReference; import java.util.*; @@ -31,6 +33,7 @@ public class LoadedFurniture implements Furniture { private final Key id; private final CustomFurniture furniture; private final AnchorType anchorType; + private FurnitureExtraData extraData; // location private final Location location; // base entity @@ -54,10 +57,11 @@ public class LoadedFurniture implements Furniture { public LoadedFurniture(Entity baseEntity, CustomFurniture furniture, - AnchorType anchorType) { + FurnitureExtraData extraData) { this.id = furniture.id(); + this.extraData = extraData; this.baseEntityId = baseEntity.getEntityId(); - this.anchorType = anchorType; + this.anchorType = extraData.anchorType().orElse(furniture.getAnyPlacement()); this.location = baseEntity.getLocation(); this.baseEntity = new WeakReference<>(baseEntity); this.furniture = furniture; @@ -75,9 +79,9 @@ public class LoadedFurniture implements Furniture { } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id, e); } - hasExternalModel = true; + this.hasExternalModel = true; } else { - hasExternalModel = false; + this.hasExternalModel = false; } float yaw = this.location.getYaw(); @@ -92,10 +96,12 @@ public class LoadedFurniture implements Furniture { List colliders = new ArrayList<>(); World world = world(); + WorldPosition position = new WorldPosition(world, x, y, z, yaw, 0); + Integer dyedColor = this.extraData.dyedColor().orElse(null); for (FurnitureElement element : placement.elements()) { int entityId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); fakeEntityIds.add(entityId); - element.initPackets(entityId, world, x, y, z, yaw, conjugated, packet -> { + element.initPackets(entityId, position, conjugated, dyedColor, packet -> { packets.add(packet); if (this.minimized) minimizedPackets.add(packet); }); @@ -107,7 +113,7 @@ public class LoadedFurniture implements Furniture { mainEntityIds.add(entityId); this.hitBoxes.put(entityId, hitBox); } - hitBox.initPacketsAndColliders(ids, world, x, y, z, yaw, conjugated, (packet, canBeMinimized) -> { + hitBox.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> { packets.add(packet); if (this.minimized && !canBeMinimized) { minimizedPackets.add(packet); @@ -176,6 +182,17 @@ public class LoadedFurniture implements Furniture { return baseEntity().isValid(); } + @NotNull + public Location dropLocation() { + Optional dropOffset = config().getPlacement(this.anchorType).dropOffset(); + if (dropOffset.isEmpty()) { + return location(); + } + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); + return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); + } + @Override public void destroy() { if (!isValid()) { @@ -294,7 +311,27 @@ public class LoadedFurniture implements Furniture { spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat); } - public void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) { + @Override + public FurnitureExtraData extraData() { + return this.extraData; + } + + @Override + public void setExtraData(FurnitureExtraData extraData) { + this.extraData = extraData; + this.save(); + } + + @Override + public void save() { + try { + this.baseEntity().getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, this.extraData.toBytes()); + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to save furniture data.", e); + } + } + + private void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) { Location location = this.calculateSeatLocation(seat); Entity seatEntity = seat.limitPlayerRotation() ? EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java index 5a3e0780e..6d2fe233b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java @@ -9,7 +9,7 @@ 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.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.NamespacedKey; import org.bukkit.Registry; @@ -51,11 +51,11 @@ public class CustomHitBox extends AbstractHitBox { } @Override - public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { + public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { Vector3f offset = conjugated.transform(new Vector3f(position())); try { packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, + entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(), FastNMS.INSTANCE.toNMSEntityType(this.entityType), 0, Reflections.instance$Vec3$Zero, 0 ), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java index dae5e7afe..1aeeb4966 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java @@ -4,7 +4,7 @@ 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.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -33,7 +33,7 @@ public class HappyGhastHitBox extends AbstractHitBox { } @Override - public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { + public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java index e48713e89..587034e32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java @@ -9,7 +9,7 @@ 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.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -53,8 +53,12 @@ public class InteractionHitBox extends AbstractHitBox { } @Override - public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { + public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { Vector3f offset = conjugated.transform(new Vector3f(position())); + double x = position.x(); + double y = position.y(); + double z = position.z(); + float yaw = position.xRot(); packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0 @@ -66,7 +70,7 @@ public class InteractionHitBox extends AbstractHitBox { if (blocksBuilding() || this.canBeHitByProjectile()) { AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y); Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - collider.accept(new BukkitCollider(world.serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding())); + collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding())); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java index 56d8985a4..530cb80b1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -196,9 +197,13 @@ public class ShulkerHitBox extends AbstractHitBox { } @Override - public void initPacketsAndColliders(int[] entityIds, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { + public void initPacketsAndColliders(int[] entityIds, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb) { Vector3f offset = conjugated.transform(new Vector3f(position())); try { + double x = position.x(); + double y = position.y(); + double z = position.z(); + float yaw = position.xRot(); double originalY = y + offset.y; double integerPart = Math.floor(originalY); double fractionalPart = originalY - integerPart; @@ -228,7 +233,7 @@ public class ShulkerHitBox extends AbstractHitBox { Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false); } - this.spawner.accept(entityIds, world, x, y, z, yaw, offset, packets, collider, aabb); + this.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets, collider, aabb); } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitCustomProjectile.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitCustomProjectile.java new file mode 100644 index 000000000..45c4193bd --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitCustomProjectile.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.entity.projectile; + +import net.momirealms.craftengine.core.entity.projectile.AbstractCustomProjectile; +import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; +import net.momirealms.craftengine.core.item.Item; +import org.bukkit.entity.Projectile; +import org.bukkit.inventory.ItemStack; + +public class BukkitCustomProjectile extends AbstractCustomProjectile { + + public BukkitCustomProjectile(ProjectileMeta meta, Projectile projectile, Item projectileItem) { + super(meta, new BukkitProjectile(projectile), projectileItem); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectile.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectile.java new file mode 100644 index 000000000..5ddf01516 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectile.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.bukkit.entity.projectile; + +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.core.entity.projectile.Projectile; + +public class BukkitProjectile extends BukkitEntity implements Projectile { + + public BukkitProjectile(org.bukkit.entity.Projectile entity) { + super(entity); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectileManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectileManager.java new file mode 100644 index 000000000..3d3ad8f87 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/BukkitProjectileManager.java @@ -0,0 +1,196 @@ +package net.momirealms.craftengine.bukkit.entity.projectile; + +import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import io.papermc.paper.event.player.PlayerStopUsingItemEvent; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask; +import net.momirealms.craftengine.bukkit.util.ParticleUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.projectile.CustomProjectile; +import net.momirealms.craftengine.core.entity.projectile.ProjectileManager; +import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Bukkit; +import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.world.EntitiesLoadEvent; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class BukkitProjectileManager implements Listener, ProjectileManager { + private static BukkitProjectileManager instance; + private final BukkitCraftEngine plugin; + private final Map projectiles; + + public BukkitProjectileManager(BukkitCraftEngine plugin) { + this.plugin = plugin; + this.projectiles = new ConcurrentHashMap<>(); + instance = this; + } + + @Override + public void delayedInit() { + Bukkit.getPluginManager().registerEvents(this, this.plugin.bootstrap()); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @Override + public Optional projectileByEntityId(int entityId) { + return Optional.ofNullable(this.projectiles.get(entityId)); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onProjectileLaunch(ProjectileLaunchEvent event) { + Projectile projectile = event.getEntity(); + handleProjectileLoad(projectile); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEntitiesLoad(EntitiesLoadEvent event) { + for (Entity entity : event.getEntities()) { + if (entity instanceof Projectile projectile) { + handleProjectileLoad(projectile); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onEntityRemove(EntityRemoveFromWorldEvent event) { + this.projectiles.remove(event.getEntity().getEntityId()); + } + + private void handleProjectileLoad(Projectile projectile) { + ItemStack projectileItem; + if (projectile instanceof ThrowableProjectile throwableProjectile) { + projectileItem = throwableProjectile.getItem(); + } else if (projectile instanceof Arrow arrow) { + projectileItem = arrow.getItemStack(); + } else { + return; + } + Item wrapped = this.plugin.itemManager().wrap(projectileItem); + if (wrapped == null) return; + wrapped.getCustomItem().ifPresent(it -> { + ProjectileMeta meta = it.settings().projectileMeta(); + if (meta != null) { + this.projectiles.put(projectile.getEntityId(), new BukkitCustomProjectile(meta, projectile, wrapped)); + new ProjectileInjectTask(projectile); + } + }); + } + + @EventHandler + public void onPlayerInteract(PlayerItemConsumeEvent event) { + ItemStack item = event.getItem(); + String type = getType(item); + if (type == null) return; + if (type.equals("bow") || type.equals("spear")) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPlayerStopUsingItem(PlayerStopUsingItemEvent event) { + ItemStack item = event.getItem(); + String type = getType(item); + if (type == null) return; + int ticksHeldFor = event.getTicksHeldFor(); + Player player = event.getPlayer(); + if (type.equals("bow")) { + if (ticksHeldFor < 3) return; + // player.sendMessage("可以投出自定义弓: " + item.getType() + " 持续 " + ticksHeldFor + " 刻"); + } else if (type.equals("trident")) { + if (ticksHeldFor < 10) return; + // player.sendMessage("可以投出自定义三叉戟: " + item.getType() + " 持续 " + ticksHeldFor + " 刻"); + Object nmsItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(item); + Object nmsServerLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(player); + boolean success = TridentRelease.releaseUsing(nmsItemStack, nmsServerLevel, nmsEntity); + // player.sendMessage("释放成功: " + success); + } + } + + @Nullable + private String getType(ItemStack item) { + Item wrapped = BukkitItemManager.instance().wrap(item); + Optional> optionalCustomItem = wrapped.getCustomItem(); + if (optionalCustomItem.isEmpty()) return null; + CustomItem customItem = optionalCustomItem.get(); + ProjectileMeta meta = customItem.settings().projectileMeta(); + if (meta == null) return null; + return meta.type(); + } + + public class ProjectileInjectTask implements Runnable { + private final Projectile projectile; + private final SchedulerTask task; + private boolean injected; + + public ProjectileInjectTask(Projectile projectile) { + this.projectile = projectile; + if (VersionHelper.isFolia()) { + this.task = new FoliaTask(projectile.getScheduler().runAtFixedRate(plugin.bootstrap(), (t) -> this.run(), () -> {}, 1, 1)); + } else { + this.task = plugin.scheduler().sync().runRepeating(this, 1, 1); + } + } + + @Override + public void run() { + if (!this.projectile.isValid()) { + this.task.cancel(); + return; + } + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(this.projectile); + if (!this.injected) { + Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity); + if (trackedEntity == null) { + return; + } + Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity); + if (serverEntity == null) { + return; + } + try { + Reflections.field$ServerEntity$updateInterval.set(serverEntity, 1); + this.injected = true; + } catch (ReflectiveOperationException e) { + plugin.logger().warn("Failed to update server entity tracking interval", e); + } + } + if (canSpawnParticle(nmsEntity)) { + this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0); + } + } + + private static boolean canSpawnParticle(Object nmsEntity) { + if (!FastNMS.INSTANCE.field$Entity$wasTouchingWater(nmsEntity)) return false; + if (Reflections.clazz$AbstractArrow.isInstance(nmsEntity)) { + return !FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity); + } + return true; + } + } + + public static BukkitProjectileManager instance() { + return instance; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/TridentRelease.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/TridentRelease.java new file mode 100644 index 000000000..549b3f8f8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/projectile/TridentRelease.java @@ -0,0 +1,515 @@ +package net.momirealms.craftengine.bukkit.entity.projectile; + +import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; + +public class TridentRelease { + + private TridentRelease() {} + + public static boolean releaseUsing(Object stack, Object level, Object entity) { + if (VersionHelper.isOrAbove1_21_2()) { + return releaseUsing_1_21_2(stack, level, entity); + } else if (VersionHelper.isOrAbove1_21()) { + return releaseUsing_1_21(stack, level, entity); + } else if (VersionHelper.isOrAbove1_20_5()) { + return releaseUsing_1_20_5(stack, level, entity); + } else if (VersionHelper.isOrAbove1_20_3()) { + return releaseUsing_1_20_3(stack, level, entity); + } else if (VersionHelper.isOrAbove1_20()) { + return releaseUsing_1_20(stack, level, entity); + } + return false; + } + + private static boolean releaseUsing_1_21_2(Object stack, Object level, Object entity) { + Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false; + Object copyStack1 = FastNMS.INSTANCE.method$ItemStack$copy(stack); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack1)) return false; + float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity); + if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) { + return false; + } + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack1, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + + Object sound = FastNMS.INSTANCE.method$EnchantmentHelper$pickHighestLevel(stack); + + if (spinStrength == 0.0F) { + Object projectile = FastNMS.INSTANCE.method$Projectile$ThrownTrident$spawnProjectileFromRotationDelayed( + level, + copyStack, + entity, + 0.0F, + 2.5F, + 1.0F + ); + PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent( + (Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack), + (Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(FastNMS.INSTANCE.method$Projectile$Delayed$projectile(projectile)) + ); + + if (!event.callEvent() || !FastNMS.INSTANCE.method$Projectile$Delayed$attemptSpawn(projectile)) { + FastNMS.INSTANCE.method$AbstractContainerMenu$sendAllDataToRemote(FastNMS.INSTANCE.field$Player$containerMenu(entity)); + return false; + } + + Object trident = FastNMS.INSTANCE.method$Projectile$Delayed$projectile(projectile); + if (event.shouldConsume()) { + FastNMS.INSTANCE.method$ItemStack$hurtWithoutBreaking(stack, 1, entity); + FastNMS.INSTANCE.method$ItemStack$consume(stack, 1, entity); + } + + FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(trident, copyStack1); + if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) { + FastNMS.INSTANCE.field$AbstractArrow$pickup(trident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY); + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + trident, + FastNMS.INSTANCE.method$Holder$value(sound), + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + + float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity); + float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity); + float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD); + float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + + float length = MCUtils.sqrt(x * x + y * y + z * z); + x = x / length * spinStrength; + y = y / length * spinStrength; + z = z / length * spinStrength; + + FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z); + FastNMS.INSTANCE.method$Entity$push(entity, x, y, z); + FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, 8.0F, stack); + + if (FastNMS.INSTANCE.method$Entity$onGround(entity)) { + FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D)); + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entity, + FastNMS.INSTANCE.method$Holder$value(sound), + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + + private static boolean releaseUsing_1_21(Object stack, Object level, Object entity) { + Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false; + + float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity); + + if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) { + return false; + } + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + + Object sound = FastNMS.INSTANCE.method$EnchantmentHelper$pickHighestLevel(stack); + + if (spinStrength == 0.0F) { + Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack); + FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation( + entitythrowntrident, + entity, + FastNMS.INSTANCE.method$Entity$getXRot(entity), + FastNMS.INSTANCE.method$Entity$getYRot(entity), + 0.0F, 2.5F, 1.0F + ); + if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) { + FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY); + } + + PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent( + (Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack), + (Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident) + ); + if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) { + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity); + if (bukkitEntity instanceof Player player) { + player.updateInventory(); + } + + return false; + } + + if (event.shouldConsume()) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + stack, 1, entity, + FastNMS.INSTANCE.method$LivingEntity$getSlotForHand(FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity)) + ); + } + + FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack); + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entitythrowntrident, + FastNMS.INSTANCE.method$Holder$value(sound), + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + if (event.shouldConsume() && !FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) { + FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack); + } + return true; + } + + float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity); + float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity); + float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD); + float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + + float length = MCUtils.sqrt(x * x + y * y + z * z); + x = x / length * spinStrength; + y = y / length * spinStrength; + z = z / length * spinStrength; + + FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z); + FastNMS.INSTANCE.method$Entity$push(entity, x, y, z); + FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, 8.0F, stack); + + if (FastNMS.INSTANCE.method$Entity$onGround(entity)) { + FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D)); + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entity, + FastNMS.INSTANCE.method$Holder$value(sound), + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + + private static boolean releaseUsing_1_20_5(Object stack, Object level, Object entity) { + Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false; + + float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity); + + if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) { + return false; + } + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + + if (spinStrength == 0.0F) { + Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack); + FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation( + entitythrowntrident, + entity, + FastNMS.INSTANCE.method$Entity$getXRot(entity), + FastNMS.INSTANCE.method$Entity$getYRot(entity), + 0.0F, 2.5F, 1.0F + ); + if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) { + FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY); + } + + PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent( + (Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack), + (Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident) + ); + if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) { + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity); + if (bukkitEntity instanceof Player player) { + player.updateInventory(); + } + + return false; + } + + if (event.shouldConsume()) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + stack, 1, entity, + FastNMS.INSTANCE.method$LivingEntity$getSlotForHand(FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity)) + ); + } + + FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack); + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entitythrowntrident, + Reflections.instance$SoundEvent$TRIDENT_THROW, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + if (event.shouldConsume() && !FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) { + FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack); + } + return true; + } + + float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity); + float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity); + float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD); + float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + + float length = MCUtils.sqrt(x * x + y * y + z * z); + x = x / length * spinStrength; + y = y / length * spinStrength; + z = z / length * spinStrength; + + FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z); + FastNMS.INSTANCE.method$Entity$push(entity, x, y, z); + FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null); + + if (FastNMS.INSTANCE.method$Entity$onGround(entity)) { + FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D)); + } + + Object soundeffect; + if (spinStrength >= 3) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3; + } else if (spinStrength == 2) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2; + } else { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1; + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entity, + soundeffect, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + + private static boolean releaseUsing_1_20_3(Object stack, Object level, Object entity) { + Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false; + + float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity); + + if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) { + return false; + } + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + + if (spinStrength == 0.0F) { + Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack); + FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation( + entitythrowntrident, + entity, + FastNMS.INSTANCE.method$Entity$getXRot(entity), + FastNMS.INSTANCE.method$Entity$getYRot(entity), + 0.0F, 2.5F, 1.0F + ); + if (FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) { + FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY); + } + + PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent( + (Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack), + (Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident) + ); + if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) { + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity); + if (bukkitEntity instanceof Player player) { + player.updateInventory(); + } + + return false; + } + + if (event.shouldConsume()) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + stack, 1, entity, + (player1) -> FastNMS.INSTANCE.method$LivingEntity$broadcastBreakEvent(player1, FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity)) + ); + } + + FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack); + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entitythrowntrident, + Reflections.instance$SoundEvent$TRIDENT_THROW, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + if (event.shouldConsume() && !FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) { + FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack); + } + return true; + } + + float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity); + float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity); + float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD); + float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + + float length = MCUtils.sqrt(x * x + y * y + z * z); + x = x / length * spinStrength; + y = y / length * spinStrength; + z = z / length * spinStrength; + + FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z); + FastNMS.INSTANCE.method$Entity$push(entity, x, y, z); + FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null); + + if (FastNMS.INSTANCE.method$Entity$onGround(entity)) { + FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D)); + } + + Object soundeffect; + if (spinStrength >= 3) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3; + } else if (spinStrength == 2) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2; + } else { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1; + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entity, + soundeffect, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + + private static boolean releaseUsing_1_20(Object stack, Object level, Object entity) { + Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false; + + float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity); + + if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) { + return false; + } + FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + + if (spinStrength == 0.0F) { + Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack); + FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation( + entitythrowntrident, + entity, + FastNMS.INSTANCE.method$Entity$getXRot(entity), + FastNMS.INSTANCE.method$Entity$getYRot(entity), + 0.0F, 2.5F, 1.0F + ); + if (FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) { + FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY); + } + + PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent( + (Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack), + (Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident) + ); + if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) { + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity); + if (bukkitEntity instanceof Player player) { + player.updateInventory(); + } + + return false; + } + + if (event.shouldConsume()) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + stack, 1, entity, + (player1) -> FastNMS.INSTANCE.method$LivingEntity$broadcastBreakEvent(player1, FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity)) + ); + } + + FastNMS.INSTANCE.field$ThrownTrident$tridentItem(entitythrowntrident, copyStack); + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entitythrowntrident, + Reflections.instance$SoundEvent$TRIDENT_THROW, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + if (event.shouldConsume() && !FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) { + FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack); + } + return true; + } + + float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity); + float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity); + float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD); + float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD); + + float length = MCUtils.sqrt(x * x + y * y + z * z); + x = x / length * spinStrength; + y = y / length * spinStrength; + z = z / length * spinStrength; + + FastNMS.INSTANCE.method$Entity$push(entity, x, y, z); + FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true); + FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1); + FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null); + + if (FastNMS.INSTANCE.method$Entity$onGround(entity)) { + FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D)); + } + + Object soundeffect; + if (spinStrength >= 3) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3; + } else if (spinStrength == 2) { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2; + } else { + soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1; + } + + FastNMS.INSTANCE.method$Level$playSound( + level, + null, + entity, + soundeffect, + Reflections.instance$SoundSource$PLAYERS, + 1.0F, 1.0F + ); + return true; + } + +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java index 93e2ad6db..f3ac95c70 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java @@ -6,14 +6,15 @@ import net.momirealms.craftengine.bukkit.util.MaterialUtils; import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.util.Key; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; public class BukkitCustomItem implements CustomItem { private final Key id; @@ -26,6 +27,7 @@ public class BukkitCustomItem implements CustomItem { private final NetworkItemDataProcessor[] networkItemDataProcessors; private final List behaviors; private final ItemSettings settings; + private final EnumMap>> events; @SuppressWarnings("unchecked") public BukkitCustomItem(Key id, @@ -34,10 +36,12 @@ public class BukkitCustomItem implements CustomItem { List> modifiers, List> clientBoundModifiers, List behaviors, - ItemSettings settings) { + ItemSettings settings, + EnumMap>> events) { this.id = id; this.material = material; this.materialKey = materialKey; + this.events = events; // unchecked cast this.modifiers = modifiers.toArray(new ItemDataModifier[0]); // unchecked cast @@ -65,6 +69,13 @@ public class BukkitCustomItem implements CustomItem { this.networkItemDataProcessors = networkItemDataProcessors.toArray(new NetworkItemDataProcessor[0]); } + @Override + public void execute(PlayerOptionalContext context, EventTrigger trigger) { + for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { + function.run(context); + } + } + @Override public Key id() { return this.id; @@ -145,6 +156,7 @@ public class BukkitCustomItem implements CustomItem { private Material material; private Key materialKey; private ItemSettings settings; + private EnumMap>> events = new EnumMap<>(EventTrigger.class); private final List behaviors = new ArrayList<>(); private final List> modifiers = new ArrayList<>(); private final List> clientBoundModifiers = new ArrayList<>(); @@ -204,10 +216,16 @@ public class BukkitCustomItem implements CustomItem { return this; } + @Override + public Builder events(EnumMap>> events) { + this.events = events; + return this; + } + @Override public CustomItem build() { this.modifiers.addAll(this.settings.modifiers()); - return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings); + return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings, this.events); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 9296dd4c5..5210f9e4a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -1,10 +1,14 @@ package net.momirealms.craftengine.bukkit.item; +import com.saicone.rtag.item.ItemTagStream; import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.BoneMealItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; +import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener; +import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener; +import net.momirealms.craftengine.bukkit.item.listener.ItemEventListener; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ItemUtils; @@ -27,8 +31,9 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.select.ChargeTypeSelectProperty; import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectProperty; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.event.EventFunctions; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -62,6 +67,7 @@ public class BukkitItemManager extends AbstractItemManager { private final BukkitCraftEngine plugin; private final ItemEventListener itemEventListener; private final DebugStickListener debugStickListener; + private final ArmorEventListener armorEventListener; private final ItemParser itemParser; public BukkitItemManager(BukkitCraftEngine plugin) { @@ -71,6 +77,7 @@ public class BukkitItemManager extends AbstractItemManager { this.factory = BukkitItemFactory.create(plugin); this.itemEventListener = new ItemEventListener(plugin); this.debugStickListener = new DebugStickListener(plugin); + this.armorEventListener = new ArmorEventListener(); this.itemParser = new ItemParser(); this.registerAllVanillaItems(); if (plugin.hasMod()) { @@ -129,6 +136,7 @@ public class BukkitItemManager extends AbstractItemManager { public void delayedInit() { Bukkit.getPluginManager().registerEvents(this.itemEventListener, this.plugin.bootstrap()); Bukkit.getPluginManager().registerEvents(this.debugStickListener, this.plugin.bootstrap()); + Bukkit.getPluginManager().registerEvents(this.armorEventListener, this.plugin.bootstrap()); } public static BukkitItemManager instance() { @@ -161,10 +169,16 @@ public class BukkitItemManager extends AbstractItemManager { this.unload(); HandlerList.unregisterAll(this.itemEventListener); HandlerList.unregisterAll(this.debugStickListener); + HandlerList.unregisterAll(this.armorEventListener); } @Override - public ConfigSectionParser parser() { + public Item fromByteArray(byte[] bytes) { + return this.factory.wrap(ItemTagStream.INSTANCE.fromBytes(bytes)); + } + + @Override + public ConfigParser parser() { return this.itemParser; } @@ -224,7 +238,7 @@ public class BukkitItemManager extends AbstractItemManager { return wrapped.id(); } - public class ItemParser implements ConfigSectionParser { + public class ItemParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; @Override @@ -344,12 +358,15 @@ public class BukkitItemManager extends AbstractItemManager { itemSettings.canPlaceRelatedVanillaBlock(true); } itemBuilder.settings(itemSettings); + itemBuilder.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))); CustomItem customItem = itemBuilder.build(); customItems.put(id, customItem); // cache command suggestions cachedSuggestions.add(Suggestion.suggestion(id.toString())); + + // TODO Deprecated 理论支持任意物品类型 if (material == Material.TOTEM_OF_UNDYING) cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); @@ -411,6 +428,7 @@ public class BukkitItemManager extends AbstractItemManager { } if (Config.packMinVersion() < 21.39f) { + // TODO 手动指定旧版格式 List legacyOverridesModels = new ArrayList<>(); processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, customModelData); TreeSet lom = legacyOverrides.computeIfAbsent(materialId, k -> new TreeSet<>()); @@ -463,6 +481,12 @@ public class BukkitItemManager extends AbstractItemManager { baseModel.path(), customModelData )); + } else if (currentModel instanceof SpecialItemModel specialModel) { + resultList.add(new LegacyOverridesModel( + new LinkedHashMap<>(accumulatedPredicates), + specialModel.base(), + customModelData + )); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java index 75f692f57..b7c1eb500 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java @@ -14,11 +14,11 @@ public class ComponentItemWrapper implements ItemWrapper { private final ItemStack item; public ComponentItemWrapper(final ItemStack item) { - this.item = item; + this.item = FastNMS.INSTANCE.ensureCraftItemStack(item); } public ComponentItemWrapper(final ItemStack item, int count) { - this.item = item; + this.item = FastNMS.INSTANCE.ensureCraftItemStack(item); this.item.setAmount(count); } @@ -71,7 +71,7 @@ public class ComponentItemWrapper implements ItemWrapper { @Override public ItemWrapper copyWithCount(int count) { - ItemStack copied = item.clone(); + ItemStack copied = this.item.clone(); copied.setAmount(count); return new ComponentItemWrapper(copied); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentTypes.java index 904cac448..d755ab11b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentTypes.java @@ -25,6 +25,7 @@ public class ComponentTypes { public static final Object REPAIR_COST = getComponentType(ComponentKeys.REPAIR_COST); public static final Object CUSTOM_DATA = getComponentType(ComponentKeys.CUSTOM_DATA); public static final Object PROFILE = getComponentType(ComponentKeys.PROFILE); + public static final Object DYED_COLOR = getComponentType(ComponentKeys.DYED_COLOR); private ComponentTypes() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java deleted file mode 100644 index bc5e9772f..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java +++ /dev/null @@ -1,230 +0,0 @@ -package net.momirealms.craftengine.bukkit.item; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior; -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.player.InteractionHand; -import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.item.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.behavior.ItemBehavior; -import net.momirealms.craftengine.core.item.context.UseOnContext; -import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.world.BlockHitResult; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -public class ItemEventListener implements Listener { - private final BukkitCraftEngine plugin; - - public ItemEventListener(BukkitCraftEngine plugin) { - this.plugin = plugin; - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) - public void onInteractBlock(PlayerInteractEvent event) { - Action action = event.getAction(); - if (action != Action.LEFT_CLICK_BLOCK && action != Action.RIGHT_CLICK_BLOCK) { - return; - } - Block block = Objects.requireNonNull(event.getClickedBlock()); - Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); - int stateId = BlockStateUtils.blockStateToId(blockState); - if (BlockStateUtils.isVanillaBlock(stateId)) { - return; - } - - // it's breaking the block - if (action == Action.LEFT_CLICK_BLOCK && event.getPlayer().getGameMode() == GameMode.CREATIVE) { - return; - } - - CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent( - event.getPlayer(), - block.getLocation(), - event.getInteractionPoint(), - BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId), - block, - event.getBlockFace(), - event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND, - action.isRightClick() ? CustomBlockInteractEvent.Action.RIGHT_CLICK : CustomBlockInteractEvent.Action.LEFT_CLICK, - event.getItem() - ); - if (EventUtils.fireAndCheckCancel(interactEvent)) { - event.setCancelled(true); - } - } - - @EventHandler - public void onInteractAir(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_AIR) - return; - Player bukkitPlayer = event.getPlayer(); - BukkitServerPlayer player = this.plugin.adapt(bukkitPlayer); - InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; - if (cancelEventIfHasInteraction(event, player, hand)) { - return; - } - if (player.isSpectatorMode()) { - return; - } - - // Gets the item in hand - Item itemInHand = player.getItemInHand(hand); - // should never be null - if (itemInHand == null) return; - Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); - - if (optionalItemBehaviors.isPresent()) { - for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { - InteractionResult result = itemBehavior.use(player.world(), player, hand); - if (result == InteractionResult.SUCCESS_AND_CANCEL) { - event.setCancelled(true); - return; - } - if (result != InteractionResult.PASS) { - return; - } - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onInteractAtBlock(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (event.useItemInHand() == Event.Result.DENY || event.useInteractedBlock() == Event.Result.DENY) return; - Location interactionPoint = event.getInteractionPoint(); - if (interactionPoint == null) return; - Player bukkitPlayer = event.getPlayer(); - Block clickedBlock = Objects.requireNonNull(event.getClickedBlock()); - BukkitServerPlayer player = this.plugin.adapt(bukkitPlayer); - InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; - if (cancelEventIfHasInteraction(event, player, hand)) { - return; - } - - // Gets the item in hand - Item itemInHand = player.getItemInHand(hand); - if (itemInHand == null) return; - Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); - - // has custom item behavior - if (optionalItemBehaviors.isPresent()) { - BlockPos pos = LocationUtils.toBlockPos(clickedBlock.getLocation()); - Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); - Direction direction = DirectionUtils.toDirection(event.getBlockFace()); - BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false); - boolean interactable = InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerId(clickedBlock), bukkitPlayer, clickedBlock.getBlockData(), hitResult, itemInHand); - - // do not allow to place block if it's a vanilla block - Optional> optionalCustomItem = itemInHand.getCustomItem(); - if (itemInHand.isBlockItem() && optionalCustomItem.isPresent()) { - // it's a custom item, but now it's ignored - if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) { - return; - } - if (!interactable || player.isSecondaryUseActive()) { - event.setCancelled(true); - } - } - - if (!player.isSecondaryUseActive() && interactable) { - // if it's interactable on server, cancel the custom behaviors - return; - } - - // no spectator interactions - if (player.isSpectatorMode()) { - return; - } - - for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { - InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(player, hand, hitResult)); - if (result == InteractionResult.SUCCESS_AND_CANCEL) { - event.setCancelled(true); - return; - } - int maxY = player.world().worldHeight().getMaxBuildHeight() - 1; - if (direction == Direction.UP - && result != InteractionResult.SUCCESS - && pos.y() >= maxY - && itemBehavior instanceof BlockItemBehavior - ) { - player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED)); - return; - } - if (result != InteractionResult.PASS) { - return; - } - } - return; - } - - // it's a vanilla block - if (itemInHand.isBlockItem() && !itemInHand.isCustomItem()) { - // client won't have sounds if the fake block is interactable - // so we should check and resend sounds on interact - Object blockState = BlockStateUtils.blockDataToBlockState(clickedBlock.getBlockData()); - int stateId = BlockStateUtils.blockStateToId(blockState); - ImmutableBlockState againstCustomBlock = BukkitBlockManager.instance().getImmutableBlockState(stateId); - if (againstCustomBlock == null || againstCustomBlock.isEmpty()) { - return; - } - - BlockPos pos = LocationUtils.toBlockPos(clickedBlock.getLocation()); - Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); - Direction direction = DirectionUtils.toDirection(event.getBlockFace()); - BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false); - try { - BlockData craftBlockData = BlockStateUtils.fromBlockData(againstCustomBlock.vanillaBlockState().handle()); - if (InteractUtils.isInteractable(KeyUtils.namespacedKey2Key(craftBlockData.getMaterial().getKey()), bukkitPlayer, craftBlockData, hitResult, itemInHand)) { - if (!player.isSecondaryUseActive()) { - player.setResendSound(); - } - } else { - if (BlockStateUtils.isReplaceable(againstCustomBlock.customBlockState().handle()) && !BlockStateUtils.isReplaceable(againstCustomBlock.vanillaBlockState().handle())) { - player.setResendSwing(); - } - } - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to get CraftBlockData", e); - } - } - } - - private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) { - if (hand == InteractionHand.OFF_HAND) { - int currentTicks = player.gameTicks(); - // The client will send multiple packets to the server if the client thinks it should - // However, if the main hand item interaction is successful, the off-hand item should be blocked. - if (player.lastSuccessfulInteractionTick() == currentTicks) { - event.setCancelled(true); - return true; - } - } - return false; - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index 732978b30..d1e6b038e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -89,7 +89,7 @@ public class AxeItemBehavior extends ItemBehavior { bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1); // resend swing if it's not interactable on client side - if (!InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerIdFromState(state.vanillaBlockState().handle()), + if (!InteractUtils.isInteractable( bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), item ) || player.isSecondaryUseActive()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index 847935ca0..00fbf33cc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -1,11 +1,14 @@ package net.momirealms.craftengine.bukkit.item.behavior; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.api.event.CustomBlockAttemptPlaceEvent; import net.momirealms.craftengine.bukkit.api.event.CustomBlockPlaceEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; @@ -20,11 +23,17 @@ 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.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Cancellable; +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.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Bukkit; import org.bukkit.GameEvent; import org.bukkit.Location; @@ -38,7 +47,6 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import javax.annotation.Nullable; import java.nio.file.Path; import java.util.Map; import java.util.Optional; @@ -57,34 +65,32 @@ public class BlockItemBehavior extends ItemBehavior { } public InteractionResult place(BlockPlaceContext context) { - if (!context.canPlace()) { - return InteractionResult.FAIL; - } Optional optionalBlock = BukkitBlockManager.instance().blockById(this.blockId); if (optionalBlock.isEmpty()) { CraftEngine.instance().logger().warn("Failed to place unknown block " + this.blockId); return InteractionResult.FAIL; } - CustomBlock block = optionalBlock.get(); - BlockPlaceContext placeContext = updatePlacementContext(context); - if (placeContext == null) { + if (!context.canPlace()) { return InteractionResult.FAIL; } - ImmutableBlockState blockStateToPlace = getPlacementState(placeContext, block); + + CustomBlock block = optionalBlock.get(); + BlockPos pos = context.getClickedPos(); + int maxY = context.getLevel().worldHeight().getMaxBuildHeight() - 1; + if (context.getClickedFace() == Direction.UP && pos.y() >= maxY) { + context.getPlayer().sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED)); + return InteractionResult.FAIL; + } + + ImmutableBlockState blockStateToPlace = getPlacementState(context, block); if (blockStateToPlace == null) { return InteractionResult.FAIL; } - Player player = placeContext.getPlayer(); - BlockPos pos = placeContext.getClickedPos(); - BlockPos againstPos = placeContext.getAgainstPos(); - World world = (World) placeContext.getLevel().platformWorld(); + + Player player = context.getPlayer(); + BlockPos againstPos = context.getAgainstPos(); + World world = (World) context.getLevel().platformWorld(); Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z()); - - int gameTicks = player.gameTicks(); - if (!player.updateLastSuccessfulInteractionTick(gameTicks)) { - return InteractionResult.FAIL; - } - Block bukkitBlock = world.getBlockAt(placeLocation); Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z()); org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); @@ -105,6 +111,11 @@ public class BlockItemBehavior extends ItemBehavior { } } + int gameTicks = player.gameTicks(); + if (!player.updateLastSuccessfulInteractionTick(gameTicks)) { + return InteractionResult.FAIL; + } + // trigger event CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand()); @@ -117,7 +128,7 @@ public class BlockItemBehavior extends ItemBehavior { // place custom block CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false); // call bukkit event - BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) placeContext.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); + BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) { // revert changes previousState.update(true, false); @@ -132,24 +143,32 @@ public class BlockItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } + WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5); + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(bukkitBlock)) + .withParameter(DirectContextParameters.POSITION, position) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.HAND, context.getHand()) + .withParameter(DirectContextParameters.ITEM_IN_HAND, context.getItem()) + ); + block.execute(functionContext, EventTrigger.PLACE); + if (dummy.isCancelled()) { + return InteractionResult.SUCCESS_AND_CANCEL; + } + if (!player.isCreativeMode()) { - Item item = placeContext.getItem(); + Item item = context.getItem(); item.count(item.count() - 1); item.load(); } - player.swingHand(placeContext.getHand()); - placeContext.getLevel().playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), blockStateToPlace.sounds().placeSound()); + player.swingHand(context.getHand()); + context.getLevel().playBlockSound(position, blockStateToPlace.sounds().placeSound()); world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z())); return InteractionResult.SUCCESS; } - // for child class to override - @Nullable - public BlockPlaceContext updatePlacementContext(BlockPlaceContext context) { - return context; - } - protected ImmutableBlockState getPlacementState(BlockPlaceContext context, CustomBlock block) { ImmutableBlockState state = block.getStateForPlacement(context); return state != null && this.canPlace(context, state) ? state : null; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index ff062ebec..20773f4f1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -8,8 +8,10 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; import net.momirealms.craftengine.core.entity.furniture.HitBox; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -19,6 +21,10 @@ 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.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; @@ -126,7 +132,15 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } - LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().place(customFurniture, furnitureLocation.clone(), anchorType, false); + Item item = context.getItem(); + + LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().place( + furnitureLocation.clone(), customFurniture, + FurnitureExtraData.builder() + .item(item.copyWithCount(1)) + .anchorType(anchorType) + .dyedColor(item.dyedColor().orElse(null)) + .build(), false); FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, loadedFurniture, furnitureLocation, context.getHand()); if (EventUtils.fireAndCheckCancel(placeEvent)) { @@ -134,8 +148,20 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, loadedFurniture) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.HAND, context.getHand()) + .withParameter(DirectContextParameters.ITEM_IN_HAND, item) + ); + customFurniture.execute(functionContext, EventTrigger.PLACE); + if (dummy.isCancelled()) { + return InteractionResult.SUCCESS_AND_CANCEL; + } + if (!player.isCreativeMode()) { - Item item = context.getItem(); item.count(item.count() - 1); item.load(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java index 149f335f5..8606b48a6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.item.factory; import com.google.gson.JsonElement; +import com.saicone.rtag.item.ItemTagStream; import net.momirealms.craftengine.bukkit.util.ItemTags; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.item.EquipmentData; @@ -39,15 +40,21 @@ public abstract class BukkitItemFactory> extend case "1.21.4" -> { return new ComponentItemFactory1_21_4(plugin); } - case "1.21.5", "1.22", "1.22.1" -> { + case "1.21.5", "1.21.6", "1.22", "1.22.1" -> { return new ComponentItemFactory1_21_5(plugin); } default -> throw new IllegalStateException("Unsupported server version: " + plugin.serverVersion()); } } + @Override + protected byte[] toByteArray(W item) { + return ItemTagStream.INSTANCE.toBytes(item.getItem()); + } + @Override protected boolean isBlockItem(W item) { + // todo 这个 isBlockItem 他考虑组件了吗??? return item.getItem().getType().isBlock(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 08cc98ec3..9261ae98d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -244,6 +244,30 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory dyedColor(ComponentItemWrapper item) { + if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty(); + Object javaObj = ComponentType.encodeJava( + ComponentTypes.DYED_COLOR, + item.getComponent(ComponentTypes.DYED_COLOR) + ).orElse(null); + if (javaObj instanceof Integer integer) { + return Optional.of(integer); + } else if (javaObj instanceof Map map) { + return Optional.of((int) map.get("rgb")); + } + return Optional.empty(); + } + + @Override + protected void dyedColor(ComponentItemWrapper item, Integer color) { + if (color == null) { + item.resetComponent(ComponentTypes.DYED_COLOR); + } else { + item.setJavaComponent(ComponentTypes.DYED_COLOR, color); + } + } + @Override protected Optional maxDamage(ComponentItemWrapper item) { if (!item.hasComponent(ComponentTypes.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability()); @@ -309,7 +333,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory map = EnchantmentUtils.toMap(enchant); - map.put(enchantment.toString(), enchantment.level()); + map.put(enchantment.id().toString(), enchantment.level()); item.setJavaComponent(ComponentTypes.ENCHANTMENTS, map); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to add enchantment", e); @@ -321,7 +345,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory map = EnchantmentUtils.toMap(enchant); - map.put(enchantment.toString(), enchantment.level()); + map.put(enchantment.id().toString(), enchantment.level()); item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, map); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to add stored enchantment", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 787dba986..dbf97a69d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -152,6 +152,21 @@ public class UniversalItemFactory extends BukkitItemFactory { item.set(damage, "Damage"); } + @Override + protected Optional dyedColor(LegacyItemWrapper item) { + if (!item.hasTag("display", "color")) return Optional.empty(); + return Optional.of(item.get("display", "color")); + } + + @Override + protected void dyedColor(LegacyItemWrapper item, Integer color) { + if (color == null) { + item.remove("display", "color"); + } else { + item.set(color, "display", "color"); + } + } + @Override protected Optional maxDamage(LegacyItemWrapper item) { return Optional.of((int) item.getItem().getType().getMaxDurability()); @@ -198,9 +213,9 @@ public class UniversalItemFactory extends BukkitItemFactory { return; } } - item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "Enchantments"); + item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "Enchantments"); } else { - item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "Enchantments"); + item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "Enchantments"); } } @@ -214,9 +229,9 @@ public class UniversalItemFactory extends BukkitItemFactory { return; } } - item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "StoredEnchantments"); + item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "StoredEnchantments"); } else { - item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "StoredEnchantments"); + item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "StoredEnchantments"); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ArmorEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ArmorEventListener.java new file mode 100644 index 000000000..0b748e981 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ArmorEventListener.java @@ -0,0 +1,102 @@ +package net.momirealms.craftengine.bukkit.item.listener; + +import net.momirealms.craftengine.bukkit.api.CraftEngineItems; +import net.momirealms.craftengine.bukkit.util.ItemUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Material; +import org.bukkit.entity.Horse; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.inventory.HorseInventory; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +public class ArmorEventListener implements Listener { + + // 只有在没有equippable组件的版本才生效,阻止自定义物品放到马上 + // 低版本没有自定义盔甲,所以完全不需要考虑能放置上去的情况 + @EventHandler(ignoreCancelled = true) + public void onInteractHorse(PlayerInteractEntityEvent event) { + if (VersionHelper.isOrAbove1_21_2()) return; + if (event.getRightClicked() instanceof Horse horse) { + ItemStack itemInHand = event.getPlayer().getInventory().getItem(event.getHand()); + if (horse.getInventory().getArmor() == null) { + switch (itemInHand.getType()) { + case LEATHER_HORSE_ARMOR, IRON_HORSE_ARMOR, GOLDEN_HORSE_ARMOR, DIAMOND_HORSE_ARMOR -> { + if (CraftEngineItems.isCustomItem(itemInHand)) { + event.setCancelled(true); + } + } + } + } else if (horse.getInventory().getSaddle() == null) { + if (itemInHand.getType() == Material.SADDLE) { + if (CraftEngineItems.isCustomItem(itemInHand)) { + event.setCancelled(true); + } + } + } + } + } + + // 处理低版本的马物品栏 + @EventHandler(ignoreCancelled = true) + public void onMoveItemInHorseInventory(InventoryClickEvent event) { + if (VersionHelper.isOrAbove1_21_2()) return; + if (!(event.getInventory() instanceof HorseInventory horseInventory)) { + return; + } + if (event.getClickedInventory() == event.getWhoClicked().getInventory()) { + ItemStack currentItem = event.getCurrentItem(); + if (event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) { + if (currentItem != null && CraftEngineItems.isCustomItem(currentItem)) { + event.setCancelled(true); + } + } + } else if (event.getClickedInventory() == horseInventory) { + ItemStack itemInCursor = event.getCursor(); + if (event.getAction() == InventoryAction.SWAP_WITH_CURSOR || event.getAction() == InventoryAction.PLACE_ALL || event.getAction() == InventoryAction.PLACE_ONE) { + if (!ItemUtils.isEmpty(itemInCursor) && CraftEngineItems.isCustomItem(itemInCursor)) { + event.setCancelled(true); + return; + } + } + if (event.getAction() == InventoryAction.HOTBAR_SWAP) { + int slot = event.getHotbarButton(); + if (slot != -1) { + ItemStack itemInHotBar = event.getWhoClicked().getInventory().getItem(slot); + if (!ItemUtils.isEmpty(itemInHotBar) && CraftEngineItems.isCustomItem(itemInHotBar)) { + event.setCancelled(true); + return; + } + } else { + ItemStack offHand = event.getWhoClicked().getInventory().getItemInOffHand(); + if (!ItemUtils.isEmpty(offHand) && CraftEngineItems.isCustomItem(offHand)) { + event.setCancelled(true); + return; + } + } + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onMoveItemInHorseInventory(InventoryDragEvent event) { + if (VersionHelper.isOrAbove1_21_2()) return; + if (!(event.getInventory() instanceof HorseInventory horseInventory)) { + return; + } + for (Map.Entry item : event.getNewItems().entrySet()) { + if (item.getKey() == 0 || item.getKey() == 1) { + if (!ItemUtils.isEmpty(item.getValue()) && CraftEngineItems.isCustomItem(item.getValue())) { + event.setCancelled(true); + return; + } + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/DebugStickListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java similarity index 98% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/DebugStickListener.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java index af5bbd4fd..ff87989ed 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/DebugStickListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java @@ -1,8 +1,9 @@ -package net.momirealms.craftengine.bukkit.item; +package net.momirealms.craftengine.bukkit.item.listener; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java new file mode 100644 index 000000000..183db3a06 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -0,0 +1,312 @@ +package net.momirealms.craftengine.bukkit.item.listener; + +import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; +import net.momirealms.craftengine.core.util.Cancellable; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.world.BlockHitResult; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class ItemEventListener implements Listener { + private final BukkitCraftEngine plugin; + + public ItemEventListener(BukkitCraftEngine plugin) { + this.plugin = plugin; + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onInteractBlock(PlayerInteractEvent event) { + Action action = event.getAction(); + Player player = event.getPlayer(); + if ( + (action != Action.LEFT_CLICK_BLOCK && action != Action.RIGHT_CLICK_BLOCK) || /* block is required */ + (player.getGameMode() == GameMode.SPECTATOR) || /* no spectator interactions */ + (action == Action.LEFT_CLICK_BLOCK && player.getGameMode() == GameMode.CREATIVE) /* it's breaking the block */ + ) { + return; + } + + BukkitServerPlayer serverPlayer = this.plugin.adapt(player); + InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; + // 如果本tick内主手已被处理,则不处理副手 + // 这是因为客户端可能会同时发主副手交互包,但实际上只能处理其中一个 + if (this.cancelEventIfHasInteraction(event, serverPlayer, hand)) { + return; + } + + // some common data + Block block = Objects.requireNonNull(event.getClickedBlock()); + BlockData blockData = block.getBlockData(); + Object blockState = BlockStateUtils.blockDataToBlockState(blockData); + ImmutableBlockState immutableBlockState = null; + int stateId = BlockStateUtils.blockStateToId(blockState); + Item itemInHand = serverPlayer.getItemInHand(hand); + + // 处理自定义方块 + if (!BlockStateUtils.isVanillaBlock(stateId)) { + immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); + // call the event if it's custom + CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent( + player, + block.getLocation(), + event.getInteractionPoint(), + immutableBlockState, + block, + event.getBlockFace(), + hand, + action.isRightClick() ? CustomBlockInteractEvent.Action.RIGHT_CLICK : CustomBlockInteractEvent.Action.LEFT_CLICK, + event.getItem() + ); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + event.setCancelled(true); + return; + } + + Cancellable dummy = Cancellable.dummy(); + // run custom functions + CustomBlock customBlock = immutableBlockState.owner().value(); + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState) + .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + ); + if (action.isRightClick()) customBlock.execute(context, EventTrigger.RIGHT_CLICK); + else customBlock.execute(context, EventTrigger.LEFT_CLICK); + if (dummy.isCancelled()) { + event.setCancelled(true); + return; + } + } + + Optional> optionalCustomItem = itemInHand == null ? Optional.empty() : itemInHand.getCustomItem(); + boolean hasItem = itemInHand != null; + boolean hasCustomItem = optionalCustomItem.isPresent(); + + // interact block with items + if (hasItem && action == Action.RIGHT_CLICK_BLOCK) { + Location interactionPoint = Objects.requireNonNull(event.getInteractionPoint(), "interaction point should not be null"); + Direction direction = DirectionUtils.toDirection(event.getBlockFace()); + BlockPos pos = LocationUtils.toBlockPos(block.getLocation()); + Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); + BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false); + + // handle block item + if (itemInHand.isBlockItem()) { + // vanilla item + if (!hasCustomItem) { + // interact a custom block + if (immutableBlockState != null) { + // client won't have sounds if the clientside block is interactable + // so we should check and resend sounds on BlockPlaceEvent + BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); + if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) { + if (!serverPlayer.isSecondaryUseActive()) { + serverPlayer.setResendSound(); + } + } else { + if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().handle()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().handle())) { + serverPlayer.setResendSwing(); + } + } + } + } + // custom item + else { + if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) { + // 如果用户设置了允许放置对应的原版方块,那么直接返回。 + // 这种情况下最好是return,以避免同时触发多个behavior发生冲突 + // 当用户选择其作为原版方块放下时,自定义行为可能已经不重要了? + return; + } else { + // todo 实际上这里的处理并不正确,因为判断玩家是否能够放置那个方块需要更加细节的判断。比如玩家无法对着树叶放置火把,但是交互事件依然触发,此情况下不可丢弃自定义行为。 + if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) { + event.setCancelled(true); + } + } + } + } + + // execute item right click functions + if (hasCustomItem) { + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withOptionalParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) + .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.EVENT, dummy) + ); + CustomItem customItem = optionalCustomItem.get(); + customItem.execute(context, EventTrigger.RIGHT_CLICK); + if (dummy.isCancelled()) { + event.setCancelled(true); + return; + } + } + + // 检查其他的物品行为,物品行为理论只在交互时处理 + Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); + // 物品类型是否包含自定义物品行为,行为不一定来自于自定义物品,部分原版物品也包含了新的行为 + if (optionalItemBehaviors.isPresent()) { + // 检测是否可交互应当只判断原版方块,因为自定义方块早就判断过了,如果可交互不可能到这一步 + boolean interactable = immutableBlockState == null && InteractUtils.isInteractable(player, blockData, hitResult, itemInHand); + // 如果方块可交互但是玩家没shift,那么原版的方块交互优先,取消自定义物品的behavior + // todo 如果我的物品行为允许某些交互呢?是否值得进一步处理? + if (!serverPlayer.isSecondaryUseActive() && interactable) { + return; + } + // 依次执行物品行为 + for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { + InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(serverPlayer, hand, hitResult)); + if (result == InteractionResult.SUCCESS_AND_CANCEL) { + event.setCancelled(true); + return; + } + // 非pass的情况直接结束 + if (result != InteractionResult.PASS) { + return; + } + } + } + } + + if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) { + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withOptionalParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) + .withParameter(DirectContextParameters.HAND, hand) + ); + CustomItem customItem = optionalCustomItem.get(); + customItem.execute(context, EventTrigger.LEFT_CLICK); + if (dummy.isCancelled()) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler + public void onInteractAir(PlayerInteractEvent event) { + Action action = event.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR) + return; + Player player = event.getPlayer(); + BukkitServerPlayer serverPlayer = this.plugin.adapt(player); + if (serverPlayer.isSpectatorMode()) + return; + // Gets the item in hand + InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; + Item itemInHand = serverPlayer.getItemInHand(hand); + // should never be null + if (itemInHand == null) return; + + // todo 真的需要这个吗 +// if (cancelEventIfHasInteraction(event, serverPlayer, hand)) { +// return; +// } + + Optional> optionalCustomItem = itemInHand.getCustomItem(); + if (optionalCustomItem.isPresent()) { + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.HAND, hand) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + ); + CustomItem customItem = optionalCustomItem.get(); + if (action.isRightClick()) customItem.execute(context, EventTrigger.RIGHT_CLICK); + else customItem.execute(context, EventTrigger.LEFT_CLICK); + } + + if (action.isRightClick()) { + Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); + if (optionalItemBehaviors.isPresent()) { + for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { + InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand); + if (result == InteractionResult.SUCCESS_AND_CANCEL) { + event.setCancelled(true); + return; + } + if (result != InteractionResult.PASS) { + return; + } + } + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onConsumeItem(PlayerItemConsumeEvent event) { + ItemStack consumedItem = event.getItem(); + if (ItemUtils.isEmpty(consumedItem)) return; + Item wrapped = this.plugin.itemManager().wrap(consumedItem); + Optional> optionalCustomItem = wrapped.getCustomItem(); + if (optionalCustomItem.isEmpty()) { + return; + } + Cancellable dummy = Cancellable.dummy(); + CustomItem customItem = optionalCustomItem.get(); + PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder() + .withParameter(DirectContextParameters.CONSUMED_ITEM, wrapped) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND) + ); + customItem.execute(context, EventTrigger.CONSUME); + if (dummy.isCancelled()) { + event.setCancelled(true); + return; + } + } + + private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) { + if (hand == InteractionHand.OFF_HAND) { + int currentTicks = player.gameTicks(); + // The client will send multiple packets to the server if the client thinks it should + // However, if the main hand item interaction is successful, the off-hand item should be blocked. + if (player.lastSuccessfulInteractionTick() == currentTicks) { + event.setCancelled(true); + return true; + } + } + return false; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index aaa910a99..469cb7e1e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -330,14 +330,12 @@ public class RecipeEventListener implements Listener { if (clicked == null) return; Material type = clicked.getType(); if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; - if (!VersionHelper.isOrAbove1_21_2()) { - if (clicked.getState() instanceof Campfire campfire) { - try { - Object blockEntity = Reflections.field$CraftBlockEntityState$tileEntity.get(campfire); - BukkitInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } + if (clicked.getState() instanceof Campfire campfire) { + try { + Object blockEntity = Reflections.field$CraftBlockEntityState$tileEntity.get(campfire); + BukkitInjector.injectCookingBlockEntity(blockEntity); + } catch (Exception e) { + this.plugin.logger().warn("Failed to inject cooking block entity", e); } } @@ -704,59 +702,72 @@ public class RecipeEventListener implements Listener { try { Object mcRecipe = Reflections.field$CraftComplexRecipe$recipe.get(complexRecipe); - if (!Reflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) { + + // Repair recipe + if (Reflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) { + // repair item + ItemStack[] itemStacks = inventory.getMatrix(); + Pair onlyTwoItems = getTheOnlyTwoItem(itemStacks); + if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) { + inventory.setResult(null); + return; + } + + Item left = plugin.itemManager().wrap(onlyTwoItems.left()); + Item right = plugin.itemManager().wrap(onlyTwoItems.right()); + if (!left.id().equals(right.id())) { + inventory.setResult(null); + return; + } + + int totalDamage = right.damage().orElse(0) + left.damage().orElse(0); + int totalMaxDamage = left.maxDamage().get() + right.maxDamage().get(); + // should be impossible, but take care + if (totalDamage >= totalMaxDamage) { + inventory.setResult(null); + return; + } + + Player player; + try { + player = (Player) Reflections.method$InventoryView$getPlayer.invoke(event.getView()); + } catch (ReflectiveOperationException e) { + plugin.logger().warn("Failed to get inventory viewer", e); + return; + } + + Optional> customItemOptional = plugin.itemManager().getCustomItem(left.id()); + if (customItemOptional.isEmpty()) { + inventory.setResult(null); + return; + } + + CustomItem customItem = customItemOptional.get(); + if (!customItem.settings().canRepair()) { + inventory.setResult(null); + return; + } + + Item newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player))); + int remainingDurability = totalMaxDamage - totalDamage; + int newItemDamage = Math.max(0, newItem.maxDamage().get() - remainingDurability); + newItem.damage(newItemDamage); + inventory.setResult(newItem.load()); + } else if (Reflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe)) { + ItemStack[] itemStacks = inventory.getMatrix(); + for (ItemStack itemStack : itemStacks) { + if (itemStack == null) continue; + Item item = plugin.itemManager().wrap(itemStack); + Optional> optionalCustomItem = item.getCustomItem(); + if (optionalCustomItem.isPresent() && !optionalCustomItem.get().settings().dyeable()) { + inventory.setResult(null); + return; + } + } + } else { inventory.setResult(null); return; } - - // repair item - ItemStack[] itemStacks = inventory.getMatrix(); - Pair onlyTwoItems = getTheOnlyTwoItem(itemStacks); - if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) { - inventory.setResult(null); - return; - } - - Item left = plugin.itemManager().wrap(onlyTwoItems.left()); - Item right = plugin.itemManager().wrap(onlyTwoItems.right()); - if (!left.id().equals(right.id())) { - inventory.setResult(null); - return; - } - - int totalDamage = right.damage().orElse(0) + left.damage().orElse(0); - int totalMaxDamage = left.maxDamage().get() + right.maxDamage().get(); - // should be impossible, but take care - if (totalDamage >= totalMaxDamage) { - inventory.setResult(null); - return; - } - - Player player; - try { - player = (Player) Reflections.method$InventoryView$getPlayer.invoke(event.getView()); - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to get inventory viewer", e); - return; - } - - Optional> customItemOptional = plugin.itemManager().getCustomItem(left.id()); - if (customItemOptional.isEmpty()) { - inventory.setResult(null); - return; - } - - CustomItem customItem = customItemOptional.get(); - if (!customItem.settings().canRepair()) { - inventory.setResult(null); - return; - } - - Item newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player))); - int remainingDurability = totalMaxDamage - totalDamage; - int newItemDamage = Math.max(0, newItem.maxDamage().get() - remainingDurability); - newItem.damage(newItemDamage); - inventory.setResult(newItem.load()); } catch (Exception e) { this.plugin.logger().warn("Failed to handle minecraft custom recipe", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index f994c121b..2a1d1630b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -12,12 +12,12 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.loot.VanillaLoot; 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.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; @@ -65,33 +65,31 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme } Location location = entity.getLocation(); net.momirealms.craftengine.core.world.World world = new BukkitWorld(entity.getWorld()); - Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); - ContextHolder.Builder builder = ContextHolder.builder(); - builder.withParameter(CommonParameters.WORLD, world); - builder.withParameter(CommonParameters.LOCATION, vec3d); + WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ()); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position); BukkitServerPlayer optionalPlayer = null; if (VersionHelper.isOrAbove1_20_5()) { if (event.getDamageSource().getCausingEntity() instanceof Player player) { optionalPlayer = this.plugin.adapt(player); - builder.withParameter(CommonParameters.PLAYER, optionalPlayer); - //mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND)); + builder.withParameter(DirectContextParameters.PLAYER, optionalPlayer); } } ContextHolder contextHolder = builder.build(); for (LootTable lootTable : loot.lootTables()) { for (Item item : lootTable.getRandomItems(contextHolder, world, optionalPlayer)) { - world.dropItemNaturally(vec3d, item); + world.dropItemNaturally(position, item); } } }); } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.vanillaLootParser; } - public class VanillaLootParser implements ConfigSectionParser { + public class VanillaLootParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot", "loots", "loot"}; @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index fe7f48e8f..499c2cd36 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; +import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors; @@ -40,7 +41,6 @@ import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.inventory.ItemStack; @@ -166,6 +166,7 @@ public class BukkitCraftEngine extends CraftEngine { super.vanillaLootManager = new BukkitVanillaLootManager(this); super.fontManager = new BukkitFontManager(this); super.advancementManager = new BukkitAdvancementManager(this); + super.projectileManager = new BukkitProjectileManager(this); super.onPluginEnable(); super.compatibilityManager().onEnable(); } @@ -192,15 +193,7 @@ public class BukkitCraftEngine extends CraftEngine { new Metrics(this.bootstrap(), 24333); } // tick task - if (VersionHelper.isFolia()) { - this.tickTask = this.scheduler().sync().runRepeating(() -> { - for (BukkitServerPlayer serverPlayer : networkManager().onlineUsers()) { - org.bukkit.entity.Player player = serverPlayer.platformPlayer(); - Location location = player.getLocation(); - scheduler().sync().run(serverPlayer::tick, player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - } - }, 1, 1); - } else { + if (!VersionHelper.isFolia()) { this.tickTask = this.scheduler().sync().runRepeating(() -> { for (BukkitServerPlayer serverPlayer : networkManager().onlineUsers()) { serverPlayer.tick(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java new file mode 100644 index 000000000..ddb3b04da --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.bukkit.plugin.classpath; + +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.plugin.Plugin; +import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; +import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; + +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; + +public class BukkitClassPathAppender implements ClassPathAppender { + private final URLClassLoaderAccess classLoaderAccess; + + public BukkitClassPathAppender(ClassLoader classLoader) throws IllegalAccessException { + if (Reflections.clazz$PaperPluginClassLoader != null && Reflections.clazz$PaperPluginClassLoader.isInstance(classLoader)) { + URLClassLoader libraryClassLoader = (URLClassLoader) Reflections.field$PaperPluginClassLoader$libraryLoader.get(classLoader); + this.classLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); + } else if (classLoader instanceof URLClassLoader) { + this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader); + } else { + throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + } + } + + public BukkitClassPathAppender(Plugin plugin) throws IllegalAccessException { + this(plugin.getClass().getClassLoader()); + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.classLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java index b14e164e9..549d9d5f2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java @@ -7,15 +7,10 @@ import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.sender.Sender; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.world.chunk.CESection; import org.bukkit.Chunk; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.incendo.cloud.Command; -import org.incendo.cloud.parser.standard.StringParser; public class DebugIsSectionInjectedCommand extends BukkitCommandFeature { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index df390ccf2..70914aa49 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.util.KeyUtils; @@ -55,7 +56,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { Component.text(reloadResult.asyncTime()), Component.text(reloadResult.syncTime()) ); - }); } catch (Exception e) { handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE); @@ -88,6 +87,9 @@ public class ReloadCommand extends BukkitCommandFeature { Component.text(reloadResult.syncTime()), Component.text(packTime) ); + } catch (Exception e) { + handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE); + plugin().logger().warn("Failed to generate resource pack", e); } finally { RELOAD_PACK_FLAG = false; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index 116ac584a..ab1b59966 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -3,15 +3,32 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.util.AdventureHelper; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.BooleanParser; import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.CompletableFuture; public class TestCommand extends BukkitCommandFeature { + public static final Collection TARGET_BLOCK_SUGGESTIONS = new HashSet<>(); + + static { + for (Material material : Material.values()) { + TARGET_BLOCK_SUGGESTIONS.add(Suggestion.suggestion(material.getKey().toString())); + } + } public TestCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { super(commandManager, plugin); @@ -21,11 +38,19 @@ public class TestCommand extends BukkitCommandFeature { public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder .senderType(Player.class) - .required("text", StringParser.greedyStringParser()) + .required("reset", BooleanParser.booleanParser()) + .required("setTag", NamespacedKeyParser.namespacedKeyParser()) + .required("targetBlock", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(TARGET_BLOCK_SUGGESTIONS); + } + })) .handler(context -> { - String text = ""; - PlayerOptionalContext context1 = PlayerOptionalContext.of(plugin().adapt(context.sender()), ContextHolder.builder()); - plugin().senderFactory().wrap(context.sender()).sendMessage(AdventureHelper.customMiniMessage().deserialize(text, context1.tagResolvers())); + Player player = context.sender(); + player.sendMessage("开始测试"); + NamespacedKey key = context.get("setTag"); + player.sendMessage("结束测试"); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java index 2b6d78874..79b1528a5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java @@ -1,13 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.gui; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.core.plugin.gui.AbstractGui; import net.momirealms.craftengine.core.plugin.gui.Gui; import net.momirealms.craftengine.core.plugin.gui.GuiManager; import net.momirealms.craftengine.core.plugin.gui.Inventory; -import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; -import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,7 +16,6 @@ import org.bukkit.event.inventory.InventoryDragEvent; public class BukkitGuiManager implements GuiManager, Listener { private final BukkitCraftEngine plugin; - private SchedulerTask timerTask; public BukkitGuiManager(BukkitCraftEngine plugin) { this.plugin = plugin; @@ -28,35 +24,11 @@ public class BukkitGuiManager implements GuiManager, Listener { @Override public void delayedInit() { Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); - this.timerTask = plugin.scheduler().sync().runRepeating(this::timerTask, 30, 30); } @Override public void disable() { HandlerList.unregisterAll(this); - if (this.timerTask != null && !this.timerTask.cancelled()) { - this.timerTask.cancel(); - } - } - - public void timerTask() { - if (VersionHelper.isFolia()) { - for (Player player : Bukkit.getOnlinePlayers()) { - this.plugin.scheduler().sync().run(() -> { - org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(player) : player.getOpenInventory().getTopInventory(); - if (top.getHolder() instanceof CraftEngineInventoryHolder holder) { - holder.gui().onTimer(); - } - }, player.getWorld(), player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4); - } - } else { - for (Player player : Bukkit.getOnlinePlayers()) { - org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(player) : player.getOpenInventory().getTopInventory(); - if (top.getHolder() instanceof CraftEngineInventoryHolder holder) { - holder.gui().onTimer(); - } - } - } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 9a54e77a1..60f26221c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java @@ -68,7 +68,6 @@ import java.lang.reflect.Modifier; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.Callable; import java.util.function.Consumer; @@ -827,8 +826,8 @@ public class BukkitInjector { if (previousLight != newLight || (clientSideNewState != null && (BlockStateUtils.isOcclude(newState) != BlockStateUtils.isOcclude(clientSideNewState)))) { CEWorld world = thisObj.ceChunk().world(); SectionPos sectionPos = thisObj.cePos(); - Set posSet = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, Math.max(newLight, previousLight)); - world.sectionLightUpdated(posSet); + List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, Math.max(newLight, previousLight)); + world.sectionLightUpdated(pos); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index d485c0da4..d5998aed6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -14,9 +14,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.network.ConnectionState; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; -import net.momirealms.craftengine.core.plugin.network.NetworkManager; +import net.momirealms.craftengine.core.plugin.network.*; import net.momirealms.craftengine.core.util.*; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -141,7 +139,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.RESPAWN, Reflections.clazz$ClientboundRespawnPacket); registerNMSPacketConsumer(PacketConsumers.INTERACT_ENTITY, Reflections.clazz$ServerboundInteractPacket); registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, Reflections.clazz$ClientboundEntityPositionSyncPacket); - registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos); + registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos); registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket); registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket); registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket); @@ -152,10 +150,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket); registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, Reflections.clazz$ClientboundEntityEventPacket); + registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$PosRot); registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); - registerByteBufPacketConsumer(VersionHelper.isOrAbove1_21_3() ? PacketConsumers.LEVEL_PARTICLE_1_21_3 : (VersionHelper.isOrAbove1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket()); + registerByteBufPacketConsumer(VersionHelper.isOrAbove1_21_4() ? PacketConsumers.LEVEL_PARTICLE_1_21_4 : (VersionHelper.isOrAbove1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket()); registerByteBufPacketConsumer(PacketConsumers.LEVEL_EVENT, this.packetIds.clientboundLevelEventPacket()); registerByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.OPEN_SCREEN_1_20_3 : PacketConsumers.OPEN_SCREEN_1_20, this.packetIds.clientboundOpenScreenPacket()); registerByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_TITLE_TEXT_1_20_3 : PacketConsumers.SET_TITLE_TEXT_1_20, this.packetIds.clientboundSetTitleTextPacket()); @@ -185,6 +184,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes user.setPlayer(player); this.onlineUsers.put(player.getUniqueId(), user); this.resetUserArray(); + if (VersionHelper.isFolia()) { + player.getScheduler().runAtFixedRate(plugin.bootstrap(), (t) -> user.tick(), + () -> plugin.debug(() -> "Player " + player.getName() + "'s entity scheduler is retired"), 1, 1); + } } } @@ -544,7 +547,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = new FriendlyByteBuf(buffer); int preProcessIndex = buf.readerIndex(); int packetId = buf.readVarInt(); - ByteBufPacketEvent event = new ByteBufPacketEvent(packetId, buf); + int preIndex = buf.readerIndex(); + ByteBufPacketEvent event = new ByteBufPacketEvent(packetId, buf, preIndex); BukkitNetworkManager.this.handleByteBufPacket(this.player, event); if (event.isCancelled()) { buf.clear(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NetWorkDataTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NetWorkDataTypes.java new file mode 100644 index 000000000..50b8e57bf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NetWorkDataTypes.java @@ -0,0 +1,64 @@ +package net.momirealms.craftengine.bukkit.plugin.network; + + +import net.momirealms.craftengine.core.util.FriendlyByteBuf; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class NetWorkDataTypes { + private static final Map> id2NetWorkDataTypes = new HashMap<>(); + + public static final NetWorkDataTypes CLIENT_CUSTOM_BLOCK = + new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt); + + public static final NetWorkDataTypes CANCEL_BLOCK_UPDATE = + new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean); + + static { + register(CLIENT_CUSTOM_BLOCK); + register(CANCEL_BLOCK_UPDATE); + } + + private static void register(NetWorkDataTypes type) { + id2NetWorkDataTypes.put(type.id, type); + } + + private final int id; + private final Function decoder; + private final BiConsumer encoder; + + public NetWorkDataTypes(int id, Function decoder, BiConsumer encoder) { + this.id = id; + this.decoder = decoder; + this.encoder = encoder; + } + + public T decode(FriendlyByteBuf buf) { + return decoder.apply(buf); + } + + public void encode(FriendlyByteBuf buf, T data) { + encoder.accept(buf, data); + } + + public int id() { + return id; + } + + public void writeType(FriendlyByteBuf buf) { + buf.writeVarInt(id); + } + + public static NetWorkDataTypes readType(FriendlyByteBuf buf) { + int id = buf.readVarInt(); + return id2NetWorkDataTypes.get(id); + } + + @SuppressWarnings("unchecked") + public NetWorkDataTypes as(Class clazz) { + return (NetWorkDataTypes) this; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index a5cb326fc..abd76f731 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -12,11 +12,13 @@ import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.pack.BukkitPackManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.network.handler.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -31,15 +33,13 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.network.ConnectionState; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; -import net.momirealms.craftengine.core.plugin.network.NetworkManager; -import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.network.*; import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.BlockHitResult; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.Palette; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; @@ -47,6 +47,7 @@ import net.momirealms.craftengine.core.world.chunk.packet.MCSection; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.*; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; @@ -420,7 +421,8 @@ public class PacketConsumers { return; } EnumSet> enums = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$actions(packet); - outer: { + outer: + { for (Object entry : enums) { if (entry == Reflections.instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME) { break outer; @@ -1051,7 +1053,7 @@ public class PacketConsumers { } }; - public static final BiConsumer LEVEL_PARTICLE_1_21_3 = (user, event) -> { + public static final BiConsumer LEVEL_PARTICLE_1_21_4 = (user, event) -> { try { FriendlyByteBuf buf = event.getBuffer(); boolean overrideLimiter = buf.readBoolean(); @@ -1577,9 +1579,11 @@ public class PacketConsumers { buf.writeShort(za); } } else if (type == Reflections.instance$EntityType$BLOCK_DISPLAY$registryId) { - user.entityView().put(id, Reflections.instance$EntityType$BLOCK_DISPLAY); + user.entityPacketHandlers().put(id, BlockDisplayPacketHandler.INSTANCE); } else if (type == Reflections.instance$EntityType$TEXT_DISPLAY$registryId) { - user.entityView().put(id, Reflections.instance$EntityType$TEXT_DISPLAY); + user.entityPacketHandlers().put(id, TextDisplayPacketHandler.INSTANCE); + } else if (type == Reflections.instance$EntityType$ARMOR_STAND$registryId) { + user.entityPacketHandlers().put(id, ArmorStandPacketHandler.INSTANCE); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e); @@ -1589,12 +1593,12 @@ public class PacketConsumers { public static final TriConsumer ADD_ENTITY = (user, event, packet) -> { try { Object entityType = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$type(packet); + int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) { // Furniture - int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId); if (furniture != null) { - user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds()); + user.entityPacketHandlers().computeIfAbsent(entityId, k -> new FurniturePacketHandler(furniture.fakeEntityIds())); user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { event.setCancelled(true); @@ -1602,40 +1606,36 @@ public class PacketConsumers { } } else if (entityType == BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE) { // Cancel collider entity packet - int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId); if (furniture != null) { event.setCancelled(true); + user.entityPacketHandlers().put(entityId, FurnitureCollisionPacketHandler.INSTANCE); } + } else { + BukkitProjectileManager.instance().projectileByEntityId(entityId).ifPresent(customProjectile -> { + ProjectilePacketHandler handler = new ProjectilePacketHandler(customProjectile, entityId); + event.replacePacket(handler.convertAddCustomProjectilePacket(packet)); + user.entityPacketHandlers().put(entityId, handler); + }); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e); } }; - // 1.21.3+ + // 1.21.2+ public static final TriConsumer SYNC_ENTITY_POSITION = (user, event, packet) -> { try { int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleSyncEntityPosition(user, event, packet); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityPositionSyncPacket", e); } }; - public static final TriConsumer MOVE_ENTITY = (user, event, packet) -> { - try { - int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$Pos", e); - } - }; - public static final BiConsumer REMOVE_ENTITY = (user, event) -> { try { FriendlyByteBuf buf = event.getBuffer(); @@ -1643,12 +1643,9 @@ public class PacketConsumers { IntList intList = buf.readIntIdList(); for (int i = 0, size = intList.size(); i < size; i++) { int entityId = intList.getInt(i); - user.entityView().remove(entityId); - List entities = user.furnitureView().remove(entityId); - if (entities == null) continue; - for (int subEntityId : entities) { + EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); + if (handler != null && handler.handleEntitiesRemove(intList)) { isChange = true; - intList.add(subEntityId); } } if (isChange) { @@ -1693,6 +1690,15 @@ public class PacketConsumers { if (EventUtils.fireAndCheckCancel(breakEvent)) { return; } + + // execute functions + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, furniture) + .withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position())) + ); + furniture.config().execute(context, EventTrigger.LEFT_CLICK); + furniture.config().execute(context, EventTrigger.BREAK); + CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true); } } else if (actionType == Reflections.instance$ServerboundInteractPacket$ActionType$INTERACT_AT) { @@ -1715,6 +1721,13 @@ public class PacketConsumers { return; } + // execute functions + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, furniture) + .withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position())) + ); + furniture.config().execute(context, EventTrigger.RIGHT_CLICK);; + if (player.isSneaking()) { // try placing another furniture above it AABB hitBox = furniture.aabbByEntityId(entityId); @@ -1745,7 +1758,7 @@ public class PacketConsumers { } else { furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> { if (furniture.tryOccupySeat(seatPos)) { - furniture.spawnSeatEntityForPlayer(Objects.requireNonNull(player.getPlayer()), seatPos); + furniture.spawnSeatEntityForPlayer(serverPlayer, seatPos); } }); } @@ -1795,7 +1808,7 @@ public class PacketConsumers { buf.writeLong(seed); } } else { - Optional optionalSound = FastNMS.INSTANCE.method$BuiltInRegistries$byId(Reflections.instance$BuiltInRegistries$SOUND_EVENT, id - 1); + Optional optionalSound = FastNMS.INSTANCE.method$IdMap$byId(Reflections.instance$BuiltInRegistries$SOUND_EVENT, id - 1); if (optionalSound.isEmpty()) return; Object soundEvent = optionalSound.get(); Key soundId = Key.of(FastNMS.INSTANCE.method$SoundEvent$location(soundEvent)); @@ -1966,29 +1979,38 @@ public class PacketConsumers { } else { data = (byte[]) Reflections.method$DiscardedPayload$dataByteArray.invoke(payload); } - String decodeData = new String(data, StandardCharsets.UTF_8); - if (!decodeData.endsWith("init")) return; - int firstColon = decodeData.indexOf(':'); - if (firstColon == -1) return; - int secondColon = decodeData.indexOf(':', firstColon + 1); - if (secondColon == -1) return; - int clientBlockRegistrySize = Integer.parseInt(decodeData.substring(firstColon + 1, secondColon)); - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (clientBlockRegistrySize != serverBlockRegistrySize) { - Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance( - ComponentUtils.adventureToMinecraft( - Component.translatable( - "disconnect.craftengine.block_registry_mismatch", - TranslationArgument.numeric(clientBlockRegistrySize), - TranslationArgument.numeric(serverBlockRegistrySize) - ) - ) - ); - user.nettyChannel().writeAndFlush(kickPacket); - user.nettyChannel().disconnect(); - return; + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(data)); + NetWorkDataTypes dataType = NetWorkDataTypes.readType(buf); + if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { + int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf); + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (clientBlockRegistrySize != serverBlockRegistrySize) { + Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance( + ComponentUtils.adventureToMinecraft( + Component.translatable( + "disconnect.craftengine.block_registry_mismatch", + TranslationArgument.numeric(clientBlockRegistrySize), + TranslationArgument.numeric(serverBlockRegistrySize) + ) + ) + ); + user.nettyChannel().writeAndFlush(kickPacket); + user.nettyChannel().disconnect(); + return; + } + user.setClientModState(true); + } else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { + if (!VersionHelper.isOrAbove1_20_2()) return; + if (dataType.as(Boolean.class).decode(buf)) { + FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); + dataType.writeType(bufPayload); + dataType.as(Boolean.class).encode(bufPayload, true); + Object channelKey = KeyUtils.toResourceLocation(Key.of(NetworkManager.MOD_CHANNEL)); + Object dataPayload = Reflections.constructor$DiscardedPayload.newInstance(channelKey, bufPayload.array()); + Object responsePacket = Reflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload); + user.nettyChannel().writeAndFlush(responsePacket); + } } - user.setClientModState(true); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); @@ -2000,121 +2022,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); - Object entityType = user.entityView().get(id); - if (entityType == Reflections.instance$EntityType$BLOCK_DISPLAY) { - boolean isChanged = false; - List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); - for (int i = 0; i < packedItems.size(); i++) { - Object packedItem = packedItems.get(i); - int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == EntityDataUtils.BLOCK_STATE_DATA_ID) { - Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - int stateId = BlockStateUtils.blockStateToId(blockState); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = remap(stateId); - } else { - newStateId = remapMOD(stateId); - } - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( - entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId) - )); - isChanged = true; - } else if (Config.interceptEntityName() && entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) { - Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - if (optionalTextComponent.isPresent()) { - Object textComponent = optionalTextComponent.get(); - String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (!tokens.isEmpty()) { - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( - entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) - )); - isChanged = true; - } - } - } - } - if (isChanged) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(id); - FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); - } - } else if (entityType == Reflections.instance$EntityType$TEXT_DISPLAY) { - if (Config.interceptTextDisplay()) { - boolean isChanged = false; - List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); - for (int i = 0; i < packedItems.size(); i++) { - Object packedItem = packedItems.get(i); - int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == EntityDataUtils.TEXT_DATA_ID) { - Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - if (textComponent == Reflections.instance$Component$empty) break; - String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (!tokens.isEmpty()) { - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component))); - isChanged = true; - break; - } - } - } - if (isChanged) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(id); - FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); - } - } - } else if (entityType == Reflections.instance$EntityType$ARMOR_STAND) { - if (Config.interceptArmorStand()) { - boolean isChanged = false; - List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); - for (int i = 0; i < packedItems.size(); i++) { - Object packedItem = packedItems.get(i); - int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) { - Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - if (optionalTextComponent.isPresent()) { - Object textComponent = optionalTextComponent.get(); - String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (!tokens.isEmpty()) { - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); - isChanged = true; - break; - } - } - } - } - if (isChanged) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(id); - FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); - } - } - } else if (Config.interceptEntityName()) { + EntityPacketHandler handler = user.entityPacketHandlers().get(id); + if (handler != null) { + handler.handleSetEntityData(user, event); + return; + } + if (Config.interceptEntityName()) { boolean isChanged = false; List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); for (int i = 0; i < packedItems.size(); i++) { @@ -2165,7 +2078,8 @@ public class PacketConsumers { if (hasDisplay) { displayName = buf.readNbt(false); } - outside : if (displayName != null) { + outside: + if (displayName != null) { Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) break outside; Component component = AdventureHelper.tagToComponent(displayName); @@ -2318,4 +2232,27 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); } }; + + public static final TriConsumer MOVE_POS_ENTITY = (user, event, packet) -> { + try { + int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + event.setCancelled(true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e); + } + }; + + public static final TriConsumer MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> { + try { + int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleMoveAndRotate(user, event, packet); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$PosRot", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java new file mode 100644 index 000000000..769363ce5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java @@ -0,0 +1,62 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ArmorStandPacketHandler implements EntityPacketHandler { + public static final ArmorStandPacketHandler INSTANCE = new ArmorStandPacketHandler(); + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptArmorStand()) { + return; + } + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) { + @SuppressWarnings("unchecked") + Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (optionalTextComponent.isPresent()) { + Object textComponent = optionalTextComponent.get(); + String json = ComponentUtils.minecraftToJson(textComponent); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (!tokens.isEmpty()) { + Component component = AdventureHelper.jsonToComponent(json); + for (Map.Entry token : tokens.entrySet()) { + component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); + } + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); + isChanged = true; + break; + } + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java new file mode 100644 index 000000000..8ed3f8f7b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java @@ -0,0 +1,76 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class BlockDisplayPacketHandler implements EntityPacketHandler { + public static final BlockDisplayPacketHandler INSTANCE = new BlockDisplayPacketHandler(); + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == EntityDataUtils.BLOCK_STATE_DATA_ID) { + Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + int stateId = BlockStateUtils.blockStateToId(blockState); + int newStateId; + if (!user.clientModEnabled()) { + newStateId = PacketConsumers.remap(stateId); + } else { + newStateId = PacketConsumers.remapMOD(stateId); + } + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( + entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId) + )); + isChanged = true; + } else if (Config.interceptEntityName() && entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) { + @SuppressWarnings("unchecked") + Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (optionalTextComponent.isPresent()) { + Object textComponent = optionalTextComponent.get(); + String json = ComponentUtils.minecraftToJson(textComponent); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (!tokens.isEmpty()) { + Component component = AdventureHelper.jsonToComponent(json); + for (Map.Entry token : tokens.entrySet()) { + component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); + } + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( + entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) + )); + isChanged = true; + } + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurnitureCollisionPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurnitureCollisionPacketHandler.java new file mode 100644 index 000000000..70121b19b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurnitureCollisionPacketHandler.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; + +public class FurnitureCollisionPacketHandler implements EntityPacketHandler { + public static final FurnitureCollisionPacketHandler INSTANCE = new FurnitureCollisionPacketHandler(); + + @Override + public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { + event.setCancelled(true); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java new file mode 100644 index 000000000..f9f0134a6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; + +import java.util.List; + +public class FurniturePacketHandler implements EntityPacketHandler { + private final List fakeEntities; + + public FurniturePacketHandler(List fakeEntities) { + this.fakeEntities = fakeEntities; + } + + @Override + public boolean handleEntitiesRemove(IntList entityIds) { + entityIds.addAll(this.fakeEntities); + return true; + } + + @Override + public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { + event.setCancelled(true); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java new file mode 100644 index 000000000..0243f8184 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java @@ -0,0 +1,140 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.projectile.CustomProjectile; +import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class ProjectilePacketHandler implements EntityPacketHandler { + private final CustomProjectile projectile; + private final Object cachedPacket; + private final List cachedData; + + public ProjectilePacketHandler(CustomProjectile projectile, int entityId) { + this.projectile = projectile; + this.cachedData = createCustomProjectileEntityDataValues(); + this.cachedPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, this.cachedData); + } + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(this.cachedData, buf); + } + + @Override + public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { + Object converted = convertCustomProjectilePositionSyncPacket(packet); + event.replacePacket(converted); + } + + @Override + public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + this.cachedPacket, + convertCustomProjectileMovePacket(packet, entityId) + ))); + } + + public Object convertAddCustomProjectilePacket(Object packet) { + int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); + UUID uuid = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$uuid(packet); + double x = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$x(packet); + double y = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$y(packet); + double z = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$z(packet); + float yRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yRot(packet); + float xRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xRot(packet); + Object type = Reflections.instance$EntityType$ITEM_DISPLAY; + int data = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$data(packet); + double xa = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xa(packet); + double ya = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$ya(packet); + double za = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$za(packet); + double yHeadRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yHeadRot(packet); + return FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, uuid, x, y, z, MCUtils.clamp(-xRot, -90.0F, 90.0F), -yRot, type, data, FastNMS.INSTANCE.constructor$Vec3(xa, ya, za), yHeadRot); + } + + private Object convertCustomProjectilePositionSyncPacket(Object packet) { + int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); + Object positionMoveRotation = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$values(packet); + boolean onGround = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$onGround(packet); + Object position = FastNMS.INSTANCE.field$PositionMoveRotation$position(positionMoveRotation); + Object deltaMovement = FastNMS.INSTANCE.field$PositionMoveRotation$deltaMovement(positionMoveRotation); + float yRot = FastNMS.INSTANCE.field$PositionMoveRotation$yRot(positionMoveRotation); + float xRot = FastNMS.INSTANCE.field$PositionMoveRotation$xRot(positionMoveRotation); + Object newPositionMoveRotation = FastNMS.INSTANCE.constructor$PositionMoveRotation(position, deltaMovement, -yRot, Math.clamp(-xRot, -90.0F, 90.0F)); + return FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(entityId, newPositionMoveRotation, onGround); + } + + public List createCustomProjectileEntityDataValues() { + List itemDisplayValues = new ArrayList<>(); + Optional> customItem = BukkitItemManager.instance().getCustomItem(this.projectile.metadata().item()); + if (customItem.isEmpty()) return itemDisplayValues; + ProjectileMeta meta = this.projectile.metadata(); + Item displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY); + // 我们应当使用新的展示物品的组件覆盖原物品的组件,以完成附魔,附魔光效等组件的继承 + displayedItem = this.projectile.item().mergeCopy(displayedItem); + ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(-1, itemDisplayValues); + ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(meta.translation(), itemDisplayValues); + ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(meta.scale(), itemDisplayValues); + ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(meta.rotation(), itemDisplayValues); + if (VersionHelper.isOrAbove1_20_2()) { + ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues); + ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues); + } else { + ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues); + } + ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(displayedItem.getLiteralObject(), itemDisplayValues); + ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(meta.displayType().id(), itemDisplayValues); + return itemDisplayValues; + } + + private Object convertCustomProjectileMovePacket(Object packet, int entityId) { + short xa = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xa(packet); + short ya = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$ya(packet); + short za = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$za(packet); + float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet)); + float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet)); + boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet); + return FastNMS.INSTANCE.constructor$ClientboundMoveEntityPacket$PosRot( + entityId, xa, ya, za, + MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)), + onGround + ); + } + + private Object convertCustomProjectileTeleportPacket(Object packet, int entityId) { + float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet)); + float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet)); + boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet); + return FastNMS.INSTANCE.constructor$ClientboundTeleportEntityPacket( + entityId, this.projectile.projectile().x(), this.projectile.projectile().y(), this.projectile.projectile().z(), + MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)), + onGround + ); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java new file mode 100644 index 000000000..35eb26ddd --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java @@ -0,0 +1,59 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; + +import java.util.List; +import java.util.Map; + +public class TextDisplayPacketHandler implements EntityPacketHandler { + public static final TextDisplayPacketHandler INSTANCE = new TextDisplayPacketHandler(); + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTextDisplay()) { + return; + } + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == EntityDataUtils.TEXT_DATA_ID) { + Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (textComponent == Reflections.instance$Component$empty) break; + String json = ComponentUtils.minecraftToJson(textComponent); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (!tokens.isEmpty()) { + Component component = AdventureHelper.jsonToComponent(json); + for (Map.Entry token : tokens.entrySet()) { + component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); + } + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component))); + isChanged = true; + break; + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index c3352106c..5e045570f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -7,17 +7,20 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.BlockSettings; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.PackedBlockState; +import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.network.ConnectionState; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; @@ -25,7 +28,10 @@ import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldEvents; -import org.bukkit.*; +import org.bukkit.FluidCollisionMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.SoundCategory; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.block.Block; @@ -89,9 +95,8 @@ public class BukkitServerPlayer extends Player { // cache interaction range here private int lastUpdateInteractionRangeTick; private double cachedInteractionRange; - // for better fake furniture visual sync - private final Map> furnitureView = new ConcurrentHashMap<>(); - private final Map entityTypeView = new ConcurrentHashMap<>(); + + private final Map entityTypeView = new ConcurrentHashMap<>(); public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) { this.channel = channel; @@ -103,13 +108,6 @@ public class BukkitServerPlayer extends Player { this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); this.uuid = player.getUniqueId(); this.name = player.getName(); -// if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) { -// try { -// Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true); -// } catch (Exception e) { -// this.plugin.logger().warn("Failed to setSimplifyContainerDesyncCheck", e); -// } -// } } @Override @@ -151,18 +149,18 @@ public class BukkitServerPlayer extends Player { } @Override - public boolean isCreativeMode() { - return platformPlayer().getGameMode() == GameMode.CREATIVE; + public GameMode gameMode() { + return switch (platformPlayer().getGameMode()) { + case CREATIVE -> GameMode.CREATIVE; + case SPECTATOR -> GameMode.SPECTATOR; + case ADVENTURE -> GameMode.ADVENTURE; + case SURVIVAL -> GameMode.SURVIVAL; + }; } @Override - public boolean isSpectatorMode() { - return platformPlayer().getGameMode() == GameMode.SPECTATOR; - } - - @Override - public boolean isAdventureMode() { - return platformPlayer().getGameMode() == GameMode.ADVENTURE; + public void setGameMode(GameMode gameMode) { + platformPlayer().setGameMode(Objects.requireNonNull(org.bukkit.GameMode.getByValue(gameMode.id()))); } @Override @@ -177,12 +175,22 @@ public class BukkitServerPlayer extends Player { @Override public void sendActionBar(Component text) { - try { - Object packet = Reflections.constructor$ClientboundSetActionBarTextPacket.newInstance(ComponentUtils.adventureToMinecraft(text)); - sendPacket(packet, false); - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to send action bar", e); - } + Object packet = FastNMS.INSTANCE.constructor$ClientboundActionBarPacket(ComponentUtils.adventureToMinecraft(text)); + sendPacket(packet, false); + } + + @Override + public void sendTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut) { + Object titlePacket = FastNMS.INSTANCE.constructor$ClientboundSetTitleTextPacket(ComponentUtils.adventureToMinecraft(title)); + Object subtitlePacket = FastNMS.INSTANCE.constructor$ClientboundSetSubtitleTextPacket(ComponentUtils.adventureToMinecraft(subtitle)); + Object timePacket = FastNMS.INSTANCE.constructor$ClientboundSetTitlesAnimationPacket(fadeIn, stay, fadeOut); + sendPackets(List.of(titlePacket, subtitlePacket, timePacket), false); + } + + @Override + public void sendMessage(Component text, boolean overlay) { + Object packet = FastNMS.INSTANCE.constructor$ClientboundSystemChatPacket(ComponentUtils.adventureToMinecraft(text), overlay); + sendPacket(packet, false); } @Override @@ -334,6 +342,9 @@ public class BukkitServerPlayer extends Player { } else { this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick(); } + if (this.gameTicks % 30 == 0) { + this.updateGUI(); + } if (this.isDestroyingBlock) { this.tickBlockDestroy(); } @@ -350,6 +361,13 @@ public class BukkitServerPlayer extends Player { } } + private void updateGUI() { + org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(platformPlayer()) : platformPlayer().getOpenInventory().getTopInventory(); + if (top.getHolder() instanceof CraftEngineInventoryHolder holder) { + holder.gui().onTimer(); + } + } + @Override public float getDestroyProgress(Object blockState, BlockPos pos) { return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos)); @@ -653,12 +671,12 @@ public class BukkitServerPlayer extends Player { @Override public float getYRot() { - return platformPlayer().getLocation().getPitch(); + return platformPlayer().getPitch(); } @Override public float getXRot() { - return platformPlayer().getLocation().getYaw(); + return platformPlayer().getYaw(); } @Override @@ -685,17 +703,17 @@ public class BukkitServerPlayer extends Player { @Override public double x() { - return platformPlayer().getLocation().getX(); + return platformPlayer().getX(); } @Override public double y() { - return platformPlayer().getLocation().getY(); + return platformPlayer().getY(); } @Override public double z() { - return platformPlayer().getLocation().getZ(); + return platformPlayer().getZ(); } @Override @@ -716,12 +734,7 @@ public class BukkitServerPlayer extends Player { } @Override - public Map> furnitureView() { - return this.furnitureView; - } - - @Override - public Map entityView() { + public Map entityPacketHandlers() { return this.entityTypeView; } @@ -787,7 +800,6 @@ public class BukkitServerPlayer extends Player { @Override public void clearView() { this.entityTypeView.clear(); - this.furnitureView.clear(); } @Override @@ -816,4 +828,9 @@ public class BukkitServerPlayer extends Player { return LegacyAttributeUtils.getLuck(platformPlayer()); } } + + @Override + public boolean isFlying() { + return platformPlayer().isFlying(); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 515fdfc63..81163c601 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -101,8 +101,11 @@ public class BlockStateUtils { } public static Key getBlockOwnerId(Block block) { - BlockData data = block.getBlockData(); - Object blockState = blockDataToBlockState(data); + return getBlockOwnerId(block.getBlockData()); + } + + public static Key getBlockOwnerId(BlockData block) { + Object blockState = blockDataToBlockState(block); return getBlockOwnerIdFromState(blockState); } @@ -188,8 +191,12 @@ public class BlockStateUtils { Reflections.field$BlockStateBase$replaceable.set(state, replaceable); } - public static boolean isReplaceable(Object state) throws ReflectiveOperationException { - return (boolean) Reflections.field$BlockStateBase$replaceable.get(state); + public static boolean isReplaceable(Object state) { + try { + return (boolean) Reflections.field$BlockStateBase$replaceable.get(state); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to get replaceable property", e); + } } public static void setCanOcclude(Object state, boolean canOcclude) throws ReflectiveOperationException { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java index ac0bf989c..7152b3f98 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java @@ -273,9 +273,10 @@ public class InteractUtils { } } - public static boolean isInteractable(Key block, org.bukkit.entity.Player player, BlockData state, BlockHitResult hit, Item item) { - if (INTERACTIONS.containsKey(block)) { - return INTERACTIONS.get(block).apply(player, item, state, hit); + public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, Item item) { + Key blockType = BlockStateUtils.getBlockOwnerId(state); + if (INTERACTIONS.containsKey(blockType)) { + return INTERACTIONS.get(blockType).apply(player, item, state, hit); } else { return false; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/KeyUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/KeyUtils.java index 12d203b76..eabcb257d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/KeyUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/KeyUtils.java @@ -23,4 +23,8 @@ public class KeyUtils { public static Object toResourceLocation(Key key) { return toResourceLocation(key.namespace(), key.value()); } + + public static NamespacedKey toNamespacedKey(Key key) { + return new NamespacedKey(key.namespace(), key.value()); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java index 7d84c1a83..a7ec85455 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java @@ -23,16 +23,11 @@ public class LightUtils { List players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder); if (players.isEmpty()) continue; Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder); - BitSet blockChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$blockChangedLightSectionFilter.get(chunkHolder); - blockChangedLightSectionFilter.or(entry.getValue()); - BitSet skyChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$skyChangedLightSectionFilter.get(chunkHolder); Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); - Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, skyChangedLightSectionFilter, blockChangedLightSectionFilter); + Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, entry.getValue(), entry.getValue()); for (Object player : players) { FastNMS.INSTANCE.sendPacket(player, lightPacket); } - blockChangedLightSectionFilter.clear(); - skyChangedLightSectionFilter.clear(); } } catch (Exception e) { CraftEngine.instance().logger().warn("Could not update light for world " + world.getName()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java index 01c719118..bb3d7feec 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java @@ -1,15 +1,26 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Location; +import org.bukkit.World; import org.jetbrains.annotations.NotNull; public class LocationUtils { private LocationUtils() {} + public static Location toLocation(WorldPosition position) { + return new Location((World) position.world().platformWorld(), position.x(), position.y(), position.z(), position.xRot(), position.yRot()); + } + + public static WorldPosition toWorldPosition(Location location) { + return new WorldPosition(new BukkitWorld(location.getWorld()), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } + public static Vec3d toVec3d(Location loc) { return new Vec3d(loc.getX(), loc.getY(), loc.getZ()); } @@ -27,11 +38,7 @@ public class LocationUtils { } public static Object above(Object blockPos) throws ReflectiveOperationException { - return toBlockPos( - FastNMS.INSTANCE.field$Vec3i$x(blockPos), - FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, - FastNMS.INSTANCE.field$Vec3i$z(blockPos) - ); + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); } public static Object toBlockPos(int x, int y, int z) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java index 6e847f6f9..dc8c5aeba 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -3,7 +3,8 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Particle; -public class ParticleUtils { +public final class ParticleUtils { + private ParticleUtils() {} public static Particle getParticle(String particle) { try { @@ -12,8 +13,12 @@ public class ParticleUtils { return switch (particle) { case "REDSTONE" -> Particle.valueOf("DUST"); case "VILLAGER_HAPPY", "HAPPY_VILLAGER" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "HAPPY_VILLAGER" : "VILLAGER_HAPPY"); + case "BUBBLE", "WATER_BUBBLE" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "BUBBLE" : "WATER_BUBBLE"); default -> Particle.valueOf(particle); }; } } + + public static final Particle HAPPY_VILLAGER = getParticle("HAPPY_VILLAGER"); + public static final Particle BUBBLE = getParticle("BUBBLE"); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 3ffed4d5b..a8a39911e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -27,6 +27,7 @@ import sun.misc.Unsafe; import java.io.BufferedReader; import java.lang.invoke.VarHandle; import java.lang.reflect.*; +import java.net.URLClassLoader; import java.time.Instant; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -558,7 +559,6 @@ public class Reflections { ) ); - @Deprecated public static final Field field$ClientboundAddEntityPacket$type = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundAddEntityPacket, clazz$EntityType, 0 @@ -3368,11 +3368,23 @@ public class Reflections { ); public static final Object instance$SoundEvent$EMPTY; + public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_1; + public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_2; + public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_3; + public static final Object instance$SoundEvent$TRIDENT_THROW; static { try { - Object key = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "intentionally_empty"); - instance$SoundEvent$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, key); + Object intentionallyEmpty = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "intentionally_empty"); + instance$SoundEvent$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, intentionallyEmpty); + Object tridentRiptide1 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident_riptide_1"); + instance$SoundEvent$TRIDENT_RIPTIDE_1 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide1); + Object tridentRiptide2 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident_riptide_2"); + instance$SoundEvent$TRIDENT_RIPTIDE_2 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide2); + Object tridentRiptide3 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident.riptide_3"); + instance$SoundEvent$TRIDENT_RIPTIDE_3 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide3); + Object tridentThrow = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident.throw"); + instance$SoundEvent$TRIDENT_THROW = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentThrow); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -3810,6 +3822,8 @@ public class Reflections { public static final Object instance$EntityType$INTERACTION; public static final Object instance$EntityType$SHULKER; public static final Object instance$EntityType$OAK_BOAT; + public static final Object instance$EntityType$TRIDENT; + public static final Object instance$EntityType$SNOWBALL; static { try { @@ -3829,6 +3843,10 @@ public class Reflections { instance$EntityType$ARMOR_STAND = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, armorStand); Object oakBoat = VersionHelper.isOrAbove1_21_2() ? FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "oak_boat") : FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "boat"); instance$EntityType$OAK_BOAT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, oakBoat); + Object trident = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "trident"); + instance$EntityType$TRIDENT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, trident); + Object snowball = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "snowball"); + instance$EntityType$SNOWBALL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, snowball); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -3979,7 +3997,7 @@ public class Reflections { ) ); - // 1.21.3+ + // 1.21.2+ public static final Class clazz$ClientboundEntityPositionSyncPacket = ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundEntityPositionSyncPacket") @@ -5264,6 +5282,13 @@ public class Reflections { ) ); + public static final Class clazz$ArmorDyeRecipe = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.crafting.RecipeArmorDye", + "world.item.crafting.ArmorDyeRecipe" + ) + ); + public static final Field field$CraftComplexRecipe$recipe = requireNonNull( ReflectionUtils.getDeclaredField( clazz$CraftComplexRecipe, clazz$CustomRecipe, 0 @@ -6098,6 +6123,10 @@ public class Reflections { ) ); + public static final Constructor constructor$ClientboundMoveEntityPacket$PosRot = requireNonNull( + ReflectionUtils.getTheOnlyConstructor(clazz$ClientboundMoveEntityPacket$PosRot) + ); + public static final Class clazz$ClientboundRotateHeadPacket = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "network.protocol.game.PacketPlayOutEntityHeadRotation", @@ -6395,14 +6424,20 @@ public class Reflections { ); public static final int instance$EntityType$BLOCK_DISPLAY$registryId; + public static final int instance$EntityType$ITEM_DISPLAY$registryId; public static final int instance$EntityType$TEXT_DISPLAY$registryId; public static final int instance$EntityType$FALLING_BLOCK$registryId; + public static final int instance$EntityType$TRIDENT$registryId; + public static final int instance$EntityType$ARMOR_STAND$registryId; static { try { instance$EntityType$BLOCK_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$BLOCK_DISPLAY); + instance$EntityType$ITEM_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ITEM_DISPLAY); instance$EntityType$TEXT_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TEXT_DISPLAY); instance$EntityType$FALLING_BLOCK$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$FALLING_BLOCK); + instance$EntityType$TRIDENT$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TRIDENT); + instance$EntityType$ARMOR_STAND$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ARMOR_STAND); } catch (Exception e) { throw new RuntimeException(e); } @@ -6618,4 +6653,143 @@ public class Reflections { BukkitReflectionUtils.assembleCBClass("block.CraftBlockStates$BlockEntityStateFactory") ) ); + + public static final Class clazz$ServerEntity = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "server.level.EntityTrackerEntry", + "server.level.ServerEntity") + ); + + public static final Field field$ServerEntity$updateInterval = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ServerEntity, int.class, 0 + ) + ); + + public static final Class clazz$AbstractArrow = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.projectile.EntityArrow", + "world.entity.projectile.AbstractArrow" + ) + ); + + public static final Class clazz$ClientboundCustomPayloadPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + List.of("network.protocol.game.PacketPlayOutCustomPayload", "network.protocol.common.ClientboundCustomPayloadPacket"), + List.of("network.protocol.game.ClientboundCustomPayloadPacket", "network.protocol.common.ClientboundCustomPayloadPacket") + ) + ); + + public static final Constructor constructor$ClientboundCustomPayloadPacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0) + ); + + // 1.20.2+ + public static final Constructor constructor$DiscardedPayload = Optional.ofNullable(clazz$DiscardedPayload) + .map(clazz -> ReflectionUtils.getTheOnlyConstructor(clazz)) + .orElse(null); + + public static final Class clazz$PaperPluginClassLoader = ReflectionUtils.getClazz( + "io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader" + ); + + public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) + .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) + .orElse(null); + + public static final Method method$SoundSource$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$SoundSource, clazz$SoundSource.arrayType() + ) + ); + + public static final Object instance$SoundSource$MASTER; + public static final Object instance$SoundSource$MUSIC; + public static final Object instance$SoundSource$RECORDS; + public static final Object instance$SoundSource$WEATHER; + public static final Object instance$SoundSource$BLOCKS; + public static final Object instance$SoundSource$HOSTILE; + public static final Object instance$SoundSource$NEUTRAL; + public static final Object instance$SoundSource$PLAYERS; + public static final Object instance$SoundSource$AMBIENT; + public static final Object instance$SoundSource$VOICE; + + static { + try { + Object[] values = (Object[]) method$SoundSource$values.invoke(null); + instance$SoundSource$MASTER = values[0]; + instance$SoundSource$MUSIC = values[1]; + instance$SoundSource$RECORDS = values[2]; + instance$SoundSource$WEATHER = values[3]; + instance$SoundSource$BLOCKS = values[4]; + instance$SoundSource$HOSTILE = values[5]; + instance$SoundSource$NEUTRAL = values[6]; + instance$SoundSource$PLAYERS = values[7]; + instance$SoundSource$AMBIENT = values[8]; + instance$SoundSource$VOICE = values[9]; + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Class clazz$MoverType = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.EnumMoveType", + "world.entity.MoverType" + ) + ); + + public static final Method method$MoverType$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$MoverType, clazz$MoverType.arrayType() + ) + ); + + public static final Object instance$MoverType$SELF; + public static final Object instance$MoverType$PLAYER; + public static final Object instance$MoverType$PISTON; + public static final Object instance$MoverType$SHULKER_BOX; + public static final Object instance$MoverType$SHULKER; + + static { + try { + Object[] values = (Object[]) method$MoverType$values.invoke(null); + instance$MoverType$SELF = values[0]; + instance$MoverType$PLAYER = values[1]; + instance$MoverType$PISTON = values[2]; + instance$MoverType$SHULKER_BOX = values[3]; + instance$MoverType$SHULKER = values[4]; + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Class clazz$AbstractArrow$Pickup = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.projectile.EntityArrow$PickupStatus", + "world.entity.projectile.AbstractArrow$Pickup" + ) + ); + + public static final Method method$AbstractArrow$Pickup$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$AbstractArrow$Pickup, clazz$AbstractArrow$Pickup.arrayType() + ) + ); + + public static final Object instance$AbstractArrow$Pickup$DISALLOWED; + public static final Object instance$AbstractArrow$Pickup$ALLOWED; + public static final Object instance$AbstractArrow$Pickup$CREATIVE_ONLY; + + static { + try { + Object[] values = (Object[]) method$AbstractArrow$Pickup$values.invoke(null); + instance$AbstractArrow$Pickup$DISALLOWED = values[0]; + instance$AbstractArrow$Pickup$ALLOWED = values[1]; + instance$AbstractArrow$Pickup$CREATIVE_ONLY = values[2]; + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/TagUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/TagUtils.java new file mode 100644 index 000000000..28e6e971e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/TagUtils.java @@ -0,0 +1,86 @@ +package net.momirealms.craftengine.bukkit.util; + +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class TagUtils { + + private TagUtils() {} + + /** + * 构建模拟标签更新数据包(用于向客户端添加虚拟标签) + * + * @param tags 需要添加的标签数据,结构为嵌套映射: + *
{@code
+     *               Map结构示例:
+     *               {
+     *                 注册表键1 (如BuiltInRegistries.ITEM.key) -> {
+     *                   "命名空间:值1" -> IntList.of(1, 2, 3),  // 该命名空间下生效的物品ID列表
+     *                   "命名空间:值2" -> IntList.of(5, 7)
+     *                 },
+     *                 注册表键2 (如BuiltInRegistries.BLOCK.key) -> {
+     *                   "minecraft:beacon_base_blocks" -> IntList.of(1024, 2048)
+     *                 },
+     *                 ....
+     *               }
+     *               }
+ * 其中:
+ * - 外层键:注册表ResourceKey
+ * - 中间层键:标签的命名空间:值(字符串)
+ * - 值:包含注册表内项目数字ID的IntList + * + * @return 可发送给客户端的 ClientboundUpdateTagsPacket 数据包对象 + */ + @SuppressWarnings("unchecked") + public static Object createUpdateTagsPacket(Map> tags) { + Map registriesNetworkPayload = (Map) FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork(); + Map modified = new HashMap<>(); + for (Map.Entry> entry : tags.entrySet()) { + Object existingPayload = registriesNetworkPayload.get(entry.getKey()); + if (existingPayload == null) continue; + FriendlyByteBuf deserializeBuf = new FriendlyByteBuf(Unpooled.buffer()); + FastNMS.INSTANCE.method$TagNetworkSerialization$NetworkPayload$write(existingPayload, deserializeBuf); + Map originalTags = deserializeBuf.readMap( + FriendlyByteBuf::readUtf, + FriendlyByteBuf::readIntIdList + ); + Map> reversedTags = new HashMap<>(); + for (Map.Entry tagEntry : originalTags.entrySet()) { + for (int id : tagEntry.getValue()) { + reversedTags.computeIfAbsent(id, k -> new ArrayList<>()).add(tagEntry.getKey()); + } + } + for (TagEntry tagEntry : entry.getValue()) { + reversedTags.remove(tagEntry.id); + for (String tag : tagEntry.tags) { + reversedTags.computeIfAbsent(tagEntry.id, k -> new ArrayList<>()).add(tag); + } + } + Map processedTags = new HashMap<>(); + for (Map.Entry> tagEntry : reversedTags.entrySet()) { + for (String tag : tagEntry.getValue()) { + processedTags.computeIfAbsent(tag, k -> new IntArrayList()).addLast(tagEntry.getKey()); + } + } + FriendlyByteBuf serializeBuf = new FriendlyByteBuf(Unpooled.buffer()); + serializeBuf.writeMap(processedTags, + FriendlyByteBuf::writeUtf, + FriendlyByteBuf::writeIntIdList + ); + Object mergedPayload = FastNMS.INSTANCE.method$TagNetworkSerialization$NetworkPayload$read(serializeBuf); + modified.put(entry.getKey(), mergedPayload); + } + return FastNMS.INSTANCE.constructor$ClientboundUpdateTagsPacket(modified); + } + + public record TagEntry(int id, List tags) { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java index a7dc16a3d..67c8e080c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java @@ -6,9 +6,9 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; 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.item.CustomItem; import net.momirealms.craftengine.core.item.Item; @@ -86,21 +86,17 @@ public class BukkitBlockInWorld implements BlockInWorld { } @Override - public String getAsString() { - ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(this.block); - if (state != null) { - return state.toString(); - } - return this.block.getBlockData().getAsString(); + public ImmutableBlockState customBlockState() { + return CraftEngineBlocks.getCustomBlockState(this.block); } @Override - public Key owner() { + public CustomBlock customBlock() { ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(this.block); if (state != null) { - return state.owner().value().id(); + return state.owner().value(); } - return KeyUtils.namespacedKey2Key(this.block.getType().getKey()); + return null; } public Block block() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java index b9070e694..aadb65e6f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java @@ -4,10 +4,13 @@ import net.momirealms.craftengine.bukkit.util.LightUtils; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.SectionPosUtils; import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.SectionPos; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; +import java.util.HashSet; + public class BukkitCEWorld extends CEWorld { public BukkitCEWorld(World world, StorageAdaptor adaptor) { @@ -20,9 +23,19 @@ public class BukkitCEWorld extends CEWorld { @Override public void tick() { + HashSet poses; + synchronized (super.updatedSectionSet) { + poses = new HashSet<>(super.updatedSectionSet); + super.updatedSectionSet.clear(); + } if (Config.enableLightSystem()) { - LightUtils.updateChunkLight((org.bukkit.World) world.platformWorld(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1)); - super.updatedSectionPositions.clear(); + LightUtils.updateChunkLight( + (org.bukkit.World) world.platformWorld(), + SectionPosUtils.toMap(poses, + world.worldHeight().getMinSection() - 1, + world.worldHeight().getMaxSection() + 1 + ) + ); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java index 5223b9bd4..1554d3d79 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockInWorld; -import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.Position; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldHeight; import org.bukkit.Location; @@ -35,11 +35,7 @@ public class BukkitWorld implements World { @Override public Object serverWorld() { - try { - return FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformWorld()); - } catch (Exception e) { - throw new RuntimeException("Failed to get server world", e); - } + return FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformWorld()); } @Override @@ -71,7 +67,7 @@ public class BukkitWorld implements World { } @Override - public void dropItemNaturally(Vec3d location, Item item) { + public void dropItemNaturally(Position location, Item item) { ItemStack itemStack = (ItemStack) item.load(); if (ItemUtils.isEmpty(itemStack)) return; if (VersionHelper.isOrAbove1_21_2()) { @@ -82,7 +78,7 @@ public class BukkitWorld implements World { } @Override - public void dropExp(Vec3d location, int amount) { + public void dropExp(Position location, int amount) { if (amount <= 0) return; EntityUtils.spawnEntity(platformWorld(), new Location(platformWorld(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> { ExperienceOrb orb = (ExperienceOrb) e; @@ -91,7 +87,12 @@ public class BukkitWorld implements World { } @Override - public void playBlockSound(Vec3d location, Key sound, float volume, float pitch) { + public void playBlockSound(Position location, Key sound, float volume, float pitch) { platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch); } + + @Override + public long time() { + return platformWorld().getTime(); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index d527a0ab7..afa8d1159 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; public class BukkitWorldManager implements WorldManager, Listener { @@ -45,6 +46,7 @@ public class BukkitWorldManager implements WorldManager, Listener { private UUID lastVisitedUUID; private CEWorld lastVisitedWorld; private StorageAdaptor storageAdaptor; + private boolean isTicking = false; public BukkitWorldManager(BukkitCraftEngine plugin) { instance = this; @@ -90,12 +92,20 @@ public class BukkitWorldManager implements WorldManager, Listener { public void delayedInit() { // events and tasks - Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); - this.tickTask = plugin.scheduler().sync().runRepeating(() -> { - for (CEWorld world : worldArray) { - world.tick(); + Bukkit.getPluginManager().registerEvents(this, this.plugin.bootstrap()); + this.tickTask = this.plugin.scheduler().asyncRepeating(() -> { + try { + if (this.isTicking) { + return; + } + this.isTicking = true; + for (CEWorld world : this.worldArray) { + world.tick(); + } + } finally { + this.isTicking = false; } - }, 1, 1); + }, 50, 50, TimeUnit.MILLISECONDS); // load loaded chunks this.worldMapLock.writeLock().lock(); try { @@ -125,7 +135,6 @@ public class BukkitWorldManager implements WorldManager, Listener { if (this.tickTask != null && !this.tickTask.cancelled()) { this.tickTask.cancel(); } - for (World world : Bukkit.getWorlds()) { CEWorld ceWorld = getWorld(world.getUID()); for (Chunk chunk : world.getLoadedChunks()) { @@ -299,7 +308,7 @@ public class BukkitWorldManager implements WorldManager, Listener { } } } - if (unsaved && !FastNMS.INSTANCE.method$LevelChunk$isUnsaved(levelChunk)) { + if (unsaved /*&& !FastNMS.INSTANCE.method$LevelChunk$isUnsaved(levelChunk)*/) { FastNMS.INSTANCE.method$LevelChunk$markUnsaved(levelChunk); } ceChunk.unload(); diff --git a/client-mod/build.gradle.kts b/client-mod/build.gradle.kts index df2efc2e3..083cc7ad4 100644 --- a/client-mod/build.gradle.kts +++ b/client-mod/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("fabric-loom") version "1.10-SNAPSHOT" - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" } version = property("project_version")!! diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java index d3bdbf562..ed32e3bda 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java @@ -1,9 +1,13 @@ package net.momirealms.craftengine.fabric.client; +import io.netty.buffer.Unpooled; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.minecraft.block.Block; @@ -11,17 +15,19 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.color.world.BiomeColors; import net.minecraft.client.network.ClientConfigurationNetworkHandler; import net.minecraft.client.render.RenderLayer; +import net.minecraft.network.PacketByteBuf; import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import net.minecraft.world.biome.FoliageColors; import net.momirealms.craftengine.fabric.client.blocks.CustomBlock; import net.momirealms.craftengine.fabric.client.config.ModConfig; import net.momirealms.craftengine.fabric.client.network.CraftEnginePayload; +import net.momirealms.craftengine.fabric.client.util.NetWorkDataTypes; -import java.nio.charset.StandardCharsets; - +@Environment(EnvType.CLIENT) public class CraftEngineFabricModClient implements ClientModInitializer { public static final String MOD_ID = "craftengine"; + public static boolean serverInstalled = false; @Override public void onInitializeClient() { @@ -29,6 +35,8 @@ public class CraftEngineFabricModClient implements ClientModInitializer { PayloadTypeRegistry.configurationC2S().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC); registerRenderLayer(); ClientConfigurationConnectionEvents.START.register(CraftEngineFabricModClient::initChannel); + ClientConfigurationNetworking.registerGlobalReceiver(CraftEnginePayload.ID, CraftEngineFabricModClient::handleReceiver); + ClientPlayConnectionEvents.DISCONNECT.register((client, handler) -> serverInstalled = false); } public static void registerRenderLayer() { @@ -58,14 +66,32 @@ public class CraftEngineFabricModClient implements ClientModInitializer { } private static void initChannel(ClientConfigurationNetworkHandler handler, MinecraftClient client) { - if (ModConfig.enableNetwork) { - registerChannel(handler); - } else { + if (!ModConfig.enableNetwork && !ModConfig.enableCancelBlockUpdate) { ClientConfigurationNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID); + return; } + + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + + if (ModConfig.enableNetwork) { + NetWorkDataTypes type = NetWorkDataTypes.CLIENT_CUSTOM_BLOCK; + type.writeType(buf); + type.encode(buf, Block.STATE_IDS.size()); + } else if (ModConfig.enableCancelBlockUpdate) { + NetWorkDataTypes type = NetWorkDataTypes.CANCEL_BLOCK_UPDATE; + type.writeType(buf); + type.encode(buf, true); + } + + ClientConfigurationNetworking.send(new CraftEnginePayload(buf.array())); } - private static void registerChannel(ClientConfigurationNetworkHandler handler) { - ClientConfigurationNetworking.send(new CraftEnginePayload((":" + Block.STATE_IDS.size() + ":init").getBytes(StandardCharsets.UTF_8))); + private static void handleReceiver(CraftEnginePayload payload, ClientConfigurationNetworking.Context context) { + byte[] data = payload.data(); + PacketByteBuf buf = new PacketByteBuf(Unpooled.wrappedBuffer(data)); + NetWorkDataTypes type = NetWorkDataTypes.readType(buf); + if (type == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { + serverInstalled = type.as(Boolean.class).decode(buf); + } } } diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/config/ModConfig.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/config/ModConfig.java index 6546896d6..dc6689b15 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/config/ModConfig.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/config/ModConfig.java @@ -3,33 +3,69 @@ package net.momirealms.craftengine.fabric.client.config; import me.shedaniel.clothconfig2.api.ConfigBuilder; import me.shedaniel.clothconfig2.api.ConfigCategory; import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; public class ModConfig { - public static boolean enableNetwork = true; + private static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve("craft-engine-fabric-mod/config.yml"); + public static boolean enableNetwork = false; + public static boolean enableCancelBlockUpdate = false; public static Screen getConfigScreen(Screen parent) { ConfigBuilder builder = ConfigBuilder.create() .setParentScreen(parent) + .setSavingRunnable(ModConfig::saveConfig) .setTitle(Text.translatable("title.craftengine.config")); ConfigCategory general = builder.getOrCreateCategory(Text.translatable("category.craftengine.general")); ConfigEntryBuilder entryBuilder = builder.entryBuilder(); general.addEntry(entryBuilder.startBooleanToggle( - Text.translatable("option.craftengine.enable_network") - .formatted(Formatting.WHITE), + Text.translatable("option.craftengine.enable_network") + .formatted(Formatting.WHITE), enableNetwork) - .setDefaultValue(true) + .setDefaultValue(false) .setSaveConsumer(newValue -> enableNetwork = newValue) .setTooltip( Text.translatable("tooltip.craftengine.enable_network") .formatted(Formatting.GRAY) ) .build()); + general.addEntry(entryBuilder.startBooleanToggle( + Text.translatable("option.craftengine.enable_cancel_block_update") + .formatted(Formatting.WHITE), + enableCancelBlockUpdate) + .setDefaultValue(false) + .setSaveConsumer(newValue -> enableCancelBlockUpdate = newValue) + .setTooltip( + Text.translatable("tooltip.craftengine.enable_cancel_block_update") + ) + .build() + ); return builder.build(); } + + private static void saveConfig() { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(options); + var data = new java.util.HashMap(); + data.put("enable-network", ModConfig.enableNetwork); + data.put("enable-cancel-block-update", ModConfig.enableCancelBlockUpdate); + try (Writer writer = Files.newBufferedWriter(CONFIG_PATH)) { + yaml.dump(data, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java index c975c1f7f..f55495fea 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java @@ -9,8 +9,13 @@ public record CraftEnginePayload(byte[] data) implements CustomPayload { public static final Identifier CRAFTENGINE_PAYLOAD = Identifier.of("craftengine", "payload"); public static final Id ID = new Id<>(CraftEnginePayload.CRAFTENGINE_PAYLOAD); public static final PacketCodec CODEC = PacketCodec.of( - (payload, byteBuf) -> byteBuf.writeByteArray(payload.data()), - buf -> new CraftEnginePayload(buf.readByteArray())); + (payload, byteBuf) -> byteBuf.writeBytes(payload.data()), + buf -> { + int i = buf.readableBytes(); + byte[] data = new byte[i]; + buf.readBytes(data); + return new CraftEnginePayload(data); + }); @Override public Id getId() { diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java new file mode 100644 index 000000000..63c8bf8be --- /dev/null +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java @@ -0,0 +1,63 @@ +package net.momirealms.craftengine.fabric.client.util; + +import net.minecraft.network.PacketByteBuf; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class NetWorkDataTypes { + private static final Map> id2NetWorkDataTypes = new HashMap<>(); + + public static final NetWorkDataTypes CLIENT_CUSTOM_BLOCK = + new NetWorkDataTypes<>(0, PacketByteBuf::readInt, PacketByteBuf::writeInt); + + public static final NetWorkDataTypes CANCEL_BLOCK_UPDATE = + new NetWorkDataTypes<>(1, PacketByteBuf::readBoolean, PacketByteBuf::writeBoolean); + + static { + register(CLIENT_CUSTOM_BLOCK); + register(CANCEL_BLOCK_UPDATE); + } + + private static void register(NetWorkDataTypes type) { + id2NetWorkDataTypes.put(type.id, type); + } + + private final int id; + private final Function decoder; + private final BiConsumer encoder; + + public NetWorkDataTypes(int id, Function decoder, BiConsumer encoder) { + this.id = id; + this.decoder = decoder; + this.encoder = encoder; + } + + public T decode(PacketByteBuf buf) { + return decoder.apply(buf); + } + + public void encode(PacketByteBuf buf, T data) { + encoder.accept(buf, data); + } + + public int id() { + return id; + } + + public void writeType(PacketByteBuf buf) { + buf.writeVarInt(id); + } + + public static NetWorkDataTypes readType(PacketByteBuf buf) { + int id = buf.readVarInt(); + return id2NetWorkDataTypes.get(id); + } + + @SuppressWarnings("unchecked") + public NetWorkDataTypes as(Class clazz) { + return (NetWorkDataTypes) this; + } +} \ No newline at end of file diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java index 90a2b995d..94abd5b8a 100644 --- a/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.fabric; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.BlockState; @@ -9,16 +11,15 @@ import net.momirealms.craftengine.fabric.util.BlockUtils; import net.momirealms.craftengine.fabric.util.LoggerFilter; import net.momirealms.craftengine.fabric.util.RegisterBlocks; import net.momirealms.craftengine.fabric.util.YamlUtils; -import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.IOException; import java.io.InputStream; -import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +@Environment(EnvType.CLIENT) public class CraftEngineFabricMod implements ModInitializer { private static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve("craft-engine-fabric-mod/config.yml"); public static final String MOD_ID = "craftengine"; @@ -47,32 +48,25 @@ public class CraftEngineFabricMod implements ModInitializer { } catch (IOException e) { e.printStackTrace(); } - Runtime.getRuntime().addShutdownHook(new Thread(this::saveConfig)); } @SuppressWarnings("unchecked") private void loadConfig() { if (!Files.exists(CONFIG_PATH)) { - ModConfig.enableNetwork = true; + ModConfig.enableNetwork = false; + ModConfig.enableCancelBlockUpdate = false; return; } try (InputStream inputStream = Files.newInputStream(CONFIG_PATH)) { Yaml yaml = new Yaml(); var config = yaml.loadAs(inputStream, java.util.Map.class); - ModConfig.enableNetwork = (Boolean) config.getOrDefault("enable-network", true); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void saveConfig() { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - Yaml yaml = new Yaml(options); - var data = new java.util.HashMap(); - data.put("enable-network", ModConfig.enableNetwork); - try (Writer writer = Files.newBufferedWriter(CONFIG_PATH)) { - yaml.dump(data, writer); + if (config == null) { + ModConfig.enableNetwork = false; + ModConfig.enableCancelBlockUpdate = false; + return; + } + ModConfig.enableNetwork = (Boolean) config.getOrDefault("enable-network", false); + ModConfig.enableCancelBlockUpdate = (Boolean) config.getOrDefault("enable-cancel-block-update", false); } catch (IOException e) { e.printStackTrace(); } diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractBlockStateMixin.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractBlockStateMixin.java new file mode 100644 index 000000000..014869e0f --- /dev/null +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractBlockStateMixin.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.fabric.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.WorldView; +import net.minecraft.world.block.WireOrientation; +import net.minecraft.world.tick.ScheduledTickView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static net.momirealms.craftengine.fabric.client.CraftEngineFabricModClient.serverInstalled; +import static net.momirealms.craftengine.fabric.client.config.ModConfig.enableCancelBlockUpdate; + +@Environment(EnvType.CLIENT) +@Mixin(AbstractBlock.AbstractBlockState.class) +public abstract class AbstractBlockStateMixin { + + @Inject(method = "getStateForNeighborUpdate", at = @At("HEAD"), cancellable = true) + private void cancelGetStateForNeighborUpdate(WorldView world, ScheduledTickView tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, Random random, CallbackInfoReturnable cir) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + cir.setReturnValue(this); + } + + @Inject(method = "neighborUpdate", at = @At("HEAD"), cancellable = true) + private void cancelNeighborUpdate(World world, BlockPos pos, Block sourceBlock, WireOrientation wireOrientation, boolean notify, CallbackInfo ci) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + ci.cancel(); + } + + @Inject(method = "updateNeighbors*", at = @At("HEAD"), cancellable = true) + private void cancelUpdateNeighbors(WorldAccess world, BlockPos pos, int flags, CallbackInfo ci) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + ci.cancel(); + } + + @Inject(method = "scheduledTick", at = @At("HEAD"), cancellable = true) + private void cancelScheduledTick(ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + ci.cancel(); + } + + @Inject(method = "canPlaceAt", at = @At("HEAD"), cancellable = true) + private void passCanPlaceAt(WorldView world, BlockPos pos, CallbackInfoReturnable cir) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + cir.setReturnValue(true); + } + + @Inject(method = "randomTick", at = @At("HEAD"), cancellable = true) + private void cancelRandomTick(ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + ci.cancel(); + } +} diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractRailBlockMixin.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractRailBlockMixin.java new file mode 100644 index 000000000..1699baa52 --- /dev/null +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/AbstractRailBlockMixin.java @@ -0,0 +1,26 @@ +package net.momirealms.craftengine.fabric.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.AbstractRailBlock; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static net.momirealms.craftengine.fabric.client.CraftEngineFabricModClient.serverInstalled; +import static net.momirealms.craftengine.fabric.client.config.ModConfig.enableCancelBlockUpdate; + +@Environment(EnvType.CLIENT) +@Mixin(AbstractRailBlock.class) +public abstract class AbstractRailBlockMixin { + + @Inject(method = "updateCurves", at = @At("HEAD"), cancellable = true) + private void cancelUpdateCurves(BlockState state, World world, BlockPos pos, boolean notify, CallbackInfoReturnable cir) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + cir.setReturnValue(state); + } +} diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/BlockItemMixin.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/BlockItemMixin.java new file mode 100644 index 000000000..fe3d3a6a8 --- /dev/null +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/BlockItemMixin.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.fabric.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.NbtComponent; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Environment(EnvType.CLIENT) +@Mixin(BlockItem.class) +public class BlockItemMixin { + + @Inject(method = "place*", at = @At("HEAD"), cancellable = true) + private void onPlace(ItemPlacementContext context, CallbackInfoReturnable cir) { + ItemStack stack = context.getStack(); + NbtComponent customData = stack.getComponents().get(DataComponentTypes.CUSTOM_DATA); + if (customData == null) return; + if (customData.contains("craftengine:id")) { + cir.setReturnValue(ActionResult.FAIL); + cir.cancel(); + } + } +} diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/FluidStateMixin.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/FluidStateMixin.java new file mode 100644 index 000000000..695c89085 --- /dev/null +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/mixin/FluidStateMixin.java @@ -0,0 +1,26 @@ +package net.momirealms.craftengine.fabric.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.BlockState; +import net.minecraft.fluid.FluidState; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static net.momirealms.craftengine.fabric.client.CraftEngineFabricModClient.serverInstalled; +import static net.momirealms.craftengine.fabric.client.config.ModConfig.enableCancelBlockUpdate; + +@Environment(EnvType.CLIENT) +@Mixin(FluidState.class) +public class FluidStateMixin { + + @Inject(method = "onScheduledTick", at = @At("HEAD"), cancellable = true) + private void cancelScheduledTick(ServerWorld world, BlockPos pos, BlockState state, CallbackInfo ci) { + if (!enableCancelBlockUpdate || !serverInstalled) return; + ci.cancel(); + } +} diff --git a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/en_us.json b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/en_us.json index 711d5c3ca..2eb9b4deb 100644 --- a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/en_us.json +++ b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/en_us.json @@ -2,6 +2,8 @@ "title.craftengine.config": "CraftEngine Settings", "category.craftengine.general": "General", "option.craftengine.enable_network": "Enable custom blocks in server", + "option.craftengine.enable_cancel_block_update": "Enable cancelling block updates in the server", "tooltip.craftengine.enable_network": "Changes requires re-entering the server to take effect", + "tooltip.craftengine.enable_cancel_block_update": "Only works on servers with CraftEngine installed.", "disconnect.craftengine.block_registry_mismatch": "Block registry size mismatch. Current: %s. Expected: %s. \n 1. Make sure that the configs are the same as the server's. \n 2. Do not use any mod that might register new block. \n 3. Do not install ViaVersion." } \ No newline at end of file diff --git a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/zh_cn.json b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/zh_cn.json index c16c77c9d..e65e46887 100644 --- a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/zh_cn.json +++ b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/lang/zh_cn.json @@ -2,6 +2,8 @@ "title.craftengine.config": "CraftEngine设置", "category.craftengine.general": "通用", "option.craftengine.enable_network": "启用服务器内自定义方块", + "option.craftengine.enable_cancel_block_update": "启用服务器内取消方块更新", "tooltip.craftengine.enable_network": "需要重新进入服务器以应用更改", + "tooltip.craftengine.enable_cancel_block_update": "仅在安装了CraftEngine的服务器内生效", "disconnect.craftengine.block_registry_mismatch": "方块注册表大小不匹配. 当前: %s. 预期: %s \n 1. 确保客户端mod配置与服务端配置一致. \n 2. 不要安装任何会注册新方块的mod. \n 3. 不要使用ViaVersion." } \ No newline at end of file diff --git a/client-mod/src/main/resources/craftengine.mixins.json b/client-mod/src/main/resources/craftengine.mixins.json new file mode 100644 index 000000000..d177f9a82 --- /dev/null +++ b/client-mod/src/main/resources/craftengine.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "net.momirealms.craftengine.fabric.mixin", + "compatibilityLevel": "JAVA_21", + "client": [ + "AbstractBlockStateMixin", + "AbstractRailBlockMixin", + "BlockItemMixin", + "FluidStateMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/client-mod/src/main/resources/fabric.mod.json b/client-mod/src/main/resources/fabric.mod.json index fc6261618..785f8ed0d 100644 --- a/client-mod/src/main/resources/fabric.mod.json +++ b/client-mod/src/main/resources/fabric.mod.json @@ -24,6 +24,10 @@ "modmenu": ["net.momirealms.craftengine.fabric.client.config.ModMenuIntegration"] }, + "mixins": [ + "craftengine.mixins.json" + ], + "depends": { "fabricloader": ">=${loader_version}", "fabric": "*", diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 83e995a17..c577bb564 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" id("maven-publish") } @@ -52,12 +52,12 @@ dependencies { // Aho-Corasick java implementation compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}") // Amazon S3 - compileOnly("software.amazon.awssdk:s3:${rootProject.properties["amazon_awssdk_version"]}") { - - } + compileOnly("software.amazon.awssdk:s3:${rootProject.properties["amazon_awssdk_version"]}") compileOnly("software.amazon.awssdk:netty-nio-client:${rootProject.properties["amazon_awssdk_version"]}") // EvalEx compileOnly("com.ezylang:EvalEx:${rootProject.properties["evalex_version"]}") + // Jimfs + compileOnly("com.google.jimfs:jimfs:${rootProject.properties["jimfs_version"]}") } java { @@ -90,6 +90,8 @@ tasks { relocate("software.amazon.awssdk", "net.momirealms.craftengine.libraries.awssdk") // awssdk relocate("software.amazon.eventstream", "net.momirealms.craftengine.libraries.eventstream") // awssdk relocate("com.ezylang.evalex", "net.momirealms.craftengine.libraries.evalex") + relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") + relocate("org.apache.commons.imaging", "net.momirealms.craftengine.libraries.imaging") } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java b/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java index 0d2b8a2b0..b4a13703b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java @@ -1,9 +1,9 @@ package net.momirealms.craftengine.core.advancement; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; public interface AdvancementManager extends Manageable { - ConfigSectionParser parser(); + ConfigParser parser(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java index 125f42018..8cbeb5a5a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java @@ -4,7 +4,7 @@ import com.google.gson.JsonElement; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; import org.incendo.cloud.suggestion.Suggestion; @@ -14,7 +14,7 @@ import java.util.Optional; public interface BlockManager extends Manageable, ModelGenerator { - ConfigSectionParser parser(); + ConfigParser parser(); Collection modelsToGenerate(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java index 77645ffa1..f93bf3a57 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java @@ -2,13 +2,15 @@ package net.momirealms.craftengine.core.block; public class BlockRegistryMirror { private static PackedBlockState[] customBlockStates; + private static PackedBlockState stoneState; - public static void init(PackedBlockState[] states) { + public static void init(PackedBlockState[] states, PackedBlockState state) { customBlockStates = states; + stoneState = state; } public static PackedBlockState stateByRegistryId(int vanillaId) { - if (vanillaId < 0) return null; + if (vanillaId < 0) return stoneState; return customBlockStates[vanillaId]; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index ced0f7f7e..73c324414 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -6,6 +6,9 @@ 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.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; @@ -15,10 +18,7 @@ import net.momirealms.sparrow.nbt.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.BiFunction; public abstract class CustomBlock { @@ -29,6 +29,7 @@ public abstract class CustomBlock { protected final BlockBehavior behavior; protected final List> placements; protected final ImmutableBlockState defaultState; + protected final EnumMap>> events; @Nullable protected final LootTable lootTable; @@ -39,6 +40,7 @@ public abstract class CustomBlock { @NotNull Map appearances, @NotNull Map variantMapper, @NotNull BlockSettings settings, + @NotNull EnumMap>> events, @Nullable Map behavior, @Nullable LootTable lootTable ) { @@ -48,6 +50,7 @@ public abstract class CustomBlock { this.lootTable = lootTable; this.properties = properties; this.placements = new ArrayList<>(); + this.events = events; this.variantProvider = new BlockStateVariantProvider(holder, ImmutableBlockState::new, properties); this.defaultState = this.variantProvider.getDefaultState(); this.behavior = BlockBehaviors.fromMap(this, behavior); @@ -90,6 +93,12 @@ public abstract class CustomBlock { return lootTable; } + public void execute(PlayerOptionalContext context, EventTrigger trigger) { + for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { + function.run(context); + } + } + @NotNull public BlockStateVariantProvider variantProvider() { return variantProvider; @@ -168,11 +177,17 @@ public abstract class CustomBlock { protected BlockSettings settings; protected Map behavior; protected LootTable lootTable; + protected EnumMap>> events; protected Builder(Key id) { this.id = id; } + public Builder events(EnumMap>> events) { + this.events = events; + return this; + } + public Builder appearances(Map appearances) { this.appearances = appearances; return this; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java index 4f827823b..4e5cca009 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java @@ -1,8 +1,10 @@ package net.momirealms.craftengine.core.block; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; +import java.util.EnumMap; import java.util.Map; public class EmptyBlock extends CustomBlock { @@ -10,7 +12,7 @@ public class EmptyBlock extends CustomBlock { public static ImmutableBlockState STATE; public EmptyBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), null, null); + super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), new EnumMap<>(EventTrigger.class), null, null); INSTANCE = this; STATE = defaultState(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index 7e53699aa..772468379 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.shared.block.BlockBehavior; @@ -20,9 +20,7 @@ import java.util.List; public class ImmutableBlockState extends BlockStateHolder { private CompoundTag tag; - @Nullable private PackedBlockState customBlockState; - @Nullable private PackedBlockState vanillaBlockState; private BlockBehavior behavior; @@ -84,18 +82,18 @@ public class ImmutableBlockState extends BlockStateHolder { } public PackedBlockState customBlockState() { - return customBlockState; + return this.customBlockState; } public PackedBlockState vanillaBlockState() { - return vanillaBlockState; + return this.vanillaBlockState; } - public void setCustomBlockState(@Nullable PackedBlockState customBlockState) { + public void setCustomBlockState(@NotNull PackedBlockState customBlockState) { this.customBlockState = customBlockState; } - public void setVanillaBlockState(@Nullable PackedBlockState vanillaBlockState) { + public void setVanillaBlockState(@NotNull PackedBlockState vanillaBlockState) { this.vanillaBlockState = vanillaBlockState; } @@ -140,16 +138,12 @@ public class ImmutableBlockState extends BlockStateHolder { return state.with(property, (T) value); } - public List> getDrops(@NotNull ContextHolder.Builder builder, @NotNull World world) { - return this.getDrops(builder, world, null); - } - @SuppressWarnings("unchecked") public List> getDrops(@NotNull ContextHolder.Builder builder, @NotNull World world, @Nullable Player player) { CustomBlock block = owner.value(); if (block == null) return List.of(); LootTable lootTable = (LootTable) block.lootTable(); if (lootTable == null) return List.of(); - return lootTable.getRandomItems(builder.withParameter(CommonParameters.BLOCK_STATE, this).build(), world, player); + return lootTable.getRandomItems(builder.withParameter(DirectContextParameters.BLOCK_STATE, this).build(), world, player); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java index db164c62e..a733f2bfd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.block; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; +import java.util.EnumMap; import java.util.HashMap; import java.util.Map; @@ -12,7 +14,7 @@ public class InactiveCustomBlock extends CustomBlock { private final Map cachedData = new HashMap<>(); public InactiveCustomBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), null, null); + super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), new EnumMap<>(EventTrigger.class), null, null); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/AbstractEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/AbstractEntity.java new file mode 100644 index 000000000..c9eb5a247 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/AbstractEntity.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.world.WorldPosition; + +public abstract class AbstractEntity implements Entity { + + @Override + public WorldPosition position() { + return new WorldPosition(world(), x(), y(), z(), getXRot(), getYRot()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Billboard.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Billboard.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java index 16545019d..7533b658b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Billboard.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity; public enum Billboard { FIXED(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java index 24efd4208..15720fa5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java @@ -2,34 +2,37 @@ package net.momirealms.craftengine.core.entity; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; -public abstract class Entity { +import java.util.UUID; - public abstract Key type(); +public interface Entity { + Key type(); - public abstract double x(); + double x(); - public abstract double y(); + double y(); - public abstract double z(); + double z(); - public Vec3d position() { - return new Vec3d(x(), y(), z()); - } + WorldPosition position(); - public abstract void tick(); + void tick(); - public abstract int entityID(); + float getXRot(); - public abstract float getXRot(); + int entityID(); - public abstract float getYRot(); + float getYRot(); - public abstract World world(); + World world(); - public abstract Direction getDirection(); + Direction getDirection(); - public abstract Object literalObject(); + Object literalObject(); + + String name(); + + UUID uuid(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ItemDisplayContext.java b/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ItemDisplayContext.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java index 2fbd54078..adbead883 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ItemDisplayContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity; public enum ItemDisplayContext { NONE(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java index 426907463..18cc2fa80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.util.Key; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -12,8 +14,16 @@ public abstract class AbstractFurnitureElement implements FurnitureElement { private final Vector3f translation; private final Vector3f offset; private final Quaternionf rotation; + private final boolean applyDyedColor; - public AbstractFurnitureElement(Key item, Billboard billboard, ItemDisplayContext transform, Vector3f scale, Vector3f translation, Vector3f offset, Quaternionf rotation) { + public AbstractFurnitureElement(Key item, + Billboard billboard, + ItemDisplayContext transform, + Vector3f scale, + Vector3f translation, + Vector3f offset, + Quaternionf rotation, + boolean applyDyedColor) { this.billboard = billboard; this.transform = transform; this.scale = scale; @@ -21,6 +31,12 @@ public abstract class AbstractFurnitureElement implements FurnitureElement { this.item = item; this.rotation = rotation; this.offset = offset; + this.applyDyedColor = applyDyedColor; + } + + @Override + public boolean applyDyedColor() { + return applyDyedColor; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java index 1b8679ae6..8d2d93885 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java @@ -1,11 +1,17 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import java.util.Collections; import java.util.EnumMap; +import java.util.List; import java.util.Optional; public class CustomFurniture { @@ -13,20 +19,29 @@ public class CustomFurniture { private final FurnitureSettings settings; private final EnumMap placements; private final AnchorType anyType; + private final EnumMap>> events; @Nullable private final LootTable lootTable; public CustomFurniture(@NotNull Key id, @NotNull FurnitureSettings settings, @NotNull EnumMap placements, + @NotNull EnumMap>> events, @Nullable LootTable lootTable) { this.id = id; this.settings = settings; this.placements = placements; this.lootTable = lootTable; + this.events = events; this.anyType = placements.keySet().stream().findFirst().orElse(null); } + public void execute(PlayerOptionalContext context, EventTrigger trigger) { + for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { + function.run(context); + } + } + public Key id() { return id; } @@ -60,6 +75,7 @@ public class CustomFurniture { HitBox[] hitBoxes, RotationRule rotationRule, AlignmentRule alignmentRule, - Optional externalModel) { + Optional externalModel, + Optional dropOffset) { } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ExternalModel.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ExternalModel.java index 5a12ba446..8f2f1c4b1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ExternalModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/ExternalModel.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; public interface ExternalModel { @@ -8,5 +8,5 @@ public interface ExternalModel { String id(); - void bindModel(Entity entity); + void bindModel(AbstractEntity entity); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 42998e48b..fea19d1e0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -46,4 +46,10 @@ public interface Furniture { boolean hasExternalModel(); void spawnSeatEntityForPlayer(Player player, Seat seat); + + FurnitureExtraData extraData(); + + void setExtraData(FurnitureExtraData extraData); + + void save(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java index b92b56dc5..c730621e8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java @@ -1,7 +1,11 @@ package net.momirealms.craftengine.core.entity.furniture; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -16,11 +20,13 @@ public interface FurnitureElement { ItemDisplayContext transform(); + boolean applyDyedColor(); + Vector3f scale(); Vector3f translation(); Vector3f position(); - void initPackets(int entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer packets); + void initPackets(int entityId, @NotNull WorldPosition position, @NotNull Quaternionf conjugated, @Nullable Integer dyedColor, Consumer packets); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java new file mode 100644 index 000000000..940b0b0be --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; + +import java.io.IOException; +import java.util.Optional; + +public class FurnitureExtraData { + public static final String ITEM = "item"; + public static final String DYED_COLOR = "dyed_color"; + public static final String ANCHOR_TYPE = "anchor_type"; + + private final CompoundTag data; + + public FurnitureExtraData(CompoundTag data) { + this.data = data; + } + + public static FurnitureExtraData of(CompoundTag data) { + return new FurnitureExtraData(data); + } + + public CompoundTag copyTag() { + return this.data.copy(); + } + + public CompoundTag unsafeTag() { + return this.data; + } + + public Optional> item() { + byte[] data = this.data.getByteArray(ITEM); + if (data == null) return Optional.empty(); + return Optional.of(CraftEngine.instance().itemManager().fromByteArray(data)); + } + + public Optional dyedColor() { + if (this.data.containsKey(DYED_COLOR)) return Optional.of(this.data.getInt(DYED_COLOR)); + return Optional.empty(); + } + + public Optional anchorType() { + if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); + return Optional.empty(); + } + + public FurnitureExtraData anchorType(AnchorType type) { + this.data.putInt(ANCHOR_TYPE, type.getId()); + return this; + } + + public static Builder builder() { + return new Builder(); + } + + public static FurnitureExtraData fromBytes(final byte[] data) throws IOException { + return new FurnitureExtraData(NBT.fromBytes(data)); + } + + public static byte[] toBytes(final FurnitureExtraData data) throws IOException { + return NBT.toBytes(data.data); + } + + public byte[] toBytes() throws IOException { + return toBytes(this); + } + + public static class Builder { + private final CompoundTag data; + + public Builder() { + this.data = new CompoundTag(); + } + + public Builder item(Item item) { + this.data.putByteArray(ITEM, item.toByteArray()); + return this; + } + + public Builder dyedColor(Integer color) { + if (color == null) return this; + this.data.putInt(DYED_COLOR, color); + return this; + } + + public Builder anchorType(AnchorType type) { + this.data.putInt(ANCHOR_TYPE, type.getId()); + return this; + } + + public FurnitureExtraData build() { + return new FurnitureExtraData(data); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index d2692164d..50d4787c1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -1,11 +1,10 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import org.incendo.cloud.suggestion.Suggestion; import javax.annotation.Nullable; @@ -15,13 +14,13 @@ import java.util.Optional; public interface FurnitureManager extends Manageable { String FURNITURE_ADMIN_NODE = "craftengine.furniture.admin"; - ConfigSectionParser parser(); + ConfigParser parser(); void initSuggestions(); Collection cachedSuggestions(); - Furniture place(CustomFurniture furniture, Vec3d vec3d, World world, AnchorType anchorType, boolean playSound); + Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound); Optional furnitureById(Key id); @@ -31,7 +30,7 @@ public interface FurnitureManager extends Manageable { Furniture loadedFurnitureByRealEntityId(int entityId); @Nullable - default Furniture loadedFurnitureByRealEntity(Entity entity) { + default Furniture loadedFurnitureByRealEntity(AbstractEntity entity) { return loadedFurnitureByRealEntityId(entity.entityID()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java index 28d4208c1..cd145a799 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -14,7 +14,7 @@ public interface HitBox { Key type(); - void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, + void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, BiConsumer aabb); void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/GameMode.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/GameMode.java new file mode 100644 index 000000000..716ce0ba3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/GameMode.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.core.entity.player; + +public enum GameMode { + SURVIVAL(0), + CREATIVE(1), + ADVENTURE(2), + SPECTATOR(3); + + private final int id; + + GameMode(int id) { + this.id = id; + } + + public int id() { + return id; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 48d6b6185..bb50aec7d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.entity.player; import net.kyori.adventure.text.Component; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.Key; @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public abstract class Player extends Entity implements NetWorkUser { +public abstract class Player extends AbstractEntity implements NetWorkUser { private static final Key TYPE = Key.of("minecraft:player"); public abstract boolean isSecondaryUseActive(); @@ -46,11 +46,9 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract boolean isSneaking(); - public abstract boolean isCreativeMode(); + public abstract GameMode gameMode(); - public abstract boolean isSpectatorMode(); - - public abstract boolean isAdventureMode(); + public abstract void setGameMode(GameMode gameMode); public abstract boolean canBreak(BlockPos pos, Object state); @@ -58,6 +56,10 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract void sendActionBar(Component text); + public abstract void sendMessage(Component text, boolean overlay); + + public abstract void sendTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut); + public abstract boolean updateLastSuccessfulInteractionTick(int tick); public abstract int lastSuccessfulInteractionTick(); @@ -73,7 +75,7 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract String name(); public void playSound(Key sound) { - playSound(sound, 1, 1); + playSound(sound, 1f, 1f); } public abstract void playSound(Key sound, float volume, float pitch); @@ -90,8 +92,26 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract double luck(); + public abstract boolean isFlying(); + @Override public Key type() { return TYPE; } + + public boolean isCreativeMode() { + return gameMode() == GameMode.CREATIVE; + } + + public boolean isSpectatorMode() { + return gameMode() == GameMode.SPECTATOR; + } + + public boolean isSurvivalMode() { + return gameMode() == GameMode.SURVIVAL; + } + + public boolean isAdventureMode() { + return gameMode() == GameMode.ADVENTURE; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/AbstractCustomProjectile.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/AbstractCustomProjectile.java new file mode 100644 index 000000000..117367a84 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/AbstractCustomProjectile.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.entity.projectile; + +import net.momirealms.craftengine.core.item.Item; + +public abstract class AbstractCustomProjectile implements CustomProjectile { + protected final ProjectileMeta meta; + protected final Projectile projectile; + protected final Item item; + + protected AbstractCustomProjectile(ProjectileMeta meta, Projectile projectile, Item item) { + this.meta = meta; + this.projectile = projectile; + this.item = item; + } + + @Override + public ProjectileMeta metadata() { + return this.meta; + } + + @Override + public Projectile projectile() { + return projectile; + } + + @Override + public Item item() { + return item; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/CustomProjectile.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/CustomProjectile.java new file mode 100644 index 000000000..661f2d4ac --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/CustomProjectile.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.entity.projectile; + +import net.momirealms.craftengine.core.item.Item; + +public interface CustomProjectile { + + ProjectileMeta metadata(); + + Projectile projectile(); + + Item item(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/Projectile.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/Projectile.java new file mode 100644 index 000000000..c721ee990 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/Projectile.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.entity.projectile; + +import net.momirealms.craftengine.core.entity.Entity; + +public interface Projectile extends Entity { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileManager.java new file mode 100644 index 000000000..12796743a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileManager.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.entity.projectile; + +import net.momirealms.craftengine.core.plugin.Manageable; + +import java.util.Optional; + +public interface ProjectileManager extends Manageable { + + Optional projectileByEntityId(int entityId); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java new file mode 100644 index 000000000..9f0337be0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.entity.projectile; + +import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.util.Key; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +public record ProjectileMeta(Key item, ItemDisplayContext displayType, Vector3f scale, Vector3f translation, Quaternionf rotation, String type) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index aa7a7797e..6cd120eb1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; @@ -75,8 +75,8 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public ConfigSectionParser[] parsers() { - return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; + public ConfigParser[] parsers() { + return new ConfigParser[] {this.imageParser, this.emojiParser}; } @Override @@ -339,7 +339,7 @@ public abstract class AbstractFontManager implements FontManager { return this.fonts.computeIfAbsent(key, Font::new); } - public class EmojiParser implements ConfigSectionParser { + public class EmojiParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"emoji", "emojis"}; @Override @@ -400,7 +400,7 @@ public abstract class AbstractFontManager implements FontManager { } } - public class ImageParser implements ConfigSectionParser { + public class ImageParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"images", "image"}; @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java index 5cf91415a..ff72812aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java @@ -5,6 +5,6 @@ import net.momirealms.craftengine.core.plugin.context.ContextKey; public final class EmojiParameters { private EmojiParameters() {} - public static final ContextKey KEYWORD = ContextKey.of("keyword"); - public static final ContextKey EMOJI = ContextKey.of("emoji"); + public static final ContextKey KEYWORD = ContextKey.direct("keyword"); + public static final ContextKey EMOJI = ContextKey.direct("emoji"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 95d2251d4..307350eea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; @@ -45,7 +45,7 @@ public interface FontManager extends Manageable { IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement); - ConfigSectionParser[] parsers(); + ConfigParser[] parsers(); default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { return replaceMiniMessageEmoji(miniMessage, player, Config.maxEmojisPerParse()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java index cd8991160..70fbf6d8f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java @@ -93,6 +93,17 @@ public class AbstractItem, I> implements Item { return this.factory.maxDamage(this.item); } + @Override + public Item dyedColor(Integer data) { + this.factory.dyedColor(this.item, data); + return this; + } + + @Override + public Optional dyedColor() { + return this.factory.dyedColor(this.item); + } + @SuppressWarnings("unchecked") @Override public Optional> getCustomItem() { @@ -358,4 +369,9 @@ public class AbstractItem, I> implements Item { public void merge(Item another) { this.factory.merge(this.item, (W) ((AbstractItem) another).item); } + + @Override + public byte[] toByteArray() { + return this.factory.toByteArray(this.item); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ComponentKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ComponentKeys.java index 30e2be9ce..597639121 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ComponentKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ComponentKeys.java @@ -22,4 +22,5 @@ public class ComponentKeys { public static final Key REPAIR_COST = Key.of("minecraft", "repair_cost"); public static final Key CUSTOM_DATA = Key.of("minecraft", "custom_data"); public static final Key PROFILE = Key.of("minecraft", "profile"); + public static final Key DYED_COLOR = Key.of("minecraft", "dyed_color"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index b4ff58f2a..8f92db2d7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -3,9 +3,13 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.event.EventTrigger; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; +import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -39,6 +43,8 @@ public interface CustomItem extends BuildableItem { Item buildItem(ItemBuildContext context); + void execute(PlayerOptionalContext context, EventTrigger trigger); + @NotNull List behaviors(); @@ -61,6 +67,8 @@ public interface CustomItem extends BuildableItem { Builder settings(ItemSettings settings); + Builder events(EnumMap>> events); + CustomItem build(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 1d12dc775..50786ace9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -56,6 +56,10 @@ public interface Item { Optional maxDamage(); + Item dyedColor(Integer data); + + Optional dyedColor(); + Item customName(String displayName); Optional customName(); @@ -145,4 +149,6 @@ public interface Item { Item mergeCopy(Item another); void merge(Item another); + + byte[] toByteArray(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java index 521907dc3..a8b7dd219 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,13 +23,13 @@ public class ItemBuildContext extends PlayerOptionalContext { @NotNull public static ItemBuildContext of(@Nullable Player player, @NotNull ContextHolder.Builder builder) { - if (player != null) builder.withParameter(CommonParameters.PLAYER, player); + if (player != null) builder.withParameter(DirectContextParameters.PLAYER, player); return new ItemBuildContext(player, builder.build()); } @NotNull public static ItemBuildContext of(@Nullable Player player) { if (player == null) return new ItemBuildContext(null, ContextHolder.EMPTY); - return new ItemBuildContext(player, new ContextHolder(Map.of(CommonParameters.PLAYER, () -> player))); + return new ItemBuildContext(player, new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java index c747a5131..eb3c9fada 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java @@ -83,6 +83,10 @@ public abstract class ItemFactory, I> { protected abstract void damage(W item, Integer damage); + protected abstract Optional dyedColor(W item); + + protected abstract void dyedColor(W item, Integer color); + protected abstract Optional maxDamage(W item); protected abstract void maxDamage(W item, Integer damage); @@ -138,4 +142,7 @@ public abstract class ItemFactory, I> { protected abstract void equippable(W item, EquipmentData data); protected abstract Optional equippable(W item); + + protected abstract byte[] toByteArray(W item); + } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index f3ed35781..a5320e710 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.pack.model.ItemModel; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import org.incendo.cloud.suggestion.Suggestion; @@ -17,7 +17,7 @@ import java.util.*; public interface ItemManager extends Manageable, ModelGenerator { - ConfigSectionParser parser(); + ConfigParser parser(); Map> legacyItemOverrides(); @@ -41,6 +41,8 @@ public interface ItemManager extends Manageable, ModelGenerator { Item wrap(T itemStack); + Item fromByteArray(byte[] bytes); + Collection items(); Key itemId(T itemStack); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 59d8a06a4..c2f41b13c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.item; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; import net.momirealms.craftengine.core.item.modifier.EquippableModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; @@ -9,6 +11,8 @@ 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; +import org.joml.Quaternionf; +import org.joml.Vector3f; import java.util.*; import java.util.stream.Collectors; @@ -22,6 +26,8 @@ public class ItemSettings { List anvilRepairItems = List.of(); boolean renameable = true; boolean canPlaceRelatedVanillaBlock = false; + ProjectileMeta projectileMeta; + boolean dyeable = true; private ItemSettings() {} @@ -50,6 +56,8 @@ public class ItemSettings { newSettings.anvilRepairItems = settings.anvilRepairItems; newSettings.renameable = settings.renameable; newSettings.canPlaceRelatedVanillaBlock = settings.canPlaceRelatedVanillaBlock; + newSettings.projectileMeta = settings.projectileMeta; + newSettings.dyeable = settings.dyeable; return newSettings; } @@ -65,6 +73,10 @@ public class ItemSettings { return settings; } + public ProjectileMeta projectileMeta() { + return projectileMeta; + } + public boolean canPlaceRelatedVanillaBlock() { return canPlaceRelatedVanillaBlock; } @@ -85,6 +97,10 @@ public class ItemSettings { return tags; } + public boolean dyeable() { + return dyeable; + } + public List repairItems() { return anvilRepairItems; } @@ -109,6 +125,11 @@ public class ItemSettings { return this; } + public ItemSettings projectileMeta(ProjectileMeta projectileMeta) { + this.projectileMeta = projectileMeta; + return this; + } + public ItemSettings canPlaceRelatedVanillaBlock(boolean canPlaceRelatedVanillaBlock) { this.canPlaceRelatedVanillaBlock = canPlaceRelatedVanillaBlock; return this; @@ -129,6 +150,11 @@ public class ItemSettings { return this; } + public ItemSettings dyeable(boolean bool) { + this.dyeable = bool; + return this; + } + @FunctionalInterface public interface Modifier { @@ -193,6 +219,20 @@ public class ItemSettings { boolean bool = (boolean) value; return settings -> settings.canPlaceRelatedVanillaBlock(bool); })); + registerFactory("projectile", (value -> { + Map args = MiscUtils.castToMap(value, false); + Key customTridentItemId = Key.of(Objects.requireNonNull(args.get("item"), "'item should not be null'").toString()); + ItemDisplayContext displayType = ItemDisplayContext.valueOf(args.getOrDefault("display-transform", "NONE").toString().toUpperCase(Locale.ENGLISH)); + Vector3f translation = MiscUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation"); + Vector3f scale = MiscUtils.getAsVector3f(args.getOrDefault("scale", "1"), "scale"); + Quaternionf rotation = MiscUtils.getAsQuaternionf(ResourceConfigUtils.get(args, "rotation-left", "rotation"), "rotation-left"); + String type = args.getOrDefault("type", "none").toString(); + return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, scale, translation, rotation, type)); + })); + registerFactory("dyeable", (value -> { + boolean bool = (boolean) value; + return settings -> settings.dyeable(bool); + })); } private static void registerFactory(String id, ItemSettings.Modifier.Factory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java index af0524d2a..09d5eadc1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java @@ -28,7 +28,7 @@ public class ItemBehaviors { Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); ItemBehaviorFactory factory = BuiltInRegistries.ITEM_BEHAVIOR_FACTORY.getValue(key); if (factory == null) { - throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type.toString()); + throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type); } return factory.create(pack, path, id, map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 7220614c4..f84e496ec 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; @@ -35,7 +35,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.recipeParser; } @@ -140,7 +140,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } } - public class RecipeParser implements ConfigSectionParser { + public class RecipeParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"recipes", "recipe"}; @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java index eb59f67ed..c1fdcdbc3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -11,7 +11,7 @@ import java.util.Optional; public interface RecipeManager extends Manageable { - ConfigSectionParser parser(); + ConfigParser parser(); boolean isDataPackRecipe(Key key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java index 0dc4c9a32..43ca9b0aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java @@ -30,6 +30,9 @@ public class LootConditions { register(CommonConditions.FALLING_BLOCK, new FallingBlockCondition.FactoryImpl<>()); register(CommonConditions.RANDOM, new RandomCondition.FactoryImpl<>()); register(CommonConditions.DISTANCE, new DistanceCondition.FactoryImpl<>()); + register(CommonConditions.PERMISSION, new PermissionCondition.FactoryImpl<>()); + register(CommonConditions.EQUALS, new EqualsCondition.FactoryImpl<>()); + register(CommonConditions.EXPRESSION, new ExpressionCondition.FactoryImpl<>()); } public static void register(Key key, ConditionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootContext.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootContext.java index c7251cd9e..5e3c9092d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootContext.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.loot; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.world.World; @@ -12,7 +11,6 @@ import javax.annotation.Nullable; public class LootContext extends PlayerOptionalContext { private final World world; private final float luck; - private Item tempLoot; public LootContext(@NotNull World world, @Nullable Player player, float luck, @NotNull ContextHolder contexts) { super(player, contexts); @@ -27,12 +25,4 @@ public class LootContext extends PlayerOptionalContext { public World world() { return this.world; } - - public Item tempLoot() { - return this.tempLoot; - } - - public void setTempLoot(Item tempLoot) { - this.tempLoot = tempLoot; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/VanillaLootManager.java b/core/src/main/java/net/momirealms/craftengine/core/loot/VanillaLootManager.java index c4fabdf8d..65ece069d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/VanillaLootManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/VanillaLootManager.java @@ -1,14 +1,14 @@ package net.momirealms.craftengine.core.loot; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; import java.util.Optional; public interface VanillaLootManager extends Manageable { - ConfigSectionParser parser(); + ConfigParser parser(); Optional getBlockLoot(int blockState); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java index eaf7a96ac..19dae5698 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/ExpLootEntryContainer.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -32,8 +32,8 @@ public class ExpLootEntryContainer extends AbstractLootEntryContainer { @Override public boolean expand(LootContext context, Consumer> choiceConsumer) { if (super.test(context)) { - context.getOptionalParameter(CommonParameters.WORLD) - .ifPresent(it -> context.getOptionalParameter(CommonParameters.LOCATION).ifPresent(loc -> it.dropExp(loc.toCenter(), value.getInt(context)))); + context.getOptionalParameter(DirectContextParameters.POSITION) + .ifPresent(it -> it.world().dropExp(it, value.getInt(context))); return true; } else { return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/FurnitureItemLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/FurnitureItemLootEntryContainer.java new file mode 100644 index 000000000..438ef2cd5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/FurnitureItemLootEntryContainer.java @@ -0,0 +1,58 @@ +package net.momirealms.craftengine.core.loot.entry; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.loot.LootConditions; +import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.loot.function.LootFunction; +import net.momirealms.craftengine.core.loot.function.LootFunctions; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; + +public class FurnitureItemLootEntryContainer extends SingleItemLootEntryContainer { + public static final Factory FACTORY = new Factory<>(); + private final boolean hasFallback; + + protected FurnitureItemLootEntryContainer(@Nullable Key item, List> conditions, List> lootFunctions, int weight, int quality) { + super(item, conditions, lootFunctions, weight, quality); + this.hasFallback = item != null; + } + + @Override + public Key type() { + return LootEntryContainers.FURNITURE_ITEM; + } + + @SuppressWarnings("unchecked") + @Override + protected void createItem(Consumer> lootConsumer, LootContext context) { + Optional> optionalItem = context.getOptionalParameter(DirectContextParameters.FURNITURE_ITEM); + if (optionalItem.isPresent()) { + lootConsumer.accept((Item) optionalItem.get()); + } else if (this.hasFallback) { + super.createItem(lootConsumer, context); + } + } + + public static class Factory implements LootEntryContainerFactory { + @SuppressWarnings("unchecked") + @Override + public LootEntryContainer create(Map arguments) { + Key item = Optional.ofNullable(arguments.get("item")).map(String::valueOf).map(Key::of).orElse(null); + int weight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("weight", 1), "weight"); + int quality = ResourceConfigUtils.getAsInt(arguments.getOrDefault("quality", 0), "quality"); + List> conditions = Optional.ofNullable(arguments.get("conditions")) + .map(it -> LootConditions.fromMapList((List>) it)) + .orElse(Collections.emptyList()); + List> functions = Optional.ofNullable(arguments.get("functions")) + .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList((List>) it))) + .orElse(Collections.emptyList()); + return new FurnitureItemLootEntryContainer<>(item, conditions, functions, weight, quality); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java index 5d44629dc..2d27fb6f1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/LootEntryContainers.java @@ -16,12 +16,14 @@ import java.util.Map; public class LootEntryContainers { public static final Key ALTERNATIVES = Key.from("craftengine:alternatives"); public static final Key ITEM = Key.from("craftengine:item"); + public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); public static final Key EXP = Key.from("craftengine:exp"); static { register(ALTERNATIVES, AlternativesLootEntryContainer.FACTORY); register(ITEM, SingleItemLootEntryContainer.FACTORY); register(EXP, ExpLootEntryContainer.FACTORY); + register(FURNITURE_ITEM, FurnitureItemLootEntryContainer.FACTORY); } public static void register(Key key, LootEntryContainerFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java index 4b5c300f2..4eaffd83c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/SingleItemLootEntryContainer.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunction; import net.momirealms.craftengine.core.loot.function.LootFunctions; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -31,7 +31,7 @@ public class SingleItemLootEntryContainer extends AbstractSingleLootEntryCont @SuppressWarnings("unchecked") @Override protected void createItem(Consumer> lootConsumer, LootContext context) { - Item tItem = (Item) CraftEngine.instance().itemManager().createWrappedItem(this.item, context.getOptionalParameter(CommonParameters.PLAYER).orElse(null)); + Item tItem = (Item) CraftEngine.instance().itemManager().createWrappedItem(this.item, context.getOptionalParameter(DirectContextParameters.PLAYER).orElse(null)); if (tItem != null) { lootConsumer.accept(tItem); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index ea6f98583..ddca1c41d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootConditions; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -31,7 +31,7 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< @Override protected Item applyInternal(Item item, LootContext context) { - Optional> itemInHand = context.getOptionalParameter(PlayerParameters.MAIN_HAND_ITEM); + Optional> itemInHand = context.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); int level = itemInHand.map(value -> value.getEnchantment(this.enchantment).map(Enchantment::level).orElse(0)).orElse(0); int newCount = this.formula.apply(item.count(), level); item.count(newCount); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java index 88e9664dc..6fda51f13 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/DropExpFunction.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -26,8 +26,8 @@ public class DropExpFunction extends AbstractLootConditionalFunction { @Override protected Item applyInternal(Item item, LootContext context) { - context.getOptionalParameter(CommonParameters.WORLD) - .ifPresent(it -> context.getOptionalParameter(CommonParameters.LOCATION).ifPresent(loc -> it.dropExp(loc.toCenter(), value.getInt(context)))); + context.getOptionalParameter(DirectContextParameters.POSITION) + .ifPresent(it -> it.world().dropExp(it, value.getInt(context))); return item; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ExplosionDecayFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ExplosionDecayFunction.java index 0ab1b6f5e..5f2faa985 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ExplosionDecayFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ExplosionDecayFunction.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootConditions; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; @@ -22,7 +22,7 @@ public class ExplosionDecayFunction extends AbstractLootConditionalFunction applyInternal(Item item, LootContext context) { - Optional radius = context.getOptionalParameter(CommonParameters.EXPLOSION_RADIUS); + Optional radius = context.getOptionalParameter(DirectContextParameters.EXPLOSION_RADIUS); if (radius.isPresent()) { float f = 1f / radius.get(); int amount = item.count(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index b582cb7d9..caf68d43b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.pack; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import com.google.gson.*; import dev.dejvokep.boostedyaml.YamlDocument; import dev.dejvokep.boostedyaml.block.implementation.Section; @@ -19,7 +21,7 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; import net.momirealms.craftengine.core.plugin.locale.I18NData; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; @@ -29,6 +31,7 @@ import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; @@ -44,7 +47,6 @@ import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import static net.momirealms.craftengine.core.util.MiscUtils.castToMap; @@ -70,8 +72,9 @@ public abstract class AbstractPackManager implements PackManager { private final CraftEngine plugin; private final BiConsumer eventDispatcher; private final Map loadedPacks = new HashMap<>(); - private final Map sectionParsers = new HashMap<>(); - private final TreeMap> cachedConfigs = new TreeMap<>(); + private final Map sectionParsers = new HashMap<>(); + private Map cachedConfigFiles = Collections.emptyMap(); + private Map cachedAssetFiles = Collections.emptyMap(); protected BiConsumer zipGenerator; protected ResourcePackHost resourcePackHost; @@ -142,7 +145,6 @@ public abstract class AbstractPackManager implements PackManager { @Override public void load() { - initFileSystemProvider(); List> list = Config.instance().settings().getMapList("resource-pack.delivery.hosting"); if (list == null || list.isEmpty()) { this.resourcePackHost = NoneHost.INSTANCE; @@ -175,7 +177,6 @@ public abstract class AbstractPackManager implements PackManager { @Override public void unload() { this.loadedPacks.clear(); - this.cachedConfigs.clear(); } @Override @@ -185,12 +186,13 @@ public abstract class AbstractPackManager implements PackManager { if (magicClazz != null) { int fileCount = ObfA.VALUES[1] - ObfA.VALUES[17]; Constructor magicConstructor = ReflectionUtils.getConstructor(magicClazz, fileCount); + assert magicConstructor != null; +// magicConstructor.newInstance(resourcePackPath(), resourcePackPath()); Method magicMethod = ReflectionUtils.getMethod(magicClazz, void.class); + assert magicMethod != null; this.zipGenerator = (p1, p2) -> { try { - assert magicConstructor != null; Object magicObject = magicConstructor.newInstance(p1, p2); - assert magicMethod != null; magicMethod.invoke(magicObject); } catch (Exception e) { this.plugin.logger().warn("Failed to generate zip files", e); @@ -204,6 +206,15 @@ public abstract class AbstractPackManager implements PackManager { } } + @Override + public void initCachedAssets() { + try { + this.updateCachedAssets(null); + } catch (Exception e) { + this.plugin.logger().warn("Failed to update cached assets", e); + } + } + @NotNull @Override public Collection loadedPacks() { @@ -211,7 +222,7 @@ public abstract class AbstractPackManager implements PackManager { } @Override - public boolean registerConfigSectionParser(ConfigSectionParser parser) { + public boolean registerConfigSectionParser(ConfigParser parser) { for (String id : parser.sectionId()) { if (this.sectionParsers.containsKey(id)) return false; } @@ -347,6 +358,8 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_crossbow_pulling_1.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_crossbow_pulling_2.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_crossbow.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_trident_3d.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/entity/equipment/humanoid/topaz.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/entity/equipment/humanoid_leggings/topaz.png"); for (String item : List.of("helmet", "chestplate", "leggings", "boots", "pickaxe", "axe", "sword", "hoe", "shovel")) { @@ -391,6 +404,8 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json"); // furniture plugin.saveResource("resources/default/configuration/furniture.yml"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json"); @@ -404,48 +419,61 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_frame.png.mcmeta"); } - private void loadResourceConfigs(Predicate predicate) { - long o1 = System.nanoTime(); + private void updateCachedConfigFiles() { + Map previousFiles = this.cachedConfigFiles; + this.cachedConfigFiles = new HashMap<>(); for (Pack pack : loadedPacks()) { if (!pack.enabled()) continue; - Pair, List> files = FileUtils.getConfigsDeeply(pack.configurationFolder()); - for (Path path : files.left()) { - try (InputStreamReader inputStream = new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8)) { - Yaml yaml = new Yaml(new StringKeyConstructor(new LoaderOptions())); - Map data = yaml.load(inputStream); - if (data == null) continue; - for (Map.Entry entry : data.entrySet()) { - processConfigEntry(entry, path, pack); + List files = FileUtils.getYmlConfigsDeeply(pack.configurationFolder()); + for (Path path : files) { + CachedConfigFile cachedFile = previousFiles.get(path); + try { + long lastModifiedTime = Files.getLastModifiedTime(path).toMillis(); + long size = Files.size(path); + if (cachedFile != null && cachedFile.lastModified() == lastModifiedTime && cachedFile.size() == size) { + this.cachedConfigFiles.put(path, cachedFile); + continue; } - } catch (Exception e) { - this.plugin.logger().warn(path, "Error loading config file", e); + try (InputStreamReader inputStream = new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8)) { + Yaml yaml = new Yaml(new StringKeyConstructor(new LoaderOptions())); + Map data = yaml.load(inputStream); + if (data == null) continue; + + this.cachedConfigFiles.put(path, new CachedConfigFile(data, pack, lastModifiedTime, size)); + } catch (Exception e) { + this.plugin.logger().warn(path, "Error loading config file", e); + } + } catch (IOException e) { + this.plugin.logger().warn(path, "Error reading last modified time", e); } } - for (Path path : files.right()) { - try (InputStreamReader inputStream = new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8)) { - Map dataRaw = GsonHelper.get().fromJson(JsonParser.parseReader(inputStream).getAsJsonObject(), Map.class); - Map data = castToMap(dataRaw, false); - for (Map.Entry entry : data.entrySet()) { - processConfigEntry(entry, path, pack); - } - } catch (Exception e) { - this.plugin.logger().warn(path, "Error loading config file", e); - } + } + } + + private void loadResourceConfigs(Predicate predicate) { + TreeMap> cachedConfigs = new TreeMap<>(); + long o1 = System.nanoTime(); + this.updateCachedConfigFiles(); + for (Map.Entry fileEntry : this.cachedConfigFiles.entrySet()) { + CachedConfigFile cachedFile = fileEntry.getValue(); + for (Map.Entry entry : cachedFile.config().entrySet()) { + processConfigEntry(entry, fileEntry.getKey(), cachedFile.pack(), (p, c) -> + cachedConfigs.computeIfAbsent(p, k -> new ArrayList<>()).add(c)); } } long o2 = System.nanoTime(); this.plugin.logger().info("Loaded packs. Took " + String.format("%.2f", ((o2 - o1) / 1_000_000.0)) + " ms"); - for (Map.Entry> entry : this.cachedConfigs.entrySet()) { - ConfigSectionParser parser = entry.getKey(); + for (Map.Entry> entry : cachedConfigs.entrySet()) { + ConfigParser parser = entry.getKey(); long t1 = System.nanoTime(); - for (CachedConfig cached : entry.getValue()) { + for (CachedConfigSection cached : entry.getValue()) { for (Map.Entry configEntry : cached.config().entrySet()) { String key = configEntry.getKey(); try { Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); try { - if (parser.isTemplate()) { - this.plugin.templateManager().addTemplate(cached.pack(), cached.filePath(), id, configEntry.getValue()); + if (parser.supportsParsingObject()) { + parser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue()); } else if (predicate.test(parser)) { if (configEntry.getValue() instanceof Map configSection0) { Map configSection1 = castToMap(configSection0, false); @@ -472,71 +500,123 @@ public abstract class AbstractPackManager implements PackManager { long t2 = System.nanoTime(); this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms"); } - this.cachedConfigs.clear(); } - private void processConfigEntry(Map.Entry entry, Path path, Pack pack) { + private void processConfigEntry(Map.Entry entry, Path path, Pack pack, BiConsumer callback) { if (entry.getValue() instanceof Map typeSections0) { String key = entry.getKey(); int hashIndex = key.indexOf('#'); String configType = hashIndex != -1 ? key.substring(0, hashIndex) : key; Optional.ofNullable(this.sectionParsers.get(configType)) - .ifPresent(parser -> this.cachedConfigs.computeIfAbsent(parser, k -> new ArrayList<>()) - .add(new CachedConfig(key, castToMap(typeSections0, false), path, pack))); - } - } - - private static void initFileSystemProvider() { - String osName = System.getProperty("os.name").toLowerCase(); - String providerClass = null; - if (osName.contains("win")) { - providerClass = "sun.nio.fs.WindowsFileSystemProvider"; - } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { - providerClass = "sun.nio.fs.LinuxFileSystemProvider"; - } else if (osName.contains("mac")) { - providerClass = "sun.nio.fs.MacOSXFileSystemProvider"; - } - if (providerClass != null) { - try { - System.setProperty("java.nio.file.spi.DefaultFileSystemProvider", providerClass); - } catch (Exception ignored) {} - } - } - - private static void deleteDirectory(Path folder) throws IOException { - if (!Files.exists(folder)) return; - try (Stream walk = Files.walk(folder)) { - walk.sorted(Comparator.reverseOrder()) - .parallel() - .forEach(path -> { - try { - Files.delete(path); - } catch (IOException ignored) {} + .ifPresent(parser -> { + callback.accept(parser, new CachedConfigSection(key, castToMap(typeSections0, false), path, pack)); }); } } +// private static void initFileSystemProvider() { +// String osName = System.getProperty("os.name").toLowerCase(); +// String providerClass = null; +// if (osName.contains("win")) { +// providerClass = "sun.nio.fs.WindowsFileSystemProvider"; +// } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { +// providerClass = "sun.nio.fs.LinuxFileSystemProvider"; +// } else if (osName.contains("mac")) { +// providerClass = "sun.nio.fs.MacOSXFileSystemProvider"; +// } +// if (providerClass != null) { +// try { +// System.setProperty("java.nio.file.spi.DefaultFileSystemProvider", providerClass); +// } catch (Exception ignored) {} +// } +// } +// +// private static void deleteDirectory(Path folder) throws IOException { +// if (!Files.exists(folder)) return; +// try (Stream walk = Files.walk(folder)) { +// walk.sorted(Comparator.reverseOrder()) +// .parallel() +// .forEach(path -> { +// try { +// Files.delete(path); +// } catch (IOException ignored) {} +// }); +// } +// } + + private List>> updateCachedAssets(@Nullable FileSystem fs) throws IOException { + List folders = new ArrayList<>(); + Map> conflictChecker = new HashMap<>(Math.max(128, this.cachedAssetFiles.size())); + Map previousFiles = this.cachedAssetFiles; + this.cachedAssetFiles = new HashMap<>(Math.max(128, this.cachedAssetFiles.size())); + 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()); + for (Path sourceFolder : folders) { + if (Files.exists(sourceFolder)) { + Files.walkFileTree(sourceFolder, new SimpleFileVisitor<>() { + @Override + public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { + CachedAssetFile cachedAsset = previousFiles.get(file); + long lastModified = Files.getLastModifiedTime(file).toMillis(); + long size = Files.size(file); + if (cachedAsset != null && cachedAsset.lastModified() == lastModified && cachedAsset.size() == size) { + AbstractPackManager.this.cachedAssetFiles.put(file, cachedAsset); + } else { + cachedAsset = new CachedAssetFile(Files.readAllBytes(file), lastModified, size); + AbstractPackManager.this.cachedAssetFiles.put(file, cachedAsset); + } + if (fs == null) return FileVisitResult.CONTINUE; + Path relative = sourceFolder.relativize(file); + Path targetPath = fs.getPath("resource_pack/" + relative.toString().replace("\\", "/")); + List conflicts = conflictChecker.get(relative); + if (conflicts == null) { + Files.createDirectories(targetPath.getParent()); + Files.write(targetPath, cachedAsset.data()); + conflictChecker.put(relative, List.of(file)); + } else { + PathContext relativeCTX = PathContext.of(relative); + PathContext targetCTX = PathContext.of(targetPath); + PathContext fileCTX = PathContext.of(file); + for (ResolutionConditional resolution : Config.resolutions()) { + if (resolution.matcher().test(relativeCTX)) { + resolution.resolution().run(targetCTX, fileCTX); + return FileVisitResult.CONTINUE; + } + } + switch (conflicts.size()) { + case 1 -> conflictChecker.put(relative, List.of(conflicts.get(0), file)); + case 2 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), file)); + case 3 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), conflicts.get(2), file)); + case 4 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), conflicts.get(2), conflicts.get(3), file)); + default -> { + // just ignore it if it has many conflict files + } + } + } + return FileVisitResult.CONTINUE; + } + }); + } + } + List>> conflicts = new ArrayList<>(); + for (Map.Entry> entry : conflictChecker.entrySet()) { + if (entry.getValue().size() > 1) { + conflicts.add(Pair.of(entry.getKey(), entry.getValue())); + } + } + return conflicts; + } + @Override - public void generateResourcePack() { + public void generateResourcePack() throws IOException { this.plugin.logger().info("Generating resource pack..."); long start = System.currentTimeMillis(); + // get the target location - Path generatedPackPath = this.plugin.dataFolderPath() - .resolve("generated") - .resolve("resource_pack"); - - try { - deleteDirectory(generatedPackPath); - } catch (IOException e) { - this.plugin.logger().severe("Error deleting previous resource pack", e); - } - - // firstly merge existing folders - try { - List folders = new ArrayList<>(); - folders.addAll(loadedPacks().stream().filter(Pack::enabled).map(Pack::resourcePackFolder).toList()); - folders.addAll(Config.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList()); - List>> duplicated = mergeFolder(folders, generatedPackPath); + try (FileSystem fs = Jimfs.newFileSystem(Configuration.forCurrentPlatform())) { + // firstly merge existing folders + Path generatedPackPath = fs.getPath("resource_pack"); + List>> duplicated = this.updateCachedAssets(fs); if (!duplicated.isEmpty()) { plugin.logger().severe(AdventureHelper.miniMessage().stripTags(TranslationManager.instance().miniMessageTranslation("warning.config.pack.duplicated_files"))); int x = 1; @@ -551,49 +631,46 @@ public abstract class AbstractPackManager implements PackManager { } } } - } catch (IOException e) { - this.plugin.logger().severe("Error merging resource pack", e); + + this.generateFonts(generatedPackPath); + this.generateItemModels(generatedPackPath, this.plugin.itemManager()); + this.generateItemModels(generatedPackPath, this.plugin.blockManager()); + this.generateBlockOverrides(generatedPackPath); + this.generateLegacyItemOverrides(generatedPackPath); + this.generateModernItemOverrides(generatedPackPath); + this.generateModernItemModels1_21_2(generatedPackPath); + this.generateModernItemModels1_21_4(generatedPackPath); + this.generateOverrideSounds(generatedPackPath); + this.generateCustomSounds(generatedPackPath); + this.generateClientLang(generatedPackPath); + this.generateEquipments(generatedPackPath); + this.generateParticle(generatedPackPath); + Path finalPath = resourcePackPath(); + Files.createDirectories(finalPath.getParent()); + try { + this.zipGenerator.accept(generatedPackPath, finalPath); + } catch (Exception e) { + this.plugin.logger().severe("Error zipping resource pack", e); + } + long end = System.currentTimeMillis(); + this.plugin.logger().info("Finished generating resource pack in " + (end - start) + "ms"); + this.eventDispatcher.accept(generatedPackPath, finalPath); } - - this.generateFonts(generatedPackPath); - this.generateLegacyItemOverrides(generatedPackPath); - this.generateModernItemOverrides(generatedPackPath); - this.generateModernItemModels1_21_2(generatedPackPath); - this.generateModernItemModels1_21_4(generatedPackPath); - this.generateBlockOverrides(generatedPackPath); - this.generateItemModels(generatedPackPath, this.plugin.itemManager()); - this.generateItemModels(generatedPackPath, this.plugin.blockManager()); - this.generateOverrideSounds(generatedPackPath); - this.generateCustomSounds(generatedPackPath); - this.generateClientLang(generatedPackPath); - this.generateEquipments(generatedPackPath); - this.generateParticle(generatedPackPath); - - Path zipFile = resourcePackPath(); - try { - this.zipGenerator.accept(generatedPackPath, zipFile); - } catch (Exception e) { - this.plugin.logger().severe("Error zipping resource pack", e); - } - - long end = System.currentTimeMillis(); - this.plugin.logger().info("Finished generating resource pack in " + (end - start) + "ms"); - this.eventDispatcher.accept(generatedPackPath, zipFile); } private void generateParticle(Path generatedPackPath) { if (!Config.removeTintedLeavesParticle()) return; if (Config.packMaxVersion() < 21.49f) return; - var json = new JsonObject(); - var textures = new JsonArray(); + JsonObject particleJson = new JsonObject(); + JsonArray textures = new JsonArray(); textures.add("empty"); - json.add("textures", textures); - var jsonPath = generatedPackPath + particleJson.add("textures", textures); + Path jsonPath = generatedPackPath .resolve("assets") .resolve("minecraft") .resolve("particles") .resolve("tinted_leaves.json"); - var pngPath = generatedPackPath + Path pngPath = generatedPackPath .resolve("assets") .resolve("minecraft") .resolve("textures") @@ -603,11 +680,11 @@ public abstract class AbstractPackManager implements PackManager { Files.createDirectories(jsonPath.getParent()); Files.createDirectories(pngPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating directories", e); + this.plugin.logger().severe("Error creating directories", e); return; } try { - GsonHelper.writeJsonFile(json, jsonPath); + GsonHelper.writeJsonFile(particleJson, jsonPath); Files.write(pngPath, EMPTY_IMAGE); } catch (IOException e) { this.plugin.logger().severe("Error writing particles file", e); @@ -719,7 +796,6 @@ public abstract class AbstractPackManager implements PackManager { .resolve("assets") .resolve(entry.getKey()) .resolve("sounds.json"); - JsonObject soundJson; if (Files.exists(soundPath)) { try (BufferedReader reader = Files.newBufferedReader(soundPath)) { @@ -731,18 +807,15 @@ public abstract class AbstractPackManager implements PackManager { } else { soundJson = new JsonObject(); } - for (SoundEvent soundEvent : entry.getValue()) { soundJson.add(soundEvent.id().value(), soundEvent.get()); } - try { Files.createDirectories(soundPath.getParent()); } catch (IOException e) { plugin.logger().severe("Error creating " + soundPath.toAbsolutePath()); return; } - try (BufferedWriter writer = Files.newBufferedWriter(soundPath)) { GsonHelper.get().toJson(soundJson, writer); } catch (IOException e) { @@ -796,14 +869,12 @@ public abstract class AbstractPackManager implements PackManager { plugin.logger().warn("Cannot find " + originalKey.value() + " in sound template"); } } - try { Files.createDirectories(soundPath.getParent()); } catch (IOException e) { plugin.logger().severe("Error creating " + soundPath.toAbsolutePath()); return; } - try (BufferedWriter writer = Files.newBufferedWriter(soundPath)) { GsonHelper.get().toJson(soundJson, writer); } catch (IOException e) { @@ -818,21 +889,18 @@ public abstract class AbstractPackManager implements PackManager { .resolve(generation.path().namespace()) .resolve("models") .resolve(generation.path().value() + ".json"); - if (Files.exists(modelPath)) { - plugin.logger().warn("Failed to generate model because " + modelPath.toAbsolutePath() + " already exists"); + TranslationManager.instance().log("warning.config.resource_pack.model.generation.already_exist", modelPath.toAbsolutePath().toString()); continue; } - try { Files.createDirectories(modelPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + modelPath.toAbsolutePath()); + plugin.logger().severe("Error creating " + modelPath.toAbsolutePath(), e); continue; } - try (BufferedWriter writer = Files.newBufferedWriter(modelPath)) { - GsonHelper.get().toJson(generation.getJson(), writer); + GsonHelper.get().toJson(generation.get(), writer); } catch (IOException e) { plugin.logger().warn("Failed to generate model: " + modelPath.toAbsolutePath(), e); } @@ -867,13 +935,13 @@ public abstract class AbstractPackManager implements PackManager { try { Files.createDirectories(overridedBlockPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + overridedBlockPath.toAbsolutePath()); + plugin.logger().severe("Error creating " + overridedBlockPath.toAbsolutePath(), e); continue; } try (BufferedWriter writer = Files.newBufferedWriter(overridedBlockPath)) { GsonHelper.get().toJson(stateJson, writer); } catch (IOException e) { - plugin.logger().warn("Failed to create block states for [" + key + "]"); + plugin.logger().warn("Failed to create block states for " + key, e); } } @@ -892,13 +960,13 @@ public abstract class AbstractPackManager implements PackManager { try { Files.createDirectories(overridedBlockPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + overridedBlockPath.toAbsolutePath()); + plugin.logger().severe("Error creating " + overridedBlockPath.toAbsolutePath(), e); continue; } try (BufferedWriter writer = Files.newBufferedWriter(overridedBlockPath)) { GsonHelper.get().toJson(stateJson, writer); } catch (IOException e) { - plugin.logger().warn("Failed to create block states for [" + key + "]"); + plugin.logger().warn("Failed to create block states for " + key, e); } } } @@ -907,83 +975,91 @@ public abstract class AbstractPackManager implements PackManager { if (Config.packMaxVersion() < 21.19f) return; if (Config.packMinVersion() > 21.39f) return; - boolean has = false; - for (Map.Entry> entry : plugin.itemManager().modernItemModels1_21_2().entrySet()) { - has = true; - Key key = entry.getKey(); + // 此段代码生成1.21.2专用的item model文件,情况非常复杂! + for (Map.Entry> entry : this.plugin.itemManager().modernItemModels1_21_2().entrySet()) { + Key itemModelPath = entry.getKey(); List legacyOverridesModels = entry.getValue(); - boolean first = true; - JsonObject jsonObject = new JsonObject(); - JsonArray overrides = new JsonArray(); - for (LegacyOverridesModel model : legacyOverridesModels) { - if (first) { - jsonObject.addProperty("parent", model.model()); - if (model.hasPredicate()) { - overrides.add(model.toLegacyPredicateElement()); - } - first = false; - } else { - overrides.add(model.toLegacyPredicateElement()); - } - } - if (!overrides.isEmpty()) { - jsonObject.add("overrides", overrides); + + // 检测item model合法性 + if (PRESET_MODERN_MODELS_ITEM.containsKey(itemModelPath) || PRESET_LEGACY_MODELS_ITEM.containsKey(itemModelPath)) { + TranslationManager.instance().log("warning.config.resource_pack.item_model.conflict.vanilla", itemModelPath.asString()); + continue; } + // 要检查目标生成路径是否已经存在模型,如果存在模型,应该只为其生成overrides Path itemPath = generatedPackPath .resolve("assets") - .resolve(key.namespace()) + .resolve(itemModelPath.namespace()) .resolve("models") .resolve("item") - .resolve(key.value() + ".json"); - if (Files.exists(itemPath)) { - plugin.logger().warn("Failed to generate item model for [" + key + "] because " + itemPath.toAbsolutePath() + " already exists"); - } else { - if (PRESET_MODERN_MODELS_ITEM.containsKey(key) || PRESET_LEGACY_MODELS_ITEM.containsKey(key)) { - plugin.logger().warn("Failed to generate item model for [" + key + "] because it conflicts with vanilla item"); + .resolve(itemModelPath.value() + ".json"); + + boolean modelExists = Files.exists(itemPath); + JsonObject itemJson; + if (modelExists) { + // 路径已经存在了,那么就应该把模型读入 + try { + itemJson = GsonHelper.readJsonFile(itemPath).getAsJsonObject(); + // 野心真大,已经自己写了overrides,那么不管你了 + if (itemJson.has("overrides")) { + continue; + } + JsonArray overrides = new JsonArray(); + for (LegacyOverridesModel legacyOverridesModel : legacyOverridesModels) { + overrides.add(legacyOverridesModel.toLegacyPredicateElement()); + } + itemJson.add("overrides", overrides); + } catch (IOException e) { + this.plugin.logger().warn("Failed to read item json " + itemPath.toAbsolutePath()); continue; } + } else { + // 如果路径不存在,则需要我们创建一个json对象,并对接model的路径 + itemJson = new JsonObject(); + LegacyOverridesModel firstModel = legacyOverridesModels.getFirst(); + itemJson.addProperty("parent", firstModel.model()); + JsonArray overrides = new JsonArray(); + for (LegacyOverridesModel legacyOverridesModel : legacyOverridesModels) { + overrides.add(legacyOverridesModel.toLegacyPredicateElement()); + } + itemJson.add("overrides", overrides); } try { Files.createDirectories(itemPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + itemPath.toAbsolutePath()); + plugin.logger().severe("Error creating " + itemPath.toAbsolutePath(), e); continue; } try (BufferedWriter writer = Files.newBufferedWriter(itemPath)) { - GsonHelper.get().toJson(jsonObject, writer); + GsonHelper.get().toJson(itemJson, writer); } catch (IOException e) { - plugin.logger().warn("Failed to save item model for [" + key + "]"); + plugin.logger().warn("Failed to save item model for " + itemModelPath, e); } } - - // 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) { if (Config.packMaxVersion() < 21.39f) return; - for (Map.Entry entry : plugin.itemManager().modernItemModels1_21_4().entrySet()) { + for (Map.Entry entry : this.plugin.itemManager().modernItemModels1_21_4().entrySet()) { Key key = entry.getKey(); Path itemPath = generatedPackPath .resolve("assets") .resolve(key.namespace()) .resolve("items") .resolve(key.value() + ".json"); + + if (PRESET_ITEMS.containsKey(key)) { + TranslationManager.instance().log("warning.config.resource_pack.item_model.conflict.vanilla", key.asString()); + continue; + } if (Files.exists(itemPath)) { - plugin.logger().warn("Failed to generate item model for [" + key + "] because " + itemPath.toAbsolutePath() + " already exists"); - } else { - if (PRESET_ITEMS.containsKey(key)) { - plugin.logger().warn("Failed to generate item model for [" + key + "] because it conflicts with vanilla item"); - continue; - } + TranslationManager.instance().log("warning.config.resource_pack.item_model.already_exist", key.asString(), itemPath.toAbsolutePath().toString()); + continue; } try { Files.createDirectories(itemPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + itemPath.toAbsolutePath()); + this.plugin.logger().severe("Error creating " + itemPath.toAbsolutePath(), e); continue; } JsonObject model = new JsonObject(); @@ -991,40 +1067,39 @@ public abstract class AbstractPackManager implements PackManager { try (BufferedWriter writer = Files.newBufferedWriter(itemPath)) { GsonHelper.get().toJson(model, writer); } catch (IOException e) { - plugin.logger().warn("Failed to save item model for [" + key + "]"); + this.plugin.logger().warn("Failed to save item model for " + key, e); } } } private void generateModernItemOverrides(Path generatedPackPath) { if (Config.packMaxVersion() < 21.39f) return; - for (Map.Entry> entry : plugin.itemManager().modernItemOverrides().entrySet()) { - Key key = entry.getKey(); + for (Map.Entry> entry : this.plugin.itemManager().modernItemOverrides().entrySet()) { + Key vanillaItemModel = entry.getKey(); Path overridedItemPath = generatedPackPath .resolve("assets") - .resolve(key.namespace()) + .resolve(vanillaItemModel.namespace()) .resolve("items") - .resolve(key.value() + ".json"); + .resolve(vanillaItemModel.value() + ".json"); JsonObject originalItemModel; if (Files.exists(overridedItemPath)) { try { originalItemModel = GsonHelper.readJsonFile(overridedItemPath).getAsJsonObject(); } catch (IOException e) { - plugin.logger().warn("Failed to load existing item model (modern)", e); + this.plugin.logger().warn("Failed to load existing item model (modern)", e); continue; } } else { - originalItemModel = PRESET_ITEMS.get(key); + originalItemModel = PRESET_ITEMS.get(vanillaItemModel); if (originalItemModel == null) { - plugin.logger().warn("Failed to load existing item model for [" + key + "] (modern)"); + this.plugin.logger().warn("Failed to load existing item model for " + vanillaItemModel + " (modern)"); continue; } } boolean handAnimationOnSwap = Optional.ofNullable(originalItemModel.get("hand_animation_on_swap")).map(JsonElement::getAsBoolean).orElse(true); JsonObject fallbackModel = originalItemModel.get("model").getAsJsonObject(); - JsonObject newJson = new JsonObject(); JsonObject model = new JsonObject(); newJson.add("model", model); @@ -1033,6 +1108,7 @@ public abstract class AbstractPackManager implements PackManager { if (!handAnimationOnSwap) { model.addProperty("hand_animation_on_swap", false); } + // 将原有的json读成fallback model.add("fallback", fallbackModel); JsonArray entries = new JsonArray(); model.add("entries", entries); @@ -1045,42 +1121,43 @@ public abstract class AbstractPackManager implements PackManager { try { Files.createDirectories(overridedItemPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + overridedItemPath.toAbsolutePath()); + this.plugin.logger().severe("Error creating " + overridedItemPath.toAbsolutePath(), e); continue; } try (BufferedWriter writer = Files.newBufferedWriter(overridedItemPath)) { GsonHelper.get().toJson(newJson, writer); } catch (IOException e) { - plugin.logger().warn("Failed to save item model for [" + key + "]"); + this.plugin.logger().warn("Failed to save item model for " + vanillaItemModel, e); } } } private void generateLegacyItemOverrides(Path generatedPackPath) { if (Config.packMinVersion() > 21.39f) return; - for (Map.Entry> entry : plugin.itemManager().legacyItemOverrides().entrySet()) { - Key key = entry.getKey(); + for (Map.Entry> entry : this.plugin.itemManager().legacyItemOverrides().entrySet()) { + Key vanillaLegacyModel = entry.getKey(); Path overridedItemPath = generatedPackPath .resolve("assets") - .resolve(key.namespace()) + .resolve(vanillaLegacyModel.namespace()) .resolve("models") .resolve("item") - .resolve(key.value() + ".json"); + .resolve(vanillaLegacyModel.value() + ".json"); JsonObject originalItemModel; if (Files.exists(overridedItemPath)) { try (BufferedReader reader = Files.newBufferedReader(overridedItemPath)) { originalItemModel = JsonParser.parseReader(reader).getAsJsonObject(); } catch (IOException e) { - plugin.logger().warn("Failed to load existing item model (legacy)", e); + this.plugin.logger().warn("Failed to load existing item model (legacy)", e); continue; } } else { - originalItemModel = PRESET_LEGACY_MODELS_ITEM.get(key); - } - if (originalItemModel == null) { - plugin.logger().warn("Failed to load item model for [" + key + "] (legacy)"); - continue; + originalItemModel = PRESET_LEGACY_MODELS_ITEM.get(vanillaLegacyModel); + if (originalItemModel == null) { + this.plugin.logger().warn("Failed to load item model for " + vanillaLegacyModel + " (legacy)"); + continue; + } + originalItemModel = originalItemModel.deepCopy(); } JsonArray overrides; if (originalItemModel.has("overrides")) { @@ -1096,21 +1173,20 @@ public abstract class AbstractPackManager implements PackManager { try { Files.createDirectories(overridedItemPath.getParent()); } catch (IOException e) { - plugin.logger().severe("Error creating " + overridedItemPath.toAbsolutePath()); + plugin.logger().severe("Error creating " + overridedItemPath.toAbsolutePath(), e); continue; } - try (BufferedWriter writer = Files.newBufferedWriter(overridedItemPath)) { GsonHelper.get().toJson(originalItemModel, writer); } catch (IOException e) { - plugin.logger().warn("Failed to save item model for [" + key + "]"); + plugin.logger().warn("Failed to save item model for " + vanillaLegacyModel, e); } } } private void generateFonts(Path generatedPackPath) { // generate image font json - for (Font font : plugin.fontManager().fonts()) { + for (Font font : this.plugin.fontManager().fonts()) { Key namespacedKey = font.key(); Path fontPath = generatedPackPath.resolve("assets") .resolve(namespacedKey.namespace()) @@ -1124,14 +1200,14 @@ public abstract class AbstractPackManager implements PackManager { fontJson = JsonParser.parseString(content).getAsJsonObject(); } catch (IOException e) { fontJson = new JsonObject(); - plugin.logger().warn(fontPath + " is not a valid font json file"); + this.plugin.logger().warn(fontPath + " is not a valid font json file"); } } else { fontJson = new JsonObject(); try { Files.createDirectories(fontPath.getParent()); } catch (IOException e) { - throw new RuntimeException(e); + this.plugin.logger().severe("Error creating " + fontPath.toAbsolutePath(), e); } } @@ -1147,10 +1223,10 @@ public abstract class AbstractPackManager implements PackManager { providers.add(image.get()); } - try (FileWriter fileWriter = new FileWriter(fontPath.toFile())) { - fileWriter.write(fontJson.toString().replace("\\\\u", "\\u")); + try { + Files.writeString(fontPath, fontJson.toString().replace("\\\\u", "\\u")); } catch (IOException e) { - throw new RuntimeException(e); + this.plugin.logger().severe("Error writing font to " + fontPath.toAbsolutePath(), e); } } @@ -1172,44 +1248,4 @@ public abstract class AbstractPackManager implements PackManager { } } } - - private List>> mergeFolder(Collection sourceFolders, Path targetFolder) throws IOException { - Map> conflictChecker = new HashMap<>(); - for (Path sourceFolder : sourceFolders) { - if (Files.exists(sourceFolder)) { - Files.walkFileTree(sourceFolder, new SimpleFileVisitor<>() { - @Override - public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { - Path relative = sourceFolder.relativize(file); - Path targetPath = targetFolder.resolve(relative); - List conflicts = conflictChecker.computeIfAbsent(relative, k -> new ArrayList<>()); - if (conflicts.isEmpty()) { - Files.createDirectories(targetPath.getParent()); - Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING); - conflicts.add(file); - } else { - PathContext relativeCTX = PathContext.of(relative); - PathContext targetCTX = PathContext.of(targetPath); - PathContext fileCTX = PathContext.of(file); - for (ResolutionConditional resolution : Config.resolutions()) { - if (resolution.matcher().test(relativeCTX)) { - resolution.resolution().run(targetCTX, fileCTX); - return FileVisitResult.CONTINUE; - } - } - conflicts.add(file); - } - return FileVisitResult.CONTINUE; - } - }); - } - } - List>> conflicts = new ArrayList<>(); - for (Map.Entry> entry : conflictChecker.entrySet()) { - if (entry.getValue().size() > 1) { - conflicts.add(Pair.of(entry.getKey(), entry.getValue())); - } - } - return conflicts; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java new file mode 100644 index 000000000..f86687e40 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.pack; + +public class CachedAssetFile { + private final byte[] data; + private final long lastModified; + private final long size; + + public CachedAssetFile(byte[] data, long lastModified, long size) { + this.data = data; + this.lastModified = lastModified; + this.size = size; + } + + public byte[] data() { + return data; + } + + public long lastModified() { + return lastModified; + } + + public long size() { + return size; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java new file mode 100644 index 000000000..f4cb38fd5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.pack; + +import java.util.Map; + +public class CachedConfigFile { + private final Map config; + private final long lastModified; + private final long size; + private final Pack pack; + + public CachedConfigFile(Map config, Pack pack, long lastModified, long size) { + this.config = config; + this.size = size; + this.lastModified = lastModified; + this.pack = pack; + } + + public Pack pack() { + return pack; + } + + public Map config() { + return config; + } + + public long lastModified() { + return lastModified; + } + + public long size() { + return size; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java similarity index 81% rename from core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java index e98faf8f8..e8016ce2a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java @@ -3,13 +3,13 @@ package net.momirealms.craftengine.core.pack; import java.nio.file.Path; import java.util.Map; -public class CachedConfig { +public class CachedConfigSection { private final Pack pack; private final Path filePath; private final String prefix; private final Map config; - public CachedConfig(String prefix, Map config, Path filePath, Pack pack) { + public CachedConfigSection(String prefix, Map config, Path filePath, Pack pack) { this.config = config; this.filePath = filePath; this.pack = pack; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java index 104508c8c..b684aa54f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java @@ -2,17 +2,18 @@ package net.momirealms.craftengine.core.pack; public class LoadingSequence { public static final int TEMPLATE = 0; - public static final int LANG = 10; - public static final int TRANSLATION = 20; - public static final int BLOCK = 30; - public static final int ITEM = 40; - public static final int FURNITURE = 50; - public static final int IMAGE = 60; - public static final int RECIPE = 70; - public static final int CATEGORY = 80; - public static final int SOUND = 90; - public static final int JUKEBOX_SONG = 100; - public static final int VANILLA_LOOTS = 110; - public static final int EMOJI = 120; - public static final int ADVANCEMENT = 130; + public static final int GLOBAL_VAR = 10; + public static final int LANG = 20; + public static final int TRANSLATION = 30; + public static final int BLOCK = 40; + public static final int ITEM = 50; + public static final int FURNITURE = 60; + public static final int IMAGE = 70; + public static final int RECIPE = 80; + public static final int CATEGORY = 90; + public static final int SOUND = 100; + public static final int JUKEBOX_SONG = 110; + public static final int VANILLA_LOOTS = 120; + public static final int EMOJI = 130; + public static final int ADVANCEMENT = 140; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java index 0734ded69..839de5886 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java @@ -3,9 +3,10 @@ package net.momirealms.craftengine.core.pack; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import org.jetbrains.annotations.NotNull; +import java.io.IOException; import java.nio.file.Path; import java.util.Collection; @@ -13,26 +14,28 @@ public interface PackManager extends Manageable { void loadResources(boolean recipe); + void initCachedAssets(); + @NotNull Collection loadedPacks(); - boolean registerConfigSectionParser(ConfigSectionParser parser); + boolean registerConfigSectionParser(ConfigParser parser); - default void registerConfigSectionParsers(ConfigSectionParser[] parsers) { - for (ConfigSectionParser parser : parsers) { + default void registerConfigSectionParsers(ConfigParser[] parsers) { + for (ConfigParser parser : parsers) { registerConfigSectionParser(parser); } } boolean unregisterConfigSectionParser(String id); - default void unregisterConfigSectionParser(ConfigSectionParser parser) { + default void unregisterConfigSectionParser(ConfigParser parser) { for (String id : parser.sectionId()) { unregisterConfigSectionParser(id); } } - void generateResourcePack(); + void generateResourcePack() throws IOException; Path resourcePackPath(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java index 4b2609c3b..1756b6b14 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java @@ -77,7 +77,7 @@ public class BaseItemModel implements ItemModel { Map generation = MiscUtils.castToMap(arguments.get("generation"), true); ModelGeneration modelGeneration = null; if (generation != null) { - modelGeneration = new ModelGeneration(Key.of(modelPath), generation); + modelGeneration = ModelGeneration.of(Key.of(modelPath), generation); } if (arguments.containsKey("tints")) { List tints = new ArrayList<>(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java index 99f125240..50052ce85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java @@ -1,24 +1,29 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.special.SpecialModel; import net.momirealms.craftengine.core.pack.model.special.SpecialModels; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; -import java.util.Objects; public class SpecialItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); private final SpecialModel specialModel; private final String base; + private final ModelGeneration modelGeneration; - public SpecialItemModel(SpecialModel specialModel, String base) { + public SpecialItemModel(SpecialModel specialModel, String base, @Nullable ModelGeneration generation) { this.specialModel = specialModel; this.base = base; + this.modelGeneration = generation; } public SpecialModel specialModel() { @@ -45,16 +50,28 @@ public class SpecialItemModel implements ItemModel { @Override public List modelsToGenerate() { - return List.of(); + if (this.modelGeneration == null) { + return List.of(); + } else { + return List.of(this.modelGeneration); + } } public static class Factory implements ItemModelFactory { @Override public ItemModel create(Map arguments) { - String base = Objects.requireNonNull(arguments.get("base")).toString(); + String base = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "base", "path"), "warning.config.item.model.special.missing_path"); + if (!ResourceLocation.isValid(base)) { + throw new LocalizedResourceConfigException("warning.config.item.model.special.invalid_path", base); + } + Map generation = MiscUtils.castToMap(arguments.get("generation"), true); + ModelGeneration modelGeneration = null; + if (generation != null) { + modelGeneration = ModelGeneration.of(Key.of(base), generation); + } Map model = MiscUtils.castToMap(arguments.get("model"), false); - return new SpecialItemModel(SpecialModels.fromMap(model), base); + return new SpecialItemModel(SpecialModels.fromMap(model), base, modelGeneration); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java index e34e9f374..d62e049f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/AbstractModelGenerator.java @@ -38,9 +38,14 @@ public abstract class AbstractModelGenerator implements ModelGenerator { if (!ResourceLocation.isValid(model.parentModelPath())) { throw new LocalizedResourceConfigException("warning.config.model.generation.parent.invalid", model.parentModelPath()); } - for (Map.Entry texture : model.texturesOverride().entrySet()) { - if (!ResourceLocation.isValid(texture.getValue())) { - throw new LocalizedResourceConfigException("warning.config.model.generation.texture.invalid", texture.getKey(), texture.getValue()); + Map textures = model.texturesOverride(); + if (textures != null) { + for (Map.Entry texture : textures.entrySet()) { + if (texture.getValue().charAt(0) != '#') { + if (!ResourceLocation.isValid(texture.getValue())) { + throw new LocalizedResourceConfigException("warning.config.model.generation.texture.invalid", texture.getKey(), texture.getValue()); + } + } } } this.modelsToGenerate.put(model.path(), model); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/GuiLight.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/GuiLight.java new file mode 100644 index 000000000..0977aa82a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/GuiLight.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.pack.model.generation; + +public enum GuiLight { + FRONT, SIDE +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java index 086d8c6d5..883d89196 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/ModelGeneration.java @@ -1,44 +1,107 @@ package net.momirealms.craftengine.core.pack.model.generation; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.model.generation.display.DisplayMeta; +import net.momirealms.craftengine.core.pack.model.generation.display.DisplayPosition; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.EnumUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Supplier; -public class ModelGeneration { +public class ModelGeneration implements Supplier { + private static final Map> BUILDER_FUNCTIONS = new HashMap<>(); + static { + BUILDER_FUNCTIONS.put("textures", (b, data) -> { + Map texturesMap = MiscUtils.castToMap(data, false); + Map texturesOverride = new LinkedHashMap<>(); + for (Map.Entry entry : texturesMap.entrySet()) { + if (entry.getValue() instanceof String p) { + texturesOverride.put(entry.getKey(), p); + } + } + b.texturesOverride(texturesOverride); + }); + BUILDER_FUNCTIONS.put("display", (b, data) -> { + Map displayMap = MiscUtils.castToMap(data, false); + Map displays = new EnumMap<>(DisplayPosition.class); + for (Map.Entry entry : displayMap.entrySet()) { + try { + DisplayPosition displayPosition = DisplayPosition.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); + if (entry.getValue() instanceof Map metaMap) { + displays.put(displayPosition, DisplayMeta.fromMap(MiscUtils.castToMap(metaMap, false))); + } + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.model.generation.invalid_display_position", e, entry.getKey(), EnumUtils.toString(DisplayPosition.values())); + } + } + b.displays(displays); + }); + BUILDER_FUNCTIONS.put("gui-light", (b, data) -> { + String guiLightStr = String.valueOf(data); + try { + GuiLight guiLight = GuiLight.valueOf(guiLightStr.toUpperCase(Locale.ENGLISH)); + b.guiLight(guiLight); + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.model.generation.invalid_gui_light", e, guiLightStr, EnumUtils.toString(GuiLight.values())); + } + }); + BUILDER_FUNCTIONS.put("ambient-occlusion", (b, data) -> { + b.ambientOcclusion((boolean) data); + }); + BUILDER_FUNCTIONS.put("parent", (b, data) -> { + String parentModelPath = data.toString(); + b.parentModelPath(parentModelPath); + }); + } + + @NotNull private final Key path; + @NotNull private final String parentModelPath; + @Nullable private final Map texturesOverride; + @Nullable + private final Map displays; + @Nullable + private final GuiLight guiLight; + @Nullable + private final Boolean ambientOcclusion; + @Nullable + private JsonObject cachedModel; - public ModelGeneration(Key path, String parentModelPath, Map texturesOverride) { + public ModelGeneration(@NotNull Key path, + @NotNull String parentModelPath, + @Nullable Map texturesOverride, + @Nullable Map displays, + @Nullable GuiLight guiLight, + @Nullable Boolean ambientOcclusion) { this.path = path; this.parentModelPath = parentModelPath; this.texturesOverride = texturesOverride; + this.displays = displays; + this.guiLight = guiLight; + this.ambientOcclusion = ambientOcclusion; } - public ModelGeneration(Key path, Map map) { - this.path = path; - Object parent = map.get("parent"); - if (parent == null) { - throw new LocalizedResourceConfigException("warning.config.model.generation.missing_parent"); - } - this.parentModelPath = parent.toString(); - Map texturesMap = MiscUtils.castToMap(map.get("textures"), true); - if (texturesMap != null) { - this.texturesOverride = new LinkedHashMap<>(); - for (Map.Entry entry : texturesMap.entrySet()) { - if (entry.getValue() instanceof String p) { - this.texturesOverride.put(entry.getKey(), p); - } - } - } else { - this.texturesOverride = Collections.emptyMap(); + public static ModelGeneration of(Key path, Map map) { + Builder builder = builder().path(path); + for (Map.Entry entry : map.entrySet()) { + Optional.ofNullable(BUILDER_FUNCTIONS.get(entry.getKey())).ifPresent(it -> it.accept(builder, entry.getValue())); } + return builder.build(); + } + + @Nullable + public Map texturesOverride() { + return texturesOverride; } public Key path() { @@ -49,36 +112,137 @@ public class ModelGeneration { return parentModelPath; } - public Map texturesOverride() { - return texturesOverride; + @Nullable + public Map displays() { + return displays; } - public JsonObject getJson() { + @Nullable + public GuiLight guiLight() { + return guiLight; + } + + @Nullable + public Boolean ambientOcclusion() { + return ambientOcclusion; + } + + @Override + public JsonObject get() { + if (this.cachedModel == null) { + this.cachedModel = this.getCachedModel(); + } + return this.cachedModel; + } + + private JsonObject getCachedModel() { JsonObject model = new JsonObject(); - model.addProperty("parent", parentModelPath); - if (this.texturesOverride != null && !this.texturesOverride.isEmpty()) { + model.addProperty("parent", this.parentModelPath); + if (this.texturesOverride != null) { JsonObject textures = new JsonObject(); for (Map.Entry entry : this.texturesOverride.entrySet()) { textures.addProperty(entry.getKey(), entry.getValue()); } model.add("textures", textures); } + if (this.displays != null) { + JsonObject displays = new JsonObject(); + for (Map.Entry entry : this.displays.entrySet()) { + JsonObject displayMetadata = new JsonObject(); + DisplayMeta meta = entry.getValue(); + if (meta.rotation() != null) + displayMetadata.add("rotation", vectorToJsonArray(meta.rotation())); + if (meta.translation() != null) + displayMetadata.add("translation", vectorToJsonArray(meta.translation())); + if (meta.scale() != null) + displayMetadata.add("scale", vectorToJsonArray(meta.scale())); + displays.add(entry.getKey().name().toLowerCase(Locale.ENGLISH), displayMetadata); + } + model.add("display", displays); + } + if (this.guiLight != null) { + model.addProperty("gui_light", this.guiLight.name().toLowerCase(Locale.ENGLISH)); + } return model; } + private JsonArray vectorToJsonArray(Vector3f vector) { + JsonArray array = new JsonArray(); + array.add(vector.x()); + array.add(vector.y()); + array.add(vector.z()); + return array; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ModelGeneration that = (ModelGeneration) o; - return this.path.equals(that.path) && parentModelPath.equals(that.parentModelPath) && Objects.equals(texturesOverride, that.texturesOverride); + return this.path.equals(that.path) && parentModelPath.equals(that.parentModelPath) && Objects.equals(texturesOverride, that.texturesOverride) + && Objects.equals(displays, that.displays) && Objects.equals(ambientOcclusion, that.ambientOcclusion) && Objects.equals(guiLight, that.guiLight); } @Override public int hashCode() { - int result = path.hashCode(); - result = 31 * result + parentModelPath.hashCode(); - result = 31 * result + texturesOverride.hashCode(); + int result = this.path.hashCode(); + result = 31 * result + this.parentModelPath.hashCode(); return result; } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Key path; + private String parentModelPath; + @Nullable + private Map texturesOverride; + @Nullable + private Map displays; + @Nullable + private GuiLight guiLight; + @Nullable + private Boolean ambientOcclusion; + + public Builder() {} + + public Builder path(Key key) { + this.path = key; + return this; + } + + public Builder parentModelPath(String parentModelPath) { + this.parentModelPath = parentModelPath; + return this; + } + + public Builder texturesOverride(Map texturesOverride) { + this.texturesOverride = texturesOverride; + return this; + } + + public Builder displays(Map displays) { + this.displays = displays; + return this; + } + + public Builder guiLight(GuiLight guiLight) { + this.guiLight = guiLight; + return this; + } + + public Builder ambientOcclusion(Boolean ambientOcclusion) { + this.ambientOcclusion = ambientOcclusion; + return this; + } + + public ModelGeneration build() { + if (this.parentModelPath == null) { + throw new LocalizedResourceConfigException("warning.config.model.generation.missing_parent"); + } + return new ModelGeneration(Objects.requireNonNull(this.path, "path should be nonnull"), this.parentModelPath, this.texturesOverride, this.displays, this.guiLight, this.ambientOcclusion); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java new file mode 100644 index 000000000..31549e547 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.pack.model.generation.display; + +import net.momirealms.craftengine.core.util.MiscUtils; +import org.joml.Vector3f; + +import java.util.Map; + +public record DisplayMeta(Vector3f rotation, Vector3f translation, Vector3f scale) { + + public static DisplayMeta fromMap(Map map) { + Vector3f rotation = null; + if (map.containsKey("rotation")) { + rotation = MiscUtils.getAsVector3f(map.get("rotation"), "rotation"); + } + Vector3f translation = null; + if (map.containsKey("translation")) { + translation = MiscUtils.getAsVector3f(map.get("translation"), "translation"); + } + Vector3f scale = null; + if (map.containsKey("scale")) { + scale = MiscUtils.getAsVector3f(map.get("scale"), "scale"); + } + return new DisplayMeta(rotation, translation, scale); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayPosition.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayPosition.java new file mode 100644 index 000000000..0f5f30a3c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayPosition.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.pack.model.generation.display; + +public enum DisplayPosition { + THIRDPERSON_RIGHTHAND, + THIRDPERSON_LEFTHAND, + FIRSTPERSON_RIGHTHAND, + FIRSTPERSON_LEFTHAND, + GUI, + HEAD, + GROUND, + FIXED +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java index 31aaf4563..a4cdce121 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java @@ -27,7 +27,9 @@ public class ChestSpecialModel implements SpecialModel { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("texture", texture); - json.addProperty("openness", openness); + if (openness > 0) { + json.addProperty("openness", openness); + } return json; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java index 6f1cf530c..7ee58315d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; +import java.util.Optional; public class HeadSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -28,8 +29,12 @@ public class HeadSpecialModel implements SpecialModel { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("kind", kind); - json.addProperty("texture", texture); - json.addProperty("animation", animation); + if (texture != null) { + json.addProperty("texture", texture); + } + if (animation != 0) { + json.addProperty("animation", animation); + } return json; } @@ -38,7 +43,7 @@ public class HeadSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { String kind = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("kind"), "warning.config.item.model.special.head.missing_kind"); - String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.head.missing_texture"); + String texture = Optional.ofNullable(arguments.get("texture")).map(String::valueOf).orElse(null); int animation = ResourceConfigUtils.getAsInt(arguments.getOrDefault("animation", 0), "animation"); return new HeadSpecialModel(kind, texture, animation); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java index 0eaf640a6..917474670 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java @@ -5,9 +5,11 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; import java.util.Locale; import java.util.Map; +import java.util.Optional; public class ShulkerBoxSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); @@ -15,7 +17,7 @@ public class ShulkerBoxSpecialModel implements SpecialModel { private final float openness; private final Direction orientation; - public ShulkerBoxSpecialModel(String texture, float openness, Direction orientation) { + public ShulkerBoxSpecialModel(String texture, float openness, @Nullable Direction orientation) { this.texture = texture; this.openness = openness; this.orientation = orientation; @@ -31,8 +33,10 @@ public class ShulkerBoxSpecialModel implements SpecialModel { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("texture", texture); + if (orientation != null) { + json.addProperty("orientation", orientation.name().toLowerCase(Locale.ENGLISH)); + } json.addProperty("openness", openness); - json.addProperty("orientation", orientation.name().toLowerCase(Locale.ENGLISH)); return json; } @@ -42,7 +46,7 @@ public class ShulkerBoxSpecialModel implements SpecialModel { public SpecialModel create(Map arguments) { float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.shulker_box.missing_texture"); - Direction orientation = Direction.valueOf(arguments.getOrDefault("orientation", "down").toString().toUpperCase(Locale.ENGLISH)); + Direction orientation = Optional.ofNullable(arguments.get("orientation")).map(String::valueOf).map(String::toUpperCase).map(Direction::valueOf).orElse(null); if (openness > 1 || openness < 0) { throw new LocalizedResourceConfigException("warning.config.item.model.special.shulker_box.invalid_openness", String.valueOf(openness)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java index e1b926ae6..54c6d40c2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java @@ -10,9 +10,9 @@ import java.util.Map; public class ConstantTint implements Tint { public static final Factory FACTORY = new Factory(); - private final Either> value; + private final Either> value; - public ConstantTint(Either> value) { + public ConstantTint(Either> value) { this.value = value; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java index c76be9f9e..0ed576384 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java @@ -10,10 +10,10 @@ import java.util.Map; public class CustomModelDataTint implements Tint { public static final Factory FACTORY = new Factory(); - private final Either> value; + private final Either> value; private final int index; - public CustomModelDataTint(Either> value, int index) { + public CustomModelDataTint(Either> value, int index) { this.index = index; this.value = value; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java index a08c0daec..a543bcfe9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java @@ -9,10 +9,10 @@ import java.util.Map; public class SimpleDefaultTint implements Tint { public static final Factory FACTORY = new Factory(); - private final Either> value; + private final Either> value; private final Key type; - public SimpleDefaultTint(Either> value, Key type) { + public SimpleDefaultTint(Either> value, Key type) { this.value = value; this.type = type; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java index 364145257..b0be23502 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java @@ -12,16 +12,16 @@ public interface Tint extends Supplier { Key type(); - default void applyAnyTint(JsonObject json, Either> value, String key) { + default void applyAnyTint(JsonObject json, Either> value, String key) { if (value.primary().isPresent()) { json.addProperty(key, value.primary().get()); } else { - List list = value.fallback().get(); + List list = value.fallback().get(); if (list.size() != 3) { throw new RuntimeException("Invalid tint value list size: " + list.size() + " which is expected to be 3"); } JsonArray array = new JsonArray(); - for (int i : list) { + for (float i : list) { array.add(i); } json.add(key, array); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java index ee595ad49..e0fbd1947 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintFactory.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.pack.model.tint; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.MiscUtils; import org.incendo.cloud.type.Either; import java.util.ArrayList; @@ -11,27 +12,51 @@ public interface TintFactory { Tint create(Map arguments); - default Either> parseTintValue(Object value) { + default Either> parseTintValue(Object value) { if (value instanceof Number i) { return Either.ofPrimary(i.intValue()); } else if (value instanceof List list) { if (list.size() == 3) { - List intList = new ArrayList<>(); - for (Object o : list) { - intList.add(Integer.parseInt(o.toString())); + List strList = MiscUtils.getAsStringList(list); + boolean hasDot = false; + for (String str : strList) { + if (str.contains(".")) { + hasDot = true; + break; + } } - return Either.ofFallback(intList); + List fList = new ArrayList<>(); + for (String str : strList) { + if (hasDot) { + fList.add(Float.parseFloat(str)); + } else { + fList.add(convertToFloat(Integer.parseInt(str))); + } + } + return Either.ofFallback(fList); } } else if (value instanceof String s) { String[] split = s.split(","); if (split.length == 3) { - List intList = new ArrayList<>(); + List fList = new ArrayList<>(); + boolean hasDot = s.contains("."); for (String string : split) { - intList.add(Integer.parseInt(string)); + if (hasDot) { + fList.add(Float.parseFloat(string)); + } else { + fList.add(convertToFloat(Integer.parseInt(string))); + } } - return Either.ofFallback(intList); + return Either.ofFallback(fList); } } throw new LocalizedResourceConfigException("warning.config.item.model.tint.invalid_value", value.toString()); } + + static float convertToFloat(int value) { + if (value < 0 || value > 255) { + throw new IllegalArgumentException("Tint value out of range: " + value + ". Allowed range is [0,255]"); + } + return value / 255.0f; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CompatibilityManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CompatibilityManager.java index b6f1d0794..7749be015 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CompatibilityManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CompatibilityManager.java @@ -27,5 +27,7 @@ public interface CompatibilityManager { String parse(Player player, String text); + String parse(Player player1, Player player2, String text); + int getPlayerProtocolVersion(UUID uuid); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 64fa38f73..53f8b5da5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.plugin; import net.momirealms.craftengine.core.advancement.AdvancementManager; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; +import net.momirealms.craftengine.core.entity.projectile.ProjectileManager; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; @@ -14,6 +15,7 @@ import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; import net.momirealms.craftengine.core.plugin.config.template.TemplateManagerImpl; +import net.momirealms.craftengine.core.plugin.context.GlobalVariableManager; import net.momirealms.craftengine.core.plugin.dependency.Dependencies; import net.momirealms.craftengine.core.plugin.dependency.Dependency; import net.momirealms.craftengine.core.plugin.dependency.DependencyManager; @@ -68,6 +70,8 @@ public abstract class CraftEngine implements Plugin { protected VanillaLootManager vanillaLootManager; protected AdvancementManager advancementManager; protected CompatibilityManager compatibilityManager; + protected GlobalVariableManager globalVariableManager; + protected ProjectileManager projectileManager; private final Consumer reloadEventDispatcher; private boolean isReloading; @@ -133,6 +137,7 @@ public abstract class CraftEngine implements Plugin { this.translationManager.reload(); // clear the outdated cache by reloading the managers this.templateManager.reload(); + this.globalVariableManager.reload(); this.furnitureManager.reload(); this.fontManager.reload(); this.itemManager.reload(); @@ -144,6 +149,7 @@ public abstract class CraftEngine implements Plugin { this.guiManager.reload(); this.packManager.reload(); this.advancementManager.reload(); + this.projectileManager.reload(); if (reloadRecipe) { this.recipeManager.reload(); } @@ -198,6 +204,7 @@ public abstract class CraftEngine implements Plugin { this.isInitializing = true; this.networkManager.init(); this.templateManager = new TemplateManagerImpl(); + this.globalVariableManager = new GlobalVariableManager(); this.itemBrowserManager = new ItemBrowserManagerImpl(this); this.commandManager.registerDefaultFeatures(); // delay the reload so other plugins can register some custom parsers @@ -214,6 +221,7 @@ public abstract class CraftEngine implements Plugin { this.fontManager.delayedInit(); this.vanillaLootManager.delayedInit(); this.advancementManager.delayedInit(); + this.projectileManager.delayedInit(); // reload the plugin try { this.reloadPlugin(Runnable::run, Runnable::run, true); @@ -226,6 +234,7 @@ public abstract class CraftEngine implements Plugin { // set up some platform extra tasks this.platformDelayedEnable(); this.isInitializing = false; + this.scheduler.executeAsync(() -> this.packManager.initCachedAssets()); }); } @@ -245,6 +254,8 @@ public abstract class CraftEngine implements Plugin { if (this.soundManager != null) this.soundManager.disable(); if (this.vanillaLootManager != null) this.vanillaLootManager.disable(); if (this.translationManager != null) this.translationManager.disable(); + if (this.globalVariableManager != null) this.globalVariableManager.disable(); + if (this.projectileManager != null) this.projectileManager.disable(); if (this.scheduler != null) this.scheduler.shutdownScheduler(); if (this.scheduler != null) this.scheduler.shutdownExecutor(); if (this.commandManager != null) this.commandManager.unregisterFeatures(); @@ -255,6 +266,8 @@ public abstract class CraftEngine implements Plugin { protected void registerDefaultParsers() { // register template parser this.packManager.registerConfigSectionParser(this.templateManager.parser()); + // register global variables parser + this.packManager.registerConfigSectionParser(this.globalVariableManager.parser()); // register font parser this.packManager.registerConfigSectionParsers(this.fontManager.parsers()); // register item parser @@ -297,7 +310,9 @@ public abstract class CraftEngine implements Plugin { Dependencies.TEXT_SERIALIZER_GSON, Dependencies.TEXT_SERIALIZER_GSON_LEGACY, Dependencies.TEXT_SERIALIZER_JSON, Dependencies.AHO_CORASICK, Dependencies.LZ4, - Dependencies.EVALEX + Dependencies.EVALEX, + Dependencies.JIMFS, + Dependencies.COMMONS_IMAGING ); } @@ -430,6 +445,16 @@ public abstract class CraftEngine implements Plugin { return compatibilityManager; } + @Override + public GlobalVariableManager globalVariableManager() { + return globalVariableManager; + } + + @Override + public ProjectileManager projectileManager() { + return projectileManager; + } + @Override public Platform platform() { return platform; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java index 37ef07865..cbb4feb74 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.plugin; import net.momirealms.craftengine.core.advancement.AdvancementManager; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; +import net.momirealms.craftengine.core.entity.projectile.ProjectileManager; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; @@ -12,6 +13,7 @@ import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; +import net.momirealms.craftengine.core.plugin.context.GlobalVariableManager; import net.momirealms.craftengine.core.plugin.dependency.DependencyManager; import net.momirealms.craftengine.core.plugin.gui.GuiManager; import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManager; @@ -91,5 +93,9 @@ public interface Plugin { CompatibilityManager compatibilityManager(); + GlobalVariableManager globalVariableManager(); + + ProjectileManager projectileManager(); + Platform platform(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index fde4746d5..d6faf0675 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -58,6 +58,7 @@ public class Config { protected boolean resource_pack$override_uniform_font; protected List resource_pack$duplicated_files_handler; protected List resource_pack$merge_external_folders; + protected List resource_pack$merge_external_zips; protected boolean resource_pack$protection$crash_tools$method_1; protected boolean resource_pack$protection$crash_tools$method_2; @@ -218,6 +219,7 @@ public class Config { resource_pack$supported_version$min = getVersion(config.get("resource-pack.supported-version.min", "1.20").toString()); resource_pack$supported_version$max = getVersion(config.get("resource-pack.supported-version.max", "LATEST").toString()); resource_pack$merge_external_folders = config.getStringList("resource-pack.merge-external-folders"); + resource_pack$merge_external_zips = config.getStringList("resource-pack.merge-external-zip-files"); resource_pack$delivery$send_on_join = config.getBoolean("resource-pack.delivery.send-on-join", true); resource_pack$delivery$resend_on_upload = config.getBoolean("resource-pack.delivery.resend-on-upload", true); resource_pack$delivery$kick_if_declined = config.getBoolean("resource-pack.delivery.kick-if-declined", true); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java similarity index 52% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java index 0c7f94f94..4d6fae451 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigSectionParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java @@ -8,20 +8,25 @@ import org.jetbrains.annotations.NotNull; import java.nio.file.Path; import java.util.Map; -public interface ConfigSectionParser extends Comparable { +public interface ConfigParser extends Comparable { String[] sectionId(); - void parseSection(Pack pack, Path path, Key id, Map section) throws LocalizedException; + default void parseSection(Pack pack, Path path, Key id, Map section) throws LocalizedException { + this.parseObject(pack, path, id, section); + } + + default void parseObject(Pack pack, Path path, Key id, Object object) throws LocalizedException { + } int loadingSequence(); - default boolean isTemplate() { + default boolean supportsParsingObject() { return false; } @Override - default int compareTo(@NotNull ConfigSectionParser another) { + default int compareTo(@NotNull ConfigParser another) { return Integer.compare(loadingSequence(), another.loadingSequence()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java index b6b2b00be..01f24d80e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java @@ -1,11 +1,8 @@ package net.momirealms.craftengine.core.plugin.config.template; -import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; -import java.nio.file.Path; import java.util.Map; import java.util.regex.Pattern; @@ -17,9 +14,7 @@ public interface TemplateManager extends Manageable { String OVERRIDES = "overrides"; String ARGUMENTS = "arguments"; - ConfigSectionParser parser(); - - void addTemplate(Pack pack, Path path, Key id, Object obj); + ConfigParser parser(); Map applyTemplates(Map input); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 080dfa9ba..8eda9774e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin.config.template; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; @@ -24,7 +24,7 @@ public class TemplateManagerImpl implements TemplateManager { this.templateParser = new TemplateParser(); } - public class TemplateParser implements ConfigSectionParser { + public class TemplateParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"templates", "template"}; @Override @@ -38,13 +38,16 @@ public class TemplateManagerImpl implements TemplateManager { } @Override - public boolean isTemplate() { + public boolean supportsParsingObject() { return true; } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - addTemplate(pack, path, id, section); + public void parseObject(Pack pack, Path path, Key id, Object obj) { + if (templates.containsKey(id)) { + throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString()); + } + templates.put(id, obj); } } @@ -54,18 +57,10 @@ public class TemplateManagerImpl implements TemplateManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.templateParser; } - @Override - public void addTemplate(Pack pack, Path path, Key id, Object obj) { - if (this.templates.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString()); - } - this.templates.put(id, obj); - } - @Override public Map applyTemplates(Map input) { Objects.requireNonNull(input, "Input must not be null"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractAdditionalParameterContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractAdditionalParameterContext.java deleted file mode 100644 index 97a3ff9b9..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractAdditionalParameterContext.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context; - -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameterProvider; - -import java.util.List; -import java.util.Optional; - -public abstract class AbstractAdditionalParameterContext extends AbstractCommonContext { - private final List providers; - - public AbstractAdditionalParameterContext(ContextHolder contexts, List providers) { - super(contexts); - this.providers = providers; - } - - public AbstractAdditionalParameterContext(ContextHolder contexts) { - super(contexts); - this.providers = List.of(new CommonParameterProvider()); - } - - @Override - public Optional getOptionalParameter(ContextKey parameter) { - for (LazyContextParameterProvider provider : providers) { - Optional result = provider.getOptionalParameter(parameter); - if (result.isPresent()) { - return result; - } - } - return super.getOptionalParameter(parameter); - } - - @Override - public T getParameterOrThrow(ContextKey parameter) { - for (LazyContextParameterProvider provider : providers) { - Optional result = provider.getOptionalParameter(parameter); - if (result.isPresent()) { - return result.get(); - } - } - return super.getParameterOrThrow(parameter); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractChainParameterContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractChainParameterContext.java new file mode 100644 index 000000000..9eed4f51b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractChainParameterContext.java @@ -0,0 +1,56 @@ +package net.momirealms.craftengine.core.plugin.context; + +import net.momirealms.craftengine.core.plugin.context.parameter.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public abstract class AbstractChainParameterContext extends AbstractCommonContext { + private static final Map, ChainParameterProvider> CHAIN_PARAMETERS = new HashMap<>(); + static { + CHAIN_PARAMETERS.put(DirectContextParameters.PLAYER, new PlayerParameterProvider()); + CHAIN_PARAMETERS.put(DirectContextParameters.WORLD, new WorldParameterProvider()); + CHAIN_PARAMETERS.put(DirectContextParameters.BLOCK, new BlockParameterProvider()); + CHAIN_PARAMETERS.put(DirectContextParameters.POSITION, new PositionParameterProvider()); + CHAIN_PARAMETERS.put(DirectContextParameters.FURNITURE, new FurnitureParameterProvider()); + CHAIN_PARAMETERS.put(DirectContextParameters.ENTITY, new EntityParameterProvider()); + ItemParameterProvider itemProvider = new ItemParameterProvider(); + CHAIN_PARAMETERS.put(DirectContextParameters.MAIN_HAND_ITEM, itemProvider); + CHAIN_PARAMETERS.put(DirectContextParameters.OFF_HAND_ITEM, itemProvider); + CHAIN_PARAMETERS.put(DirectContextParameters.FURNITURE_ITEM, itemProvider); + CHAIN_PARAMETERS.put(DirectContextParameters.CONSUMED_ITEM, itemProvider); + CHAIN_PARAMETERS.put(DirectContextParameters.ITEM_IN_HAND, itemProvider); + } + + @SuppressWarnings("unchecked") + private static ChainParameterProvider getParameterProvider(final ContextKey key) { + return (ChainParameterProvider) CHAIN_PARAMETERS.get(key); + } + + public AbstractChainParameterContext(ContextHolder contexts) { + super(contexts); + } + + public AbstractChainParameterContext(ContextHolder contexts, + List additionalParameterProviders) { + super(contexts, additionalParameterProviders); + } + + @Override + public Optional getOptionalParameter(ContextKey parameter) { + ContextKey parentKey = parameter.parent(); + if (parentKey == null) { + return super.getOptionalParameter(parameter); + } + if (!CHAIN_PARAMETERS.containsKey(parentKey)) { + return Optional.empty(); + } + Optional parentValue = getOptionalParameter(parentKey); + if (parentValue.isEmpty()) { + return Optional.empty(); + } + return getParameterProvider(parentKey).getOptionalParameter(parameter, parentValue.get()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractCommonContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractCommonContext.java index 4717ae0e0..83e0853a5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractCommonContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AbstractCommonContext.java @@ -4,14 +4,23 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.plugin.text.minimessage.*; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Optional; public abstract class AbstractCommonContext implements Context { protected final ContextHolder contexts; + protected final List additionalParameterProviders; protected TagResolver[] tagResolvers; public AbstractCommonContext(ContextHolder contexts) { this.contexts = contexts; + this.additionalParameterProviders = List.of(new CommonParameterProvider()); + } + + public AbstractCommonContext(ContextHolder contexts, + List additionalParameterProviders) { + this.contexts = contexts; + this.additionalParameterProviders = additionalParameterProviders; } @Override @@ -24,25 +33,21 @@ public abstract class AbstractCommonContext implements Context { public TagResolver[] tagResolvers() { if (this.tagResolvers == null) { this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new I18NTag(this), new NamedArgumentTag(this), - new PlaceholderTag(null), new ExpressionTag(this)}; + new PlaceholderTag(null), new ExpressionTag(this), new GlobalVariableTag(this)}; } return this.tagResolvers; } @Override public Optional getOptionalParameter(ContextKey parameter) { + if (!this.additionalParameterProviders.isEmpty()) { + for (AdditionalParameterProvider additionalParameterProvider : additionalParameterProviders) { + Optional optionalValue = additionalParameterProvider.getOptionalParameter(parameter); + if (optionalValue.isPresent()) { + return optionalValue; + } + } + } return this.contexts.getOptional(parameter); } - - @Override - public T getParameterOrThrow(ContextKey parameter) { - return this.contexts.getOrThrow(parameter); - } - - // It's not designed as mutable -// @Override -// public AbstractCommonContext withParameter(ContextKey parameter, T value) { -// this.contexts.withParameter(parameter, value); -// return this; -// } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AdditionalParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AdditionalParameterProvider.java new file mode 100644 index 000000000..db9aa0cde --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/AdditionalParameterProvider.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.plugin.context; + +import java.util.Optional; + +public interface AdditionalParameterProvider { + + Optional getOptionalParameter(ContextKey parameter); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ChainParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ChainParameterProvider.java new file mode 100644 index 000000000..5c867aac2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ChainParameterProvider.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.plugin.context; + +import java.util.Optional; + +public interface ChainParameterProvider { + + Optional getOptionalParameter(ContextKey parameter, A owner); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java similarity index 67% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameterProvider.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java index de5df90c1..89af9a7b0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java @@ -1,7 +1,6 @@ -package net.momirealms.craftengine.core.plugin.context.parameter; +package net.momirealms.craftengine.core.plugin.context; -import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.plugin.context.LazyContextParameterProvider; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.RandomUtils; import java.util.HashMap; @@ -9,16 +8,16 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -public class CommonParameterProvider implements LazyContextParameterProvider { +public class CommonParameterProvider implements AdditionalParameterProvider { private double lastRandom = -1; private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(CommonParameters.RANDOM, (f) -> { + CONTEXT_FUNCTIONS.put(DirectContextParameters.RANDOM, (f) -> { f.lastRandom = RandomUtils.generateRandomDouble(0,1); return f.lastRandom; }); - CONTEXT_FUNCTIONS.put(CommonParameters.LAST_RANDOM, (f) -> { + CONTEXT_FUNCTIONS.put(DirectContextParameters.LAST_RANDOM, (f) -> { if (f.lastRandom == -1) { f.lastRandom = RandomUtils.generateRandomDouble(0, 1); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/Context.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/Context.java index 989def892..1617c1843 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/Context.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/Context.java @@ -12,5 +12,7 @@ public interface Context { Optional getOptionalParameter(ContextKey parameter); - T getParameterOrThrow(ContextKey parameter); + default T getParameterOrThrow(ContextKey parameter) { + return getOptionalParameter(parameter).orElseThrow(() -> new RuntimeException("No parameter found for " + parameter)); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java index d184c694e..037b53e39 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java @@ -45,7 +45,7 @@ public class ContextHolder { public T getOrThrow(ContextKey parameter) { Supplier object = (Supplier) this.params.get(parameter); if (object == null) { - throw new NoSuchElementException(parameter.id().toString()); + throw new NoSuchElementException(parameter.node()); } else { return object.get(); } @@ -96,7 +96,7 @@ public class ContextHolder { public T getParameterOrThrow(ContextKey parameter) { Supplier object = (Supplier) this.params.get(parameter); if (object == null) { - throw new NoSuchElementException(parameter.id()); + throw new NoSuchElementException(parameter.node()); } else { return object.get(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextKey.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextKey.java index a33dffffd..4271a7c52 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextKey.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextKey.java @@ -1,33 +1,77 @@ package net.momirealms.craftengine.core.plugin.context; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class ContextKey { - private final String id; +public interface ContextKey { - public ContextKey(@NotNull String id) { - this.id = id; + static ContextKey direct(@NotNull String node) { + return new Direct<>(node); + } + + static ContextKey chain(@NotNull String node) { + String[] parts = node.split("\\."); + ContextKey current = null; + for (String part : parts) { + current = new Chain<>(part, current); + } + return current; + } + + @Nullable + default ContextKey parent() { + return null; } @NotNull - public String id() { - return id; + String node(); + + record Direct(String node) implements ContextKey { + public Direct(@NotNull String node) { + this.node = node; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ContextKey another)) return false; + return this.node.equals(another.node()); + } + + @Override + public int hashCode() { + return this.node.hashCode(); + } } - @NotNull - public static ContextKey of(@NotNull String id) { - return new ContextKey<>(id); - } + class Chain implements ContextKey { + private final String node; + private final ContextKey parent; - @Override - public final boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ContextKey that)) return false; - return id.equals(that.id); - } + protected Chain(@NotNull String node, @Nullable ContextKey parent) { + this.node = node; + this.parent = parent; + } - @Override - public int hashCode() { - return id.hashCode(); + @Override + public @NotNull String node() { + return this.node; + } + + @SuppressWarnings("unchecked") + @Override + public ContextKey parent() { + return (ContextKey) this.parent; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ContextKey another)) return false; + return this.node.equals(another.node()); + } + + @Override + public int hashCode() { + return this.node.hashCode(); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java new file mode 100644 index 000000000..5c29100a9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.core.plugin.context; + +import net.momirealms.craftengine.core.pack.LoadingSequence; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.Manageable; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +public class GlobalVariableManager implements Manageable { + private final Map globalVariables = new HashMap<>(); + private final GlobalVariableParser parser = new GlobalVariableParser(); + + @Nullable + public String get(final String key) { + return this.globalVariables.get(key); + } + + @Override + public void unload() { + this.globalVariables.clear(); + } + + public ConfigParser parser() { + return this.parser; + } + + public class GlobalVariableParser implements ConfigParser { + public static final String[] CONFIG_SECTION_NAME = new String[] {"global-variables", "global-variable"}; + + @Override + public int loadingSequence() { + return LoadingSequence.GLOBAL_VAR; + } + + @Override + public String[] sectionId() { + return CONFIG_SECTION_NAME; + } + + @Override + public boolean supportsParsingObject() { + return true; + } + + @Override + public void parseObject(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Object object) throws LocalizedException { + if (object != null) { + globalVariables.put(id.value(), object.toString()); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/LazyContextParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/LazyContextParameterProvider.java deleted file mode 100644 index 0758f7770..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/LazyContextParameterProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context; - -import java.util.Optional; - -public interface LazyContextParameterProvider { - - Optional getOptionalParameter(ContextKey parameter); - - static LazyContextParameterProvider dummy() { - return DummyContextParameterProvider.INSTANCE; - } - - class DummyContextParameterProvider implements LazyContextParameterProvider { - static final DummyContextParameterProvider INSTANCE = new DummyContextParameterProvider(); - - @Override - public Optional getOptionalParameter(ContextKey parameter) { - return Optional.empty(); - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerBlockActionContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerBlockActionContext.java deleted file mode 100644 index bbabeb79a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerBlockActionContext.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context; - -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.parameter.BlockParameterProvider; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameterProvider; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameterProvider; -import net.momirealms.craftengine.core.world.BlockInWorld; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class PlayerBlockActionContext extends PlayerOptionalContext { - - public PlayerBlockActionContext(@NotNull Player player, @NotNull BlockInWorld block, @NotNull ContextHolder contexts) { - super(player, contexts, List.of(new CommonParameterProvider(), new PlayerParameterProvider(player), new BlockParameterProvider(block))); - } - - public static PlayerBlockActionContext of(@NotNull Player player, @NotNull BlockInWorld block, @NotNull ContextHolder contexts) { - return new PlayerBlockActionContext(player, block, contexts); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerFurnitureActionContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerFurnitureActionContext.java deleted file mode 100644 index 4066c0405..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerFurnitureActionContext.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context; - -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameterProvider; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameterProvider; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class PlayerFurnitureActionContext extends PlayerOptionalContext { - - public PlayerFurnitureActionContext(@NotNull Player player, @NotNull Furniture furniture, @NotNull ContextHolder contexts) { - super(player, contexts, List.of(new CommonParameterProvider(), new PlayerParameterProvider(player))); - } - - public static PlayerFurnitureActionContext of(@NotNull Player player, @NotNull Furniture furniture, @NotNull ContextHolder contexts) { - return new PlayerFurnitureActionContext(player, furniture, contexts); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java index e471cc51a..e71cf975b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java @@ -2,9 +2,7 @@ package net.momirealms.craftengine.core.plugin.context; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameterProvider; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameterProvider; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.text.minimessage.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,17 +10,20 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; -public class PlayerOptionalContext extends AbstractAdditionalParameterContext implements Context { +public class PlayerOptionalContext extends AbstractChainParameterContext implements Context { public static final PlayerOptionalContext EMPTY = new PlayerOptionalContext(null, ContextHolder.EMPTY); protected final Player player; - public PlayerOptionalContext(@Nullable Player player, @NotNull ContextHolder contexts) { - super(contexts, player == null ? List.of(new CommonParameterProvider()) : List.of(new CommonParameterProvider(), new PlayerParameterProvider(player))); + public PlayerOptionalContext(@Nullable Player player, + @NotNull ContextHolder contexts) { + super(contexts); this.player = player; } - public PlayerOptionalContext(@Nullable Player player, @NotNull ContextHolder contexts, @NotNull List providers) { - super(contexts, providers); + public PlayerOptionalContext(@Nullable Player player, + @NotNull ContextHolder contexts, + List additionalParameterProviders) { + super(contexts, additionalParameterProviders); this.player = player; } @@ -33,14 +34,14 @@ public class PlayerOptionalContext extends AbstractAdditionalParameterContext im @NotNull public static PlayerOptionalContext of(@Nullable Player player, @NotNull ContextHolder.Builder contexts) { - if (player != null) contexts.withParameter(CommonParameters.PLAYER, player); + if (player != null) contexts.withParameter(DirectContextParameters.PLAYER, player); return new PlayerOptionalContext(player, contexts.build()); } @NotNull public static PlayerOptionalContext of(@Nullable Player player) { if (player == null) return EMPTY; - return new PlayerOptionalContext(player, new ContextHolder(Map.of(CommonParameters.PLAYER, () -> player))); + return new PlayerOptionalContext(player, new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); } @Nullable @@ -48,11 +49,16 @@ public class PlayerOptionalContext extends AbstractAdditionalParameterContext im return this.player; } + public boolean isPlayerPresent() { + return this.player != null; + } + @Override @NotNull public TagResolver[] tagResolvers() { if (this.tagResolvers == null) { - this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), new NamedArgumentTag(this), new ExpressionTag(this)}; + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), + new NamedArgumentTag(this), new ExpressionTag(this), new GlobalVariableTag(this)}; } return this.tagResolvers; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ViewerContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ViewerContext.java index 4591d242d..cc4f2d761 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ViewerContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ViewerContext.java @@ -57,10 +57,18 @@ public class ViewerContext implements RelationalContext { if (this.owner instanceof PlayerOptionalContext context) { optionalOwner = context.player(); } - this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, - new PlaceholderTag(optionalOwner), new ViewerPlaceholderTag(this.viewer.player()), - new NamedArgumentTag(this.owner), new ViewerNamedArgumentTag(this.viewer), - new I18NTag(this), new ExpressionTag(this)}; + if (optionalOwner != null && this.viewer.player != null) { + this.tagResolvers = new TagResolver[]{new RelationalPlaceholderTag(optionalOwner, this.viewer.player), + ShiftTag.INSTANCE, ImageTag.INSTANCE, + new PlaceholderTag(optionalOwner), new ViewerPlaceholderTag(this.viewer.player()), + new NamedArgumentTag(this.owner), new ViewerNamedArgumentTag(this.viewer), + new I18NTag(this), new ExpressionTag(this), new GlobalVariableTag(this)}; + } else { + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, + new PlaceholderTag(optionalOwner), new ViewerPlaceholderTag(this.viewer.player()), + new NamedArgumentTag(this.owner), new ViewerNamedArgumentTag(this.viewer), + new I18NTag(this), new ExpressionTag(this), new GlobalVariableTag(this)}; + } } return this.tagResolvers; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java index c89b52606..a1776b43d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -11,22 +12,18 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.function.Predicate; public class AllOfCondition implements Condition { - protected final List> conditions; + protected final Predicate condition; public AllOfCondition(List> conditions) { - this.conditions = conditions; + this.condition = MCUtils.allOf(conditions); } @Override public boolean test(CTX ctx) { - for (Condition condition : conditions) { - if (!condition.test(ctx)) { - return false; - } - } - return true; + return this.condition.test(ctx); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java index 4151e9870..e01568f5b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -11,22 +12,18 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.function.Predicate; public class AnyOfCondition implements Condition { - protected final List> conditions; + protected final Predicate condition; public AnyOfCondition(List> conditions) { - this.conditions = conditions; + this.condition = MCUtils.anyOf(conditions); } @Override public boolean test(CTX ctx) { - for (Condition condition : conditions) { - if (condition.test(ctx)) { - return true; - } - } - return false; + return this.condition.test(ctx); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java index 1607f8013..600d204c8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java @@ -17,4 +17,7 @@ public final class CommonConditions { public static final Key ENCHANTMENT = Key.from("craftengine:enchantment"); public static final Key FALLING_BLOCK = Key.from("craftengine:falling_block"); public static final Key DISTANCE = Key.from("craftengine:distance"); + public static final Key PERMISSION = Key.from("craftengine:permission"); + public static final Key EQUALS = Key.from("craftengine:equals"); + public static final Key EXPRESSION = Key.from("craftengine:expression"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/DistanceCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/DistanceCondition.java index 52e22d6a6..24920e9c8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/DistanceCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/DistanceCondition.java @@ -5,10 +5,9 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import java.util.Map; import java.util.Optional; @@ -32,14 +31,18 @@ public class DistanceCondition implements Condition { public boolean test(CTX ctx) { float min = this.min.getFloat(ctx); float max = this.max.getFloat(ctx); - Optional optionalPlayer = ctx.getOptionalParameter(CommonParameters.PLAYER); - World world = ctx.getParameterOrThrow(CommonParameters.WORLD); - Vec3d location = ctx.getParameterOrThrow(CommonParameters.LOCATION); + Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); if (optionalPlayer.isEmpty()) { return false; } + Optional optionalPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); + if (optionalPosition.isEmpty()) { + return false; + } + + WorldPosition location = optionalPosition.get(); Player player = optionalPlayer.get(); - if (!player.world().uuid().equals(world.uuid())) { + if (!player.world().uuid().equals(location.world().uuid())) { return false; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EnchantmentCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EnchantmentCondition.java index 3523c9ae4..0be266bd5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EnchantmentCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EnchantmentCondition.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -29,7 +29,7 @@ public class EnchantmentCondition implements Condition @Override public boolean test(CTX ctx) { - Optional> item = ctx.getOptionalParameter(PlayerParameters.MAIN_HAND_ITEM); + Optional> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); if (item.isEmpty()) return false; Optional enchantment = item.get().getEnchantment(id); int level = enchantment.map(Enchantment::level).orElse(0); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EqualsCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EqualsCondition.java new file mode 100644 index 000000000..bc48c0a1a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EqualsCondition.java @@ -0,0 +1,40 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class EqualsCondition implements Condition { + private final TextProvider value1; + private final TextProvider value2; + + public EqualsCondition(TextProvider value1, TextProvider value2) { + this.value1 = value1; + this.value2 = value2; + } + + @Override + public Key type() { + return CommonConditions.EQUALS; + } + + @Override + public boolean test(CTX ctx) { + return this.value1.get(ctx).equals(this.value2.get(ctx)); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + String value1 = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value1"), "warning.config.condition.equals.missing_value1"); + String value2 = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value1"), "warning.config.condition.equals.missing_value2"); + return new EqualsCondition<>(TextProviders.fromString(value1), TextProviders.fromString(value2)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java new file mode 100644 index 000000000..7ae158e1f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import com.ezylang.evalex.EvaluationException; +import com.ezylang.evalex.Expression; +import com.ezylang.evalex.parser.ParseException; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class ExpressionCondition implements Condition { + private final TextProvider expression; + + public ExpressionCondition(TextProvider expression) { + this.expression = expression; + } + + @Override + public Key type() { + return CommonConditions.EXPRESSION; + } + + @Override + public boolean test(CTX ctx) { + String exp = expression.get(ctx); + Expression expr = new Expression(exp); + try { + return expr.evaluate().getBooleanValue(); + } catch (ParseException | EvaluationException e) { + CraftEngine.instance().logger().warn("Invalid expression " + exp, e); + return false; + } + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + String value = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("expression"), "warning.config.condition.expression.missing_expression"); + return new ExpressionCondition<>(TextProviders.fromString(value)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/FallingBlockCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/FallingBlockCondition.java index 1ab5a31b2..be4055055 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/FallingBlockCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/FallingBlockCondition.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import java.util.Map; @@ -16,7 +16,7 @@ public class FallingBlockCondition implements Condition implements ConditionFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java index d33b75f8e..44ae3625b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.Pair; @@ -29,7 +29,7 @@ public class MatchBlockPropertyCondition implements Conditi @Override public boolean test(CTX ctx) { - return ctx.getOptionalParameter(CommonParameters.BLOCK_STATE).map(state -> { + return ctx.getOptionalParameter(DirectContextParameters.BLOCK_STATE).map(state -> { CustomBlock block = state.owner().value(); for (Pair property : this.properties) { Property propertyIns = block.getProperty(property.left()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java index 09951996a..c61398043 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -26,7 +26,7 @@ public class MatchItemCondition implements Condition { @Override public boolean test(CTX ctx) { - Optional> item = ctx.getOptionalParameter(PlayerParameters.MAIN_HAND_ITEM); + Optional> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); if (item.isEmpty()) return false; Key key = item.get().id(); String itemId = key.toString(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/PermissionCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/PermissionCondition.java new file mode 100644 index 000000000..2a7a7f829 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/PermissionCondition.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Optional; + +public class PermissionCondition implements Condition { + private final String permission; + + public PermissionCondition(String permission) { + this.permission = permission; + } + + @Override + public Key type() { + return CommonConditions.PERMISSION; + } + + @Override + public boolean test(CTX ctx) { + Optional player = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + return player.map(value -> value.hasPermission(this.permission)).orElse(false); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + String permission = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("permission"), "warning.config.condition.permission.missing_permission"); + return new PermissionCondition<>(permission); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/RandomCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/RandomCondition.java index a855b84f9..68b44f5da 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/RandomCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/RandomCondition.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; @@ -28,12 +28,11 @@ public class RandomCondition implements Condition { @Override public boolean test(CTX ctx) { if (this.previous) { - // TODO This might produce bugs if the context doesn't use a common provider - Optional random = ctx.getOptionalParameter(CommonParameters.LAST_RANDOM); + Optional random = ctx.getOptionalParameter(DirectContextParameters.LAST_RANDOM); return random.map(d -> d < this.chance.getFloat(ctx)) .orElseGet(() -> RandomUtils.generateRandomFloat(0, 1) < this.chance.getFloat(ctx)); } else { - Optional random = ctx.getOptionalParameter(CommonParameters.RANDOM); + Optional random = ctx.getOptionalParameter(DirectContextParameters.RANDOM); return random.map(d -> d < this.chance.getFloat(ctx)) .orElseGet(() -> RandomUtils.generateRandomFloat(0, 1) < this.chance.getFloat(ctx)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/SurvivesExplosionCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/SurvivesExplosionCondition.java index 385bb708f..ca2713f75 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/SurvivesExplosionCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/SurvivesExplosionCondition.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; @@ -18,7 +18,7 @@ public class SurvivesExplosionCondition implements Conditio @Override public boolean test(CTX ctx) { - Optional radius = ctx.getOptionalParameter(CommonParameters.EXPLOSION_RADIUS); + Optional radius = ctx.getOptionalParameter(DirectContextParameters.EXPLOSION_RADIUS); if (radius.isPresent()) { float f = 1f / radius.get(); return RandomUtils.generateRandomFloat(0, 1) < f; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/TableBonusCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/TableBonusCondition.java index fb0870344..780643a19 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/TableBonusCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/TableBonusCondition.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.PlayerParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; @@ -31,7 +31,7 @@ public class TableBonusCondition implements Condition @Override public boolean test(CTX ctx) { - Optional> item = ctx.getOptionalParameter(PlayerParameters.MAIN_HAND_ITEM); + Optional> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); int level = item.map(value -> value.getEnchantment(this.enchantmentType).map(Enchantment::level).orElse(0)).orElse(0); float f = this.values.get(Math.min(level, this.values.size() - 1)); return RandomUtils.generateRandomFloat(0, 1) < f; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java index 25d56a813..394552424 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.plugin.context.function; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.util.Factory; import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; @@ -11,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.function.Predicate; -// TODO 将loot functions迁移过来 public abstract class AbstractConditionalFunction implements Function { protected final List> predicates; private final Predicate compositePredicates; @@ -30,13 +28,17 @@ public abstract class AbstractConditionalFunction implement protected abstract void runInternal(CTX ctx); - public static abstract class AbstractFactory implements Factory> { + public static abstract class AbstractFactory implements FunctionFactory { private final java.util.function.Function, Condition> factory; public AbstractFactory(java.util.function.Function, Condition> factory) { this.factory = factory; } + public java.util.function.Function, Condition> conditionFactory() { + return factory; + } + protected List> getPredicates(Map arguments) { Object predicates = arguments.get("conditions"); if (predicates == null) return List.of(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java new file mode 100644 index 000000000..1db327924 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.*; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ActionBarFunction extends AbstractConditionalFunction { + private final TextProvider message; + private final PlayerSelector selector; + + public ActionBarFunction(List> predicates, @Nullable PlayerSelector selector, TextProvider messages) { + super(predicates); + this.message = messages; + this.selector = selector; + } + + @Override + public void runInternal(CTX ctx) { + Optional owner = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + if (this.selector == null) { + owner.ifPresent(it -> { + it.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message.get(ctx), ctx.tagResolvers())); + }); + } else { + for (Player viewer : this.selector.get(ctx)) { + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); + viewer.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message.get(relationalContext), relationalContext.tagResolvers())); + } + } + } + + @Override + public Key type() { + return CommonFunctions.ACTIONBAR; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + String message = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "actionbar", "message"), "warning.config.function.actionbar.missing_actionbar"); + return new ActionBarFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), TextProviders.fromString(message)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CancelEventFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CancelEventFunction.java new file mode 100644 index 000000000..4c75c7eb9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CancelEventFunction.java @@ -0,0 +1,41 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Cancellable; +import net.momirealms.craftengine.core.util.Key; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class CancelEventFunction extends AbstractConditionalFunction { + + public CancelEventFunction(List> predicates) { + super(predicates); + } + + @Override + public void runInternal(CTX ctx) { + Optional cancellable = ctx.getOptionalParameter(DirectContextParameters.EVENT); + cancellable.ifPresent(value -> value.setCancelled(true)); + } + + @Override + public Key type() { + return CommonFunctions.CANCEL_EVENT; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + return new CancelEventFunction<>(getPredicates(arguments)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java index a6dd5254d..0b203a447 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.Platform; import net.momirealms.craftengine.core.plugin.context.*; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; import net.momirealms.craftengine.core.plugin.context.text.TextProvider; @@ -23,7 +23,7 @@ public class CommandFunction extends AbstractConditionalFun private final boolean asPlayer; private final PlayerSelector selector; - public CommandFunction(List> predicates, List command, boolean asPlayer, @Nullable PlayerSelector selector) { + public CommandFunction(List> predicates, @Nullable PlayerSelector selector, List command, boolean asPlayer) { super(predicates); this.asPlayer = asPlayer; this.command = command; @@ -33,7 +33,7 @@ public class CommandFunction extends AbstractConditionalFun @Override public void runInternal(CTX ctx) { if (this.asPlayer) { - Optional owner = ctx.getOptionalParameter(CommonParameters.PLAYER); + Optional owner = ctx.getOptionalParameter(DirectContextParameters.PLAYER); if (this.selector == null) { owner.ifPresent(it -> { for (TextProvider c : this.command) { @@ -72,7 +72,7 @@ public class CommandFunction extends AbstractConditionalFun Object command = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "command", "commands"), "warning.config.function.command.missing_command"); List commands = MiscUtils.getAsStringList(command).stream().map(TextProviders::fromString).toList(); boolean asPlayer = (boolean) arguments.getOrDefault("as-player", false); - return new CommandFunction<>(getPredicates(arguments), commands, asPlayer, PlayerSelectors.fromObject(arguments.get("target"))); + return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index 574ad1ba0..ed0bd1e32 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java @@ -2,7 +2,22 @@ package net.momirealms.craftengine.core.plugin.context.function; import net.momirealms.craftengine.core.util.Key; -public class CommonFunctions { - public static final Key COMMAND = Key.of("craftengine:command"); +public final class CommonFunctions { + private CommonFunctions() {} + public static final Key RUN_ALL = Key.of("craftengine:run_all"); + public static final Key COMMAND = Key.of("craftengine:command"); + public static final Key MESSAGE = Key.of("craftengine:message"); + public static final Key ACTIONBAR = Key.of("craftengine:actionbar"); + public static final Key TITLE = Key.of("craftengine:title"); + public static final Key PARTICLE = Key.of("craftengine:particle"); + public static final Key SOUND = Key.of("craftengine:sound"); + public static final Key POTION_EFFECT = Key.of("craftengine:potion_effect"); + public static final Key BREAK_BLOCK = Key.of("craftengine:break_block"); + public static final Key CANCEL_EVENT = Key.of("craftengine:cancel_event"); + public static final Key FOOD = Key.of("craftengine:food"); + public static final Key SATURATION = Key.of("craftengine:saturation"); + public static final Key MONEY = Key.of("craftengine:money"); + public static final Key OXYGEN = Key.of("craftengine:oxygen"); + public static final Key MINE_RADIUS = Key.of("craftengine:mine_radius"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/FunctionFactory.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/FunctionFactory.java new file mode 100644 index 000000000..f4e52f07c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/FunctionFactory.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.plugin.context.Context; + +import java.util.Map; + +public interface FunctionFactory { + + Function create(Map args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java new file mode 100644 index 000000000..1b4f2ddbd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java @@ -0,0 +1,70 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.*; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class MessageFunction extends AbstractConditionalFunction { + private final List messages; + private final PlayerSelector selector; + private final boolean overlay; + + public MessageFunction(List> predicates, @Nullable PlayerSelector selector, List messages, boolean overlay) { + super(predicates); + this.messages = messages; + this.selector = selector; + this.overlay = overlay; + } + + @Override + public void runInternal(CTX ctx) { + Optional owner = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + if (this.selector == null) { + owner.ifPresent(it -> { + for (TextProvider c : this.messages) { + it.sendMessage(AdventureHelper.miniMessage().deserialize(c.get(ctx), ctx.tagResolvers()), this.overlay); + } + }); + } else { + for (Player viewer : this.selector.get(ctx)) { + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); + for (TextProvider c : this.messages) { + viewer.sendMessage(AdventureHelper.miniMessage().deserialize(c.get(relationalContext), relationalContext.tagResolvers()), this.overlay); + } + } + } + } + + @Override + public Key type() { + return CommonFunctions.MESSAGE; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + Object message = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "messages", "message"), "warning.config.function.command.missing_message"); + List messages = MiscUtils.getAsStringList(message).stream().map(TextProviders::fromString).toList(); + boolean overlay = (boolean) arguments.getOrDefault("overlay", false); + return new MessageFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), messages, overlay); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java new file mode 100644 index 000000000..6d55589ae --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java @@ -0,0 +1,82 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.*; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.Key; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class TitleFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final TextProvider main; + private final TextProvider sub; + private final NumberProvider fadeIn; + private final NumberProvider stay; + private final NumberProvider fadeOut; + + public TitleFunction(List> predicates, @Nullable PlayerSelector selector, + TextProvider main, TextProvider sub, NumberProvider fadeIn, NumberProvider stay, NumberProvider fadeOut) { + super(predicates); + this.selector = selector; + this.main = main; + this.sub = sub; + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; + } + + @Override + public void runInternal(CTX ctx) { + Optional owner = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + if (this.selector == null) { + owner.ifPresent(it -> it.sendTitle( + AdventureHelper.miniMessage().deserialize(this.main.get(ctx), ctx.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.sub.get(ctx), ctx.tagResolvers()), + this.fadeIn.getInt(ctx), this.stay.getInt(ctx), this.fadeOut.getInt(ctx) + )); + } else { + for (Player viewer : this.selector.get(ctx)) { + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); + viewer.sendTitle( + AdventureHelper.miniMessage().deserialize(this.main.get(relationalContext), relationalContext.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.sub.get(relationalContext), relationalContext.tagResolvers()), + this.fadeIn.getInt(relationalContext), this.stay.getInt(relationalContext), this.fadeOut.getInt(relationalContext) + ); + } + } + } + + @Override + public Key type() { + return CommonFunctions.TITLE; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + String title = arguments.getOrDefault("title", "").toString(); + String subtitle = arguments.getOrDefault("subtitle", "").toString(); + NumberProvider fadeIn = NumberProviders.fromObject(arguments.getOrDefault("fade-in", 10)); + NumberProvider stay = NumberProviders.fromObject(arguments.getOrDefault("stay", 20)); + NumberProvider fadeOut = NumberProviders.fromObject(arguments.getOrDefault("fade-out", 5)); + return new TitleFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), + TextProviders.fromString(title), TextProviders.fromString(subtitle), fadeIn, stay, fadeOut); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java index 21c2ccabf..6428df308 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java @@ -6,7 +6,6 @@ import com.ezylang.evalex.parser.ParseException; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.Factory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -41,7 +40,7 @@ public class ExpressionNumberProvider implements NumberProvider { return this.expr; } - public static class FactoryImpl implements Factory { + public static class FactoryImpl implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java index cfbd165f9..3bbf7729f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.plugin.context.number; import com.ezylang.evalex.Expression; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Factory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -27,7 +26,7 @@ public class FixedNumberProvider implements NumberProvider { return NumberProviders.FIXED; } - public static class FactoryImpl implements Factory { + public static class FactoryImpl implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviderFactory.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviderFactory.java new file mode 100644 index 000000000..3d557362a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviderFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.plugin.context.number; + +import java.util.Map; + +public interface NumberProviderFactory { + + NumberProvider create(Map args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java index 5d5d609a4..8baef2d0b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java @@ -5,7 +5,6 @@ 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.Factory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; @@ -27,8 +26,8 @@ public class NumberProviders { register(EXPRESSION, ExpressionNumberProvider.FACTORY); } - public static void register(Key key, Factory factory) { - Holder.Reference> holder = ((WritableRegistry>) BuiltInRegistries.NUMBER_PROVIDER_FACTORY) + public static void register(Key key, NumberProviderFactory factory) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.NUMBER_PROVIDER_FACTORY) .registerForHolder(new ResourceKey<>(Registries.NUMBER_PROVIDER_FACTORY.location(), key)); holder.bindValue(factory); } @@ -45,7 +44,7 @@ public class NumberProviders { public static NumberProvider fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.number.missing_type"); Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); - Factory factory = BuiltInRegistries.NUMBER_PROVIDER_FACTORY.getValue(key); + NumberProviderFactory factory = BuiltInRegistries.NUMBER_PROVIDER_FACTORY.getValue(key); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.number.invalid_type", type); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java index 38cbf80ae..62d7c4ddc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.plugin.context.number; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.util.Factory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -41,7 +40,7 @@ public class UniformNumberProvider implements NumberProvider { return NumberProviders.UNIFORM; } - public static class FactoryImpl implements Factory { + public static class FactoryImpl implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java index 50107633d..e707aca80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java @@ -1,39 +1,32 @@ package net.momirealms.craftengine.core.plugin.context.parameter; +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.plugin.context.LazyContextParameterProvider; import net.momirealms.craftengine.core.world.BlockInWorld; -import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.function.Function; -public class BlockParameterProvider implements LazyContextParameterProvider { +public class BlockParameterProvider implements ChainParameterProvider { private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(BlockParameters.X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(BlockParameters.Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(BlockParameters.Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(BlockParameters.BLOCK_X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(BlockParameters.BLOCK_Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(BlockParameters.BLOCK_Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(BlockParameters.BLOCK_OWNER, BlockInWorld::owner); - CONTEXT_FUNCTIONS.put(BlockParameters.BLOCK_STATE, BlockInWorld::getAsString); - CONTEXT_FUNCTIONS.put(BlockParameters.WORLD_NAME, b -> b.world().name()); - } - - private final BlockInWorld block; - - public BlockParameterProvider(@NotNull BlockInWorld block) { - this.block = Objects.requireNonNull(block); + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, BlockInWorld::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, BlockInWorld::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, BlockInWorld::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, BlockInWorld::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, BlockInWorld::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, BlockInWorld::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, BlockInWorld::customBlock); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_STATE, BlockInWorld::customBlockState); + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, BlockInWorld::world); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, BlockInWorld::position); } @SuppressWarnings("unchecked") @Override - public Optional getOptionalParameter(ContextKey parameter) { - return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(this.block)); + public Optional getOptionalParameter(ContextKey parameter, BlockInWorld block) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(block)); } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameters.java deleted file mode 100644 index 68d2988b8..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameters.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context.parameter; - -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.util.Key; - -public final class BlockParameters { - private BlockParameters() {} - - public static final ContextKey X = ContextKey.of("block.x"); - public static final ContextKey Y = ContextKey.of("block.y"); - public static final ContextKey Z = ContextKey.of("block.z"); - public static final ContextKey BLOCK_X = ContextKey.of("block.block_x"); - public static final ContextKey BLOCK_Y = ContextKey.of("block.block_y"); - public static final ContextKey BLOCK_Z = ContextKey.of("block.block_z"); - public static final ContextKey BLOCK_STATE = ContextKey.of("block.state"); - public static final ContextKey BLOCK_OWNER = ContextKey.of("block.owner.id"); - public static final ContextKey WORLD_NAME = ContextKey.of("block.world.name"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameters.java deleted file mode 100644 index e84c45315..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/CommonParameters.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context.parameter; - -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; - -public final class CommonParameters { - private CommonParameters() {} - - public static final ContextKey RANDOM = ContextKey.of("random"); - public static final ContextKey LAST_RANDOM = ContextKey.of("last_random"); - public static final ContextKey LOCATION = ContextKey.of("location"); - public static final ContextKey WORLD = ContextKey.of("world"); - public static final ContextKey FALLING_BLOCK = ContextKey.of("falling_block"); - public static final ContextKey EXPLOSION_RADIUS = ContextKey.of("explosion_radius"); - public static final ContextKey PLAYER = ContextKey.of("player"); - public static final ContextKey BLOCK_STATE = ContextKey.of("block_state"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java new file mode 100644 index 000000000..151b69866 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.player.GameMode; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.util.Cancellable; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.world.Position; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.UUID; + +public final class DirectContextParameters { + private DirectContextParameters() {} + + public static final ContextKey RANDOM = ContextKey.direct("random"); + public static final ContextKey LAST_RANDOM = ContextKey.direct("last_random"); + public static final ContextKey WORLD = ContextKey.direct("world"); + public static final ContextKey> FURNITURE_ITEM = ContextKey.direct("furniture_item"); + public static final ContextKey> CONSUMED_ITEM = ContextKey.direct("consumed_item"); + public static final ContextKey> ITEM_IN_HAND = ContextKey.direct("item_in_hand"); + public static final ContextKey FALLING_BLOCK = ContextKey.direct("falling_block"); + public static final ContextKey EXPLOSION_RADIUS = ContextKey.direct("explosion_radius"); + public static final ContextKey PLAYER = ContextKey.direct("player"); + public static final ContextKey ENTITY = ContextKey.direct("entity"); + public static final ContextKey BLOCK_STATE = ContextKey.direct("custom_block_state"); + public static final ContextKey COORDINATE = ContextKey.direct("coordinate"); + public static final ContextKey POSITION = ContextKey.direct("position"); + public static final ContextKey NAME = ContextKey.direct("name"); + public static final ContextKey X = ContextKey.direct("x"); + public static final ContextKey Y = ContextKey.direct("y"); + public static final ContextKey Z = ContextKey.direct("z"); + public static final ContextKey BLOCK_X = ContextKey.direct("block_x"); + public static final ContextKey BLOCK_Y = ContextKey.direct("block_y"); + public static final ContextKey BLOCK_Z = ContextKey.direct("block_z"); + public static final ContextKey UUID = ContextKey.direct("uuid"); + public static final ContextKey> MAIN_HAND_ITEM = ContextKey.direct("main_hand_item"); + public static final ContextKey> OFF_HAND_ITEM = ContextKey.direct("off_hand_item"); + public static final ContextKey CUSTOM_BLOCK = ContextKey.direct("custom_block"); + public static final ContextKey BLOCK = ContextKey.direct("block"); + public static final ContextKey TIME = ContextKey.direct("time"); + public static final ContextKey ID = ContextKey.direct("id"); + public static final ContextKey CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data"); + public static final ContextKey FURNITURE = ContextKey.direct("furniture"); + public static final ContextKey ANCHOR_TYPE = ContextKey.direct("anchor_type"); + public static final ContextKey HAND = ContextKey.direct("hand"); + public static final ContextKey EVENT = ContextKey.direct("event"); + public static final ContextKey IS_FLYING = ContextKey.direct("is_flying"); + public static final ContextKey IS_SNEAKING = ContextKey.direct("is_sneaking"); + public static final ContextKey IS_CUSTOM = ContextKey.direct("is_custom"); + public static final ContextKey GAMEMODE = ContextKey.direct("gamemode"); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java new file mode 100644 index 000000000..b36225670 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; +import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.util.MCUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class EntityParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); + static { + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, Entity::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, Entity::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, Entity::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Entity::name); + CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Entity::uuid); + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); + } + + @SuppressWarnings("unchecked") + @Override + public Optional getOptionalParameter(ContextKey parameter, Entity entity) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(entity)); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java new file mode 100644 index 000000000..2f49aca37 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; +import net.momirealms.craftengine.core.plugin.context.ContextKey; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class FurnitureParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); + static { + CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, Furniture::id); + CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid); + CONTEXT_FUNCTIONS.put(DirectContextParameters.ANCHOR_TYPE, Furniture::anchorType); + } + + @SuppressWarnings("unchecked") + @Override + public Optional getOptionalParameter(ContextKey parameter, Furniture world) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(world)); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/ItemParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/ItemParameterProvider.java new file mode 100644 index 000000000..a4709072f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/ItemParameterProvider.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; +import net.momirealms.craftengine.core.plugin.context.ContextKey; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class ItemParameterProvider implements ChainParameterProvider> { + private static final Map, Function, Object>> CONTEXT_FUNCTIONS = new HashMap<>(); + static { + CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, Item::id); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_MODEL_DATA, i -> i.customModelData().orElse(null)); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_CUSTOM, Item::isCustomItem); + } + + @SuppressWarnings("unchecked") + @Override + public Optional getOptionalParameter(ContextKey parameter, Item world) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(world)); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java index c3a17484f..4075c6ff8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java @@ -1,45 +1,41 @@ package net.momirealms.craftengine.core.plugin.context.parameter; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.plugin.context.LazyContextParameterProvider; import net.momirealms.craftengine.core.util.MCUtils; -import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.function.Function; -public class PlayerParameterProvider implements LazyContextParameterProvider { +public class PlayerParameterProvider implements ChainParameterProvider { private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(PlayerParameters.X, Entity::x); - CONTEXT_FUNCTIONS.put(PlayerParameters.Y, Entity::y); - CONTEXT_FUNCTIONS.put(PlayerParameters.Z, Entity::z); - CONTEXT_FUNCTIONS.put(PlayerParameters.POS, Entity::position); - CONTEXT_FUNCTIONS.put(PlayerParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(PlayerParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(PlayerParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); - CONTEXT_FUNCTIONS.put(PlayerParameters.NAME, Player::name); - CONTEXT_FUNCTIONS.put(PlayerParameters.UUID, Player::uuid); - CONTEXT_FUNCTIONS.put(PlayerParameters.WORLD_NAME, p -> p.world().name()); - CONTEXT_FUNCTIONS.put(PlayerParameters.MAIN_HAND_ITEM, p -> p.getItemInHand(InteractionHand.MAIN_HAND)); - CONTEXT_FUNCTIONS.put(PlayerParameters.OFF_HAND_ITEM, p -> p.getItemInHand(InteractionHand.OFF_HAND)); - } - - private final Player player; - - public PlayerParameterProvider(@NotNull Player player) { - this.player = Objects.requireNonNull(player); + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, AbstractEntity::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, AbstractEntity::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, AbstractEntity::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, AbstractEntity::position); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name); + CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Player::uuid); + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SNEAKING, Player::isSneaking); + CONTEXT_FUNCTIONS.put(DirectContextParameters.GAMEMODE, Player::gameMode); + CONTEXT_FUNCTIONS.put(DirectContextParameters.MAIN_HAND_ITEM, p -> p.getItemInHand(InteractionHand.MAIN_HAND)); + CONTEXT_FUNCTIONS.put(DirectContextParameters.OFF_HAND_ITEM, p -> p.getItemInHand(InteractionHand.OFF_HAND)); } @SuppressWarnings("unchecked") @Override - public Optional getOptionalParameter(ContextKey parameter) { - return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(this.player)); + public Optional getOptionalParameter(ContextKey parameter, Player player) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(player)); } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameters.java deleted file mode 100644 index 4d558ba1b..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameters.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.momirealms.craftengine.core.plugin.context.parameter; - -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.world.Vec3d; - -import java.util.UUID; - -public final class PlayerParameters { - private PlayerParameters() {} - - public static final ContextKey X = ContextKey.of("player.x"); - public static final ContextKey Y = ContextKey.of("player.y"); - public static final ContextKey Z = ContextKey.of("player.z"); - public static final ContextKey POS = ContextKey.of("player.pos"); - public static final ContextKey BLOCK_X = ContextKey.of("player.block_x"); - public static final ContextKey BLOCK_Y = ContextKey.of("player.block_y"); - public static final ContextKey BLOCK_Z = ContextKey.of("player.block_z"); - public static final ContextKey NAME = ContextKey.of("player.name"); - public static final ContextKey WORLD_NAME = ContextKey.of("player.world.name"); - public static final ContextKey UUID = ContextKey.of("player.uuid"); - public static final ContextKey> MAIN_HAND_ITEM = ContextKey.of("player.main_hand"); - public static final ContextKey> OFF_HAND_ITEM = ContextKey.of("player.off_hand"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java new file mode 100644 index 000000000..710917c19 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; +import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class PositionParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); + static { + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, WorldPosition::world); + CONTEXT_FUNCTIONS.put(DirectContextParameters.COORDINATE, p -> p); + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, WorldPosition::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, WorldPosition::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, WorldPosition::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + } + + @SuppressWarnings("unchecked") + @Override + public Optional getOptionalParameter(ContextKey parameter, WorldPosition position) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(position)); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/WorldParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/WorldParameterProvider.java new file mode 100644 index 000000000..ddc921a0a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/WorldParameterProvider.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.plugin.context.parameter; + +import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; +import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.world.World; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class WorldParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); + static { + CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, World::name); + CONTEXT_FUNCTIONS.put(DirectContextParameters.TIME, World::time); + CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, World::uuid); + } + + @SuppressWarnings("unchecked") + @Override + public Optional getOptionalParameter(ContextKey parameter, World world) { + return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(world)); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java index 77e431e1a..edd33b388 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java @@ -6,44 +6,41 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MCUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; public class AllPlayerSelector implements PlayerSelector { - private final List> predicates; + private final Predicate predicate; public AllPlayerSelector(List> predicates) { - this.predicates = predicates; + this.predicate = MCUtils.allOf(predicates); } public AllPlayerSelector() { - this.predicates = List.of(); - } - - public List> predicates() { - return predicates; + this.predicate = null; } @SuppressWarnings("unchecked") @Override public List get(CTX context) { - if (this.predicates.isEmpty()) { + if (this.predicate == null) { return Arrays.asList(CraftEngine.instance().networkManager().onlineUsers()); } else { List players = new ArrayList<>(); - outer: for (Player player : CraftEngine.instance().networkManager().onlineUsers()) { + for (Player player : CraftEngine.instance().networkManager().onlineUsers()) { PlayerOptionalContext newContext = PlayerOptionalContext.of(player, ContextHolder.builder() - .withOptionalParameter(CommonParameters.WORLD, context.getOptionalParameter(CommonParameters.WORLD).orElse(null)) - .withOptionalParameter(CommonParameters.LOCATION, context.getOptionalParameter(CommonParameters.LOCATION).orElse(null)) + .withOptionalParameter(DirectContextParameters.POSITION, context.getOptionalParameter(DirectContextParameters.POSITION).orElse(null)) ); - for (Condition predicate : this.predicates) { - if (!predicate.test((CTX) newContext)) { - continue outer; - } + if (!this.predicate.test((CTX) newContext)) { + continue; } players.add(player); } @@ -55,4 +52,14 @@ public class AllPlayerSelector implements PlayerSelector implements PlayerSelectorFactory { + + @Override + public PlayerSelector create(Map args, Function, Condition> conditionFactory) { + + + return null; + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectorFactory.java new file mode 100644 index 000000000..c286c38e4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectorFactory.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.plugin.context.selector; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; + +import java.util.Map; +import java.util.function.Function; + +public interface PlayerSelectorFactory { + + PlayerSelector create(Map args, Function, Condition> conditionFactory); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectors.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectors.java index ae5010c81..c1198e8d9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/PlayerSelectors.java @@ -1,21 +1,60 @@ package net.momirealms.craftengine.core.plugin.context.selector; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; +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.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.ResourceKey; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.function.Function; public class PlayerSelectors { public static final Key ALL = Key.of("craftengine:all"); public static final Key SELF = Key.of("craftengine:self"); - public static PlayerSelector fromObject(Object object) { + static { + register(ALL, new AllPlayerSelector.FactoryImpl<>()); + register(SELF, new SelfPlayerSelector.FactoryImpl<>()); + } + + public static void register(Key key, PlayerSelectorFactory factory) { + Holder.Reference> holder = ((WritableRegistry>) BuiltInRegistries.PLAYER_SELECTOR_FACTORY) + .registerForHolder(new ResourceKey<>(Registries.PLAYER_SELECTOR_FACTORY.location(), key)); + holder.bindValue(factory); + } + + @Nullable + public static PlayerSelector fromObject(Object object, Function, Condition> conditionFactory) { if (object == null) return null; - if (object instanceof String string) { - if (string.equals("self") || string.equals("@self") || string.equals("@s")) { - return new SelfPlayerSelector<>(); - } else if (string.equals("all") || string.equals("@all") || string.equals("@a")) { + if (object instanceof Map map) { + Map selectorMap = MiscUtils.castToMap(map, false); + return fromMap(selectorMap, conditionFactory); + } else if (object instanceof String target) { + if (target.equals("all") || target.equals("@a")) { return new AllPlayerSelector<>(); + } else if (target.equals("self") || target.equals("@s")) { + return new SelfPlayerSelector<>(); } } - throw new UnsupportedOperationException("Not supported yet."); + throw new LocalizedResourceConfigException("warning.config.selector.invalid_target", object.toString()); + } + + public static PlayerSelector fromMap(Map map, Function, Condition> conditionFactory) { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.selector.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + @SuppressWarnings("unchecked") + PlayerSelectorFactory factory = (PlayerSelectorFactory) BuiltInRegistries.PLAYER_SELECTOR_FACTORY.getValue(key); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.selector.invalid_type", type); + } + return factory.create(map, conditionFactory); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/SelfPlayerSelector.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/SelfPlayerSelector.java index ea749f3ba..64024828c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/SelfPlayerSelector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/SelfPlayerSelector.java @@ -1,21 +1,31 @@ package net.momirealms.craftengine.core.plugin.context.selector; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import java.util.List; +import java.util.Map; +import java.util.function.Function; public class SelfPlayerSelector implements PlayerSelector { @Override public List get(CTX context) { - return List.of(context.getParameterOrThrow(CommonParameters.PLAYER)); + return List.of(context.getParameterOrThrow(DirectContextParameters.PLAYER)); } @Override public Key type() { return PlayerSelectors.SELF; } + + public static class FactoryImpl implements PlayerSelectorFactory { + @Override + public PlayerSelector create(Map args, Function, Condition> conditionFactory) { + return new SelfPlayerSelector<>(); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java index 285430f15..2499f83d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java @@ -280,6 +280,22 @@ public class Dependencies { List.of(Relocation.of("reactivestreams", "org{}reactivestreams")) ); + public static final Dependency JIMFS = new Dependency( + "jimfs", + "com{}google{}jimfs", + "jimfs", + "jimfs", + List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs")) + ); + + public static final Dependency COMMONS_IMAGING = new Dependency( + "commons-imaging", + "org{}apache{}commons", + "commons-imaging", + "commons-imaging", + List.of(Relocation.of("imaging", "org{}apache{}commons{}imaging")) + ); + public static final Dependency AMAZON_AWSSDK_S3 = new Dependency( "amazon-sdk-s3", "software{}amazon{}awssdk", diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventConditions.java deleted file mode 100644 index 5cb58ed8a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventConditions.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.momirealms.craftengine.core.plugin.event; - -import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.PlayerBlockActionContext; -import net.momirealms.craftengine.core.plugin.context.condition.InvertedCondition; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.util.Factory; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; - -import java.util.Map; - -public class BlockEventConditions { - - public static Condition fromMap(Map map) { - String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.block.event.condition.missing_type"); - Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); - if (key.value().charAt(0) == '!') { - Factory> factory = BuiltInRegistries.PLAYER_BLOCK_CONDITION_FACTORY.getValue(new Key(key.namespace(), key.value().substring(1))); - if (factory == null) { - throw new LocalizedResourceConfigException("warning.config.block.event.condition.invalid_type", type); - } - return new InvertedCondition<>(factory.create(map)); - } else { - Factory> factory = BuiltInRegistries.PLAYER_BLOCK_CONDITION_FACTORY.getValue(key); - if (factory == null) { - throw new LocalizedResourceConfigException("warning.config.block.event.condition.invalid_type", type); - } - return factory.create(map); - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventFunctions.java deleted file mode 100644 index 885e7fec2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/BlockEventFunctions.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.momirealms.craftengine.core.plugin.event; - -import net.momirealms.craftengine.core.plugin.context.PlayerBlockActionContext; -import net.momirealms.craftengine.core.plugin.context.function.CommandFunction; -import net.momirealms.craftengine.core.plugin.context.function.CommonFunctions; -import net.momirealms.craftengine.core.plugin.context.function.Function; -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.Factory; -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; - -public class BlockEventFunctions { - - static { - register(CommonFunctions.COMMAND, new CommandFunction.FactoryImpl<>(BlockEventConditions::fromMap)); - } - - public static void register(Key key, Factory> factory) { - Holder.Reference>> holder = ((WritableRegistry>>) BuiltInRegistries.PLAYER_BLOCK_FUNCTION_FACTORY) - .registerForHolder(new ResourceKey<>(Registries.PLAYER_BLOCK_FUNCTION_FACTORY.location(), key)); - holder.bindValue(factory); - } - - public static Function fromMap(Map map) { - String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "TODO I18N"); - Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); - Factory> factory = BuiltInRegistries.PLAYER_BLOCK_FUNCTION_FACTORY.getValue(key); - if (factory == null) { - throw new LocalizedResourceConfigException("TODO I18N", type); - } - return factory.create(map); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventConditions.java new file mode 100644 index 000000000..b1c578046 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventConditions.java @@ -0,0 +1,59 @@ +package net.momirealms.craftengine.core.plugin.event; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.condition.*; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.Map; + +public class EventConditions { + + static { + register(CommonConditions.MATCH_ITEM, new MatchItemCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_BLOCK_PROPERTY, new MatchBlockPropertyCondition.FactoryImpl<>()); + register(CommonConditions.TABLE_BONUS, new TableBonusCondition.FactoryImpl<>()); + register(CommonConditions.SURVIVES_EXPLOSION, new SurvivesExplosionCondition.FactoryImpl<>()); + register(CommonConditions.ANY_OF, new AnyOfCondition.FactoryImpl<>(EventConditions::fromMap)); + register(CommonConditions.ALL_OF, new AllOfCondition.FactoryImpl<>(EventConditions::fromMap)); + register(CommonConditions.ENCHANTMENT, new EnchantmentCondition.FactoryImpl<>()); + register(CommonConditions.INVERTED, new InvertedCondition.FactoryImpl<>(EventConditions::fromMap)); + register(CommonConditions.FALLING_BLOCK, new FallingBlockCondition.FactoryImpl<>()); + register(CommonConditions.RANDOM, new RandomCondition.FactoryImpl<>()); + register(CommonConditions.DISTANCE, new DistanceCondition.FactoryImpl<>()); + register(CommonConditions.PERMISSION, new PermissionCondition.FactoryImpl<>()); + register(CommonConditions.EQUALS, new EqualsCondition.FactoryImpl<>()); + register(CommonConditions.EXPRESSION, new ExpressionCondition.FactoryImpl<>()); + } + + public static void register(Key key, ConditionFactory factory) { + Holder.Reference> holder = ((WritableRegistry>) BuiltInRegistries.EVENT_CONDITION_FACTORY) + .registerForHolder(new ResourceKey<>(Registries.EVENT_CONDITION_FACTORY.location(), key)); + holder.bindValue(factory); + } + + public static Condition fromMap(Map map) { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.event.condition.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + if (key.value().charAt(0) == '!') { + ConditionFactory factory = BuiltInRegistries.EVENT_CONDITION_FACTORY.getValue(new Key(key.namespace(), key.value().substring(1))); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.event.condition.invalid_type", type); + } + return new InvertedCondition<>(factory.create(map)); + } else { + ConditionFactory factory = BuiltInRegistries.EVENT_CONDITION_FACTORY.getValue(key); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.event.condition.invalid_type", type); + } + return factory.create(map); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventFunctions.java new file mode 100644 index 000000000..91d28760f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventFunctions.java @@ -0,0 +1,102 @@ +package net.momirealms.craftengine.core.plugin.event; + +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.*; +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.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class EventFunctions { + + static { + register(CommonFunctions.COMMAND, new CommandFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.MESSAGE, new MessageFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.ACTIONBAR, new ActionBarFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.TITLE, new TitleFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.CANCEL_EVENT, new CancelEventFunction.FactoryImpl<>(EventConditions::fromMap)); + } + + public static void register(Key key, FunctionFactory factory) { + Holder.Reference> holder = ((WritableRegistry>) BuiltInRegistries.EVENT_FUNCTION_FACTORY) + .registerForHolder(new ResourceKey<>(Registries.EVENT_FUNCTION_FACTORY.location(), key)); + holder.bindValue(factory); + } + + public static Function fromMap(Map map) { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.function.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + FunctionFactory factory = BuiltInRegistries.EVENT_FUNCTION_FACTORY.getValue(key); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.function.invalid_type", type); + } + return factory.create(map); + } + + public static EnumMap>> parseEvents(Object eventsObj) { + EnumMap>> events = new EnumMap<>(EventTrigger.class); + if (eventsObj instanceof Map eventsSection) { + Map eventsSectionMap = MiscUtils.castToMap(eventsSection, false); + for (Map.Entry eventEntry : eventsSectionMap.entrySet()) { + try { + EventTrigger eventTrigger = EventTrigger.byName(eventEntry.getKey()); + if (eventEntry.getValue() instanceof List list) { + if (list.size() == 1) { + events.put(eventTrigger, List.of(EventFunctions.fromMap(MiscUtils.castToMap(list.get(0), false)))); + } else if (list.size() == 2) { + events.put(eventTrigger, List.of( + EventFunctions.fromMap(MiscUtils.castToMap(list.get(0), false)), + EventFunctions.fromMap(MiscUtils.castToMap(list.get(1), false)) + )); + } else { + List> eventsList = new ArrayList<>(); + for (Object event : list) { + eventsList.add(EventFunctions.fromMap(MiscUtils.castToMap(event, false))); + } + events.put(eventTrigger, eventsList); + } + } else if (eventEntry.getValue() instanceof Map eventSection) { + events.put(eventTrigger, List.of(EventFunctions.fromMap(MiscUtils.castToMap(eventSection, false)))); + } + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.event.invalid_trigger", eventEntry.getKey()); + } + } + } else if (eventsObj instanceof List list) { + @SuppressWarnings("unchecked") + List> eventsList = (List>) list; + for (Map eventSection : eventsList) { + Object onObj = eventSection.get("on"); + if (onObj == null) { + throw new LocalizedResourceConfigException("warning.config.event.missing_trigger"); + } + try { + EventTrigger eventTrigger = EventTrigger.byName(onObj.toString()); + if (eventSection.containsKey("type")) { + Function function = EventFunctions.fromMap(eventSection); + events.computeIfAbsent(eventTrigger, k -> new ArrayList<>(4)).add(function); + } else if (eventSection.containsKey("functions")) { + @SuppressWarnings("unchecked") + List> functionList = (List>) eventSection.get("functions"); + for (Map function : functionList) { + events.computeIfAbsent(eventTrigger, k -> new ArrayList<>(4)).add(EventFunctions.fromMap(function)); + } + } + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.event.invalid_trigger", onObj.toString()); + } + } + } + return events; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventTrigger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventTrigger.java index 2ea3421eb..8c7060079 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventTrigger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/event/EventTrigger.java @@ -1,8 +1,37 @@ package net.momirealms.craftengine.core.plugin.event; -public enum EventTrigger { - USE_ITEM, - USE_ITEM_ON, - CONSUME, +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +public enum EventTrigger { + LEFT_CLICK("attack", "left_click"), + RIGHT_CLICK("right_click", "use_on", "use", "use_item_on"), + CONSUME("eat", "consume", "drink"), + BREAK("break", "dig"), + PLACE("place", "build"), + STEP("step"),; + + public static final Map BY_NAME = new HashMap<>(); + private final String[] names; + + EventTrigger(String... names) { + this.names = names; + } + + public String[] names() { + return names; + } + + static { + for (EventTrigger trigger : EventTrigger.values()) { + for (String name : trigger.names()) { + BY_NAME.put(name, trigger); + } + } + } + + public static EventTrigger byName(String name) { + return Optional.ofNullable(BY_NAME.get(name)).orElseThrow(() -> new IllegalArgumentException("Unknown event trigger: " + name)); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiParameters.java index 946cbf505..dddb42e5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiParameters.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiParameters.java @@ -2,9 +2,11 @@ package net.momirealms.craftengine.core.plugin.gui; import net.momirealms.craftengine.core.plugin.context.ContextKey; -public class GuiParameters { - public static final ContextKey MAX_PAGE = ContextKey.of("max_page"); - public static final ContextKey CURRENT_PAGE = ContextKey.of("current_page"); - public static final ContextKey COOKING_TIME = ContextKey.of("cooking_time"); - public static final ContextKey COOKING_EXPERIENCE = ContextKey.of("cooking_experience"); +public final class GuiParameters { + private GuiParameters() {} + + public static final ContextKey MAX_PAGE = ContextKey.direct("max_page"); + public static final ContextKey CURRENT_PAGE = ContextKey.direct("current_page"); + public static final ContextKey COOKING_TIME = ContextKey.direct("cooking_time"); + public static final ContextKey COOKING_EXPERIENCE = ContextKey.direct("cooking_experience"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManager.java index e9de45dab..9d6b9732b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManager.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.gui.Gui; import net.momirealms.craftengine.core.util.Key; @@ -19,7 +19,7 @@ public interface ItemBrowserManager extends Manageable { int MAX_RECIPE_DEPTH = 16; String GET_ITEM_PERMISSION = "craftengine.browser.get_item"; - ConfigSectionParser parser(); + ConfigParser parser(); void addExternalCategoryMember(Key item, List category); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index 9d6f17d16..024b294df 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.*; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.gui.*; @@ -68,7 +68,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } @Override - public ConfigSectionParser parser() { + public ConfigParser parser() { return this.categoryParser; } @@ -93,7 +93,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { return Optional.ofNullable(this.byId.get(key)); } - public class CategoryParser implements ConfigSectionParser { + public class CategoryParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"categories", "category"}; @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index f8bab04f3..d4ec4c773 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.locale; import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.Translator; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -44,7 +44,7 @@ public interface TranslationManager extends Manageable { return TranslationManagerImpl.instance; } - ConfigSectionParser[] parsers(); + ConfigParser[] parsers(); default String miniMessageTranslation(String key) { return miniMessageTranslation(key, null); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index dbbcdd70c..d73c40551 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.plugin.PluginProperties; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -63,8 +63,8 @@ public class TranslationManagerImpl implements TranslationManager { } @Override - public ConfigSectionParser[] parsers() { - return new ConfigSectionParser[] {this.langParser, this.i18nParser}; + public ConfigParser[] parsers() { + return new ConfigParser[] {this.langParser, this.i18nParser}; } @Override @@ -277,7 +277,7 @@ public class TranslationManagerImpl implements TranslationManager { } } - public class I18NParser implements ConfigSectionParser { + public class I18NParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"i18n", "internationalization", "translation", "translations"}; @Override @@ -308,7 +308,7 @@ public class TranslationManagerImpl implements TranslationManager { } } - public class LangParser implements ConfigSectionParser { + public class LangParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"lang", "language", "languages"}; @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/ByteBufPacketEvent.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ByteBufPacketEvent.java similarity index 53% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/ByteBufPacketEvent.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/network/ByteBufPacketEvent.java index 023bb5b94..585921ca0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/ByteBufPacketEvent.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ByteBufPacketEvent.java @@ -1,31 +1,28 @@ -package net.momirealms.craftengine.bukkit.plugin.network; +package net.momirealms.craftengine.core.plugin.network; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.FriendlyByteBuf; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - public class ByteBufPacketEvent implements Cancellable { private boolean cancelled; - private List delayedTasks = null; private final FriendlyByteBuf buf; private boolean changed; private final int packetID; + private final int preIndex; - public ByteBufPacketEvent(int packetID, FriendlyByteBuf buf) { + public ByteBufPacketEvent(int packetID, FriendlyByteBuf buf, int preIndex) { this.buf = buf; this.packetID = packetID; + this.preIndex = preIndex; } public int packetID() { - return packetID; + return this.packetID; } public FriendlyByteBuf getBuffer() { - return buf; + this.buf.readerIndex(this.preIndex); + return this.buf; } public void setChanged(boolean dirty) { @@ -33,18 +30,7 @@ public class ByteBufPacketEvent implements Cancellable { } public boolean changed() { - return changed; - } - - public void addDelayedTask(Runnable task) { - if (delayedTasks == null) { - delayedTasks = new ArrayList<>(); - } - delayedTasks.add(task); - } - - public List getDelayedTasks() { - return Optional.ofNullable(delayedTasks).orElse(Collections.emptyList()); + return this.changed; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java new file mode 100644 index 000000000..224a27f81 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -0,0 +1,19 @@ +package net.momirealms.craftengine.core.plugin.network; + +import it.unimi.dsi.fastutil.ints.IntList; + +public interface EntityPacketHandler { + + default boolean handleEntitiesRemove(IntList entityIds) { + return false; + } + + default void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + } + + default void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { + } + + default void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NMSPacketEvent.java similarity index 95% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/network/NMSPacketEvent.java index d753f4815..e95726ddb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NMSPacketEvent.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network; +package net.momirealms.craftengine.core.plugin.network; import net.momirealms.craftengine.core.util.Cancellable; import org.jetbrains.annotations.NotNull; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index a88dd7c6a..cf9442fee 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.ApiStatus; -import java.util.List; import java.util.Map; import java.util.UUID; @@ -42,9 +41,7 @@ public interface NetWorkUser { Object platformPlayer(); - Map> furnitureView(); - - Map entityView(); + Map entityPacketHandlers(); boolean clientModEnabled(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/GlobalVariableTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/GlobalVariableTag.java new file mode 100644 index 000000000..257a3a72e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/GlobalVariableTag.java @@ -0,0 +1,37 @@ +package net.momirealms.craftengine.core.plugin.text.minimessage; + +import net.kyori.adventure.text.minimessage.ParsingException; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.AdventureHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class GlobalVariableTag implements TagResolver { + private final Context context; + + public GlobalVariableTag(Context context) { + this.context = context; + } + + @Override + public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull net.kyori.adventure.text.minimessage.Context ctx) throws ParsingException { + if (!this.has(name)) { + return null; + } + String id = arguments.popOr("No argument variable id provided").toString(); + String value = CraftEngine.instance().globalVariableManager().get(id); + if (value == null) { + throw ctx.newException("Unknown variable: ", arguments); + } + return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(value, this.context.tagResolvers())); + } + + @Override + public boolean has(@NotNull String name) { + return "global".equals(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java index 595be9c85..0b775e569 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java @@ -25,7 +25,7 @@ public class NamedArgumentTag implements TagResolver { if (!has(name)) { return null; } - ContextKey key = ContextKey.of(arguments.popOr("No argument key provided").toString()); + ContextKey key = ContextKey.chain(arguments.popOr("No argument key provided").toString()); Optional optional = this.context.getOptionalParameter(key); Object value = optional.orElse(null); if (value == null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/RelationalPlaceholderTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/RelationalPlaceholderTag.java new file mode 100644 index 000000000..07f175b6d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/RelationalPlaceholderTag.java @@ -0,0 +1,40 @@ +package net.momirealms.craftengine.core.plugin.text.minimessage; + +import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.ParsingException; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.AdventureHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class RelationalPlaceholderTag implements TagResolver { + private final Player player1; + private final Player player2; + + public RelationalPlaceholderTag(@NotNull Player player1, @NotNull Player player2) { + this.player1 = player1; + this.player2 = player2; + } + + @Override + public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException { + if (!this.has(name) || !CraftEngine.instance().compatibilityManager().hasPlaceholderAPI()) { + return null; + } + String placeholder = "%" + arguments.popOr("No argument placeholder provided") + "%"; + String parsed = CraftEngine.instance().compatibilityManager().parse(player1, player2, placeholder); + if (parsed.equals(placeholder)) { + parsed = arguments.popOr("No default papi value provided").toString(); + } + return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(parsed)); + } + + @Override + public boolean has(@NotNull String name) { + return "rel_papi".equals(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index a92d3db1d..a28c42fe9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -21,12 +21,11 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; -import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.PlayerBlockActionContext; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; -import net.momirealms.craftengine.core.util.Factory; +import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -39,7 +38,7 @@ public class BuiltInRegistries { public static final Registry> LOOT_FUNCTION_FACTORY = createRegistry(Registries.LOOT_FUNCTION_FACTORY); public static final Registry> LOOT_CONDITION_FACTORY = createRegistry(Registries.LOOT_CONDITION_FACTORY); public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY); - public static final Registry> NUMBER_PROVIDER_FACTORY = createRegistry(Registries.NUMBER_PROVIDER_FACTORY); + public static final Registry NUMBER_PROVIDER_FACTORY = createRegistry(Registries.NUMBER_PROVIDER_FACTORY); public static final Registry TEMPLATE_ARGUMENT_FACTORY = createRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); public static final Registry ITEM_MODEL_FACTORY = createRegistry(Registries.ITEM_MODEL_FACTORY); public static final Registry TINT_FACTORY = createRegistry(Registries.TINT_FACTORY); @@ -54,8 +53,9 @@ public class BuiltInRegistries { public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); public static final Registry HITBOX_FACTORY = createRegistry(Registries.HITBOX_FACTORY); public static final Registry RESOURCE_PACK_HOST_FACTORY = createRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); - public static final Registry>> PLAYER_BLOCK_FUNCTION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_FUNCTION_FACTORY); - public static final Registry>> PLAYER_BLOCK_CONDITION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_CONDITION_FACTORY); + public static final Registry> EVENT_FUNCTION_FACTORY = createRegistry(Registries.EVENT_FUNCTION_FACTORY); + public static final Registry> EVENT_CONDITION_FACTORY = createRegistry(Registries.EVENT_CONDITION_FACTORY); + public static final Registry> PLAYER_SELECTOR_FACTORY = createRegistry(Registries.PLAYER_SELECTOR_FACTORY); private static Registry createRegistry(ResourceKey> key) { return new MappedRegistry<>(key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 434302ba9..5d8ed2868 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -21,12 +21,11 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; -import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.plugin.context.PlayerBlockActionContext; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; -import net.momirealms.craftengine.core.util.Factory; +import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -40,7 +39,7 @@ public class Registries { public static final ResourceKey>> LOOT_FUNCTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("loot_function_factory")); public static final ResourceKey>> LOOT_ENTRY_CONTAINER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("loot_entry_container_factory")); public static final ResourceKey>> LOOT_CONDITION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("loot_condition_factory")); - public static final ResourceKey>> NUMBER_PROVIDER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("number_provider_factory")); + public static final ResourceKey> NUMBER_PROVIDER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("number_provider_factory")); public static final ResourceKey> TEMPLATE_ARGUMENT_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("template_argument_factory")); public static final ResourceKey> ITEM_MODEL_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("item_model_factory")); public static final ResourceKey> TINT_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("tint_factory")); @@ -55,6 +54,7 @@ public class Registries { public static final ResourceKey> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory")); public static final ResourceKey> HITBOX_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory")); public static final ResourceKey> RESOURCE_PACK_HOST_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory")); - public static final ResourceKey>>> PLAYER_BLOCK_FUNCTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("player_block_function_factory")); - public static final ResourceKey>>> PLAYER_BLOCK_CONDITION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("player_block_condition_factory")); + public static final ResourceKey>> EVENT_FUNCTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("event_function_factory")); + public static final ResourceKey>> EVENT_CONDITION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("event_condition_factory")); + public static final ResourceKey>> PLAYER_SELECTOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("player_selector")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index 789aa65d8..d75ee7ec8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; @@ -32,8 +32,8 @@ public abstract class AbstractSoundManager implements SoundManager { } @Override - public ConfigSectionParser[] parsers() { - return new ConfigSectionParser[] { this.soundParser, this.songParser }; + public ConfigParser[] parsers() { + return new ConfigParser[] { this.soundParser, this.songParser }; } @Override @@ -59,7 +59,7 @@ public abstract class AbstractSoundManager implements SoundManager { protected abstract void registerSongs(Map songs); - public class SongParser implements ConfigSectionParser { + public class SongParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"jukebox_songs", "song", "songs", "jukebox", "jukebox_song"}; @Override @@ -86,7 +86,7 @@ public abstract class AbstractSoundManager implements SoundManager { } } - public class SoundParser implements ConfigSectionParser { + public class SoundParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"sounds", "sound"}; @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundManager.java index 54450acd5..55eeb6e4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundManager.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.sound; import net.momirealms.craftengine.core.plugin.Manageable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; import java.util.Map; @@ -10,7 +10,7 @@ public interface SoundManager extends Manageable { boolean isVanillaSoundEvent(Key key); - ConfigSectionParser[] parsers(); + ConfigParser[] parsers(); Map sounds(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Cancellable.java b/core/src/main/java/net/momirealms/craftengine/core/util/Cancellable.java index d9decdb44..91452fc33 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Cancellable.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Cancellable.java @@ -1,8 +1,53 @@ package net.momirealms.craftengine.core.util; +import java.util.function.Consumer; +import java.util.function.Supplier; + public interface Cancellable { boolean isCancelled(); void setCancelled(boolean cancel); + + static Cancellable dummy() { + return new Dummy(); + } + + static Cancellable of(Supplier isCancelled, Consumer setCancelled) { + return new Simple(isCancelled, setCancelled); + } + + class Dummy implements Cancellable { + private boolean cancelled; + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + } + + class Simple implements Cancellable { + private final Supplier isCancelled; + private final Consumer setCancelled; + + public Simple(Supplier isCancelled, Consumer setCancelled) { + this.isCancelled = isCancelled; + this.setCancelled = setCancelled; + } + + @Override + public boolean isCancelled() { + return this.isCancelled.get(); + } + + @Override + public void setCancelled(boolean cancel) { + this.setCancelled.accept(cancel); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ClickType.java b/core/src/main/java/net/momirealms/craftengine/core/util/ClickType.java new file mode 100644 index 000000000..1489885b7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ClickType.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.util; + +public enum ClickType { + LEFT, RIGHT, MIDDLE +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java index a55f7047c..87cd8df73 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.util; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.world.Vec3i; import org.jetbrains.annotations.Nullable; @@ -93,7 +93,7 @@ public enum Direction { }; } - public static Direction[] orderedByNearest(Entity entity) { + public static Direction[] orderedByNearest(AbstractEntity entity) { float xRotation = entity.getXRot() * (float) (Math.PI / 180.0); float yRotation = -entity.getYRot() * (float) (Math.PI / 180.0); float sinX = (float) Math.sin(xRotation); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Factory.java b/core/src/main/java/net/momirealms/craftengine/core/util/Factory.java deleted file mode 100644 index ec3e37b2f..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Factory.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.util; - -import java.util.Map; - -public interface Factory { - - T create(Map args); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java index 0e8889039..a4aec22f6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java @@ -22,6 +22,31 @@ public class FileUtils { Files.createDirectories(Files.exists(path) ? path.toRealPath() : path); } + public static List getYmlConfigsDeeply(Path configFolder) { + if (!Files.exists(configFolder)) return List.of(); + List validYaml = new ArrayList<>(); + Deque pathDeque = new ArrayDeque<>(); + pathDeque.push(configFolder); + while (!pathDeque.isEmpty()) { + Path path = pathDeque.pop(); + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + for (Path subPath : stream) { + if (Files.isDirectory(subPath)) { + pathDeque.push(subPath); + } else if (Files.isRegularFile(subPath)) { + String pathString = subPath.toString(); + if (pathString.endsWith(".yml")) { + validYaml.add(subPath); + } + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return validYaml; + } + public static Pair, List> getConfigsDeeply(Path configFolder) { if (!Files.exists(configFolder)) return Pair.of(List.of(), List.of()); List validYaml = new ArrayList<>(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java b/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java index 5219bb4b4..8f12573cb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java @@ -458,6 +458,15 @@ public class FriendlyByteBuf extends ByteBuf { this.writeBytes(Arrays.copyOf(byteArray, MCUtils.positiveCeilDiv(size, 8))); } + @SuppressWarnings("unchecked") + public > T readEnumConstant(Class enumClass) { + return (T)((Enum[])enumClass.getEnumConstants())[this.readVarInt()]; + } + + public FriendlyByteBuf writeEnumConstant(Enum instance) { + return this.writeVarInt(instance.ordinal()); + } + @FunctionalInterface public interface Writer extends BiConsumer { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Key.java b/core/src/main/java/net/momirealms/craftengine/core/util/Key.java index ed43ffe8c..994cf4b6c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Key.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Key.java @@ -57,6 +57,10 @@ public record Key(String namespace, String value) { return namespace + ":" + value; } + public String asString() { + return namespace + ":" + value; + } + private static String[] decompose(String id, String namespace) { String[] strings = new String[]{namespace, id}; int i = id.indexOf(':'); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java index 9669fe006..7886f0e0f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java @@ -12,6 +12,8 @@ public class MCUtils { private MCUtils() {} + public static final float DEG_TO_RAD = ((float)Math.PI / 180F); + private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; private static final float[] SIN = make(new float[65536], (sineTable) -> { for(int i = 0; i < sineTable.length; ++i) { @@ -116,7 +118,6 @@ public class MCUtils { return false; } } - return true; }; } @@ -148,6 +149,70 @@ public class MCUtils { }; } + public static Predicate anyOf() { + return o -> false; + } + + @SuppressWarnings("unchecked") + public static Predicate anyOf(Predicate a) { + return (Predicate) a; + } + + public static Predicate anyOf(Predicate a, Predicate b) { + return o -> a.test(o) || b.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c) { + return o -> a.test(o) || b.test(o) || c.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d) { + return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d, Predicate e) { + return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o) || e.test(o); + } + + @SafeVarargs + public static Predicate anyOf(Predicate... predicates) { + return o -> { + for (Predicate predicate : predicates) { + if (predicate.test(o)) { + return true; + } + } + return false; + }; + } + + public static Predicate anyOf(List> predicates) { + return switch (predicates.size()) { + case 0 -> anyOf(); + case 1 -> anyOf((Predicate) predicates.get(0)); + case 2 -> anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1)); + case 3 -> anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1), (Predicate) predicates.get(2)); + case 4 -> anyOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3) + ); + case 5 -> anyOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3), + (Predicate) predicates.get(4) + ); + default -> { + @SuppressWarnings("unchecked") + Predicate[] predicates2 = predicates.toArray(Predicate[]::new); + yield anyOf(predicates2); + } + }; + } + public static T findPreviousInIterable(Iterable iterable, @Nullable T object) { Iterator iterator = iterable.iterator(); T previous = null; @@ -168,6 +233,14 @@ public class MCUtils { return SIN[(int) (value * 10430.378F) & '\uffff']; } + public static float cos(float value) { + return SIN[(int)(value * 10430.378F + 16384.0F) & '\uffff']; + } + + public static float sqrt(float value) { + return (float)Math.sqrt(value); + } + public static T findNextInIterable(Iterable iterable, @Nullable T object) { Iterator iterator = iterable.iterator(); T next = iterator.next(); @@ -184,4 +257,24 @@ public class MCUtils { } return next; } + + public static byte packDegrees(float degrees) { + return (byte) fastFloor(degrees * 256.0F / 360.0F); + } + + public static float unpackDegrees(byte degrees) { + return (float) (degrees * 360) / 256.0F; + } + + public static int clamp(int value, int min, int max) { + return Math.min(Math.max(value, min), max); + } + + public static float clamp(float value, float min, float max) { + return value < min ? min : Math.min(value, max); + } + + public static double clamp(double value, double min, double max) { + return value < min ? min : Math.min(value, max); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index c208c1c2f..3e9c4ba60 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -41,27 +41,35 @@ public class MiscUtils { public static Vector3f getAsVector3f(Object o, String option) { if (o == null) return new Vector3f(); - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 3) { - return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); - } else if (split.length == 1) { - return new Vector3f(Float.parseFloat(split[0])); + if (o instanceof List list && list.size() == 3) { + return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); } else { - throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 3) { + return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); + } else if (split.length == 1) { + return new Vector3f(Float.parseFloat(split[0])); + } else { + throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + } } } public static Quaternionf getAsQuaternionf(Object o, String option) { if (o == null) return new Quaternionf(); - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 4) { - return new Quaternionf(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2]), Float.parseFloat(split[3])); - } else if (split.length == 1) { - return QuaternionUtils.toQuaternionf(0, Math.toRadians(Float.parseFloat(split[0])), 0); + if (o instanceof List list && list.size() == 4) { + return new Quaternionf(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString()), Float.parseFloat(list.get(3).toString())); } else { - throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 4) { + return new Quaternionf(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2]), Float.parseFloat(split[3])); + } else if (split.length == 1) { + return QuaternionUtils.toQuaternionf(0, Math.toRadians(Float.parseFloat(split[0])), 0); + } else { + throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java index 230a0419e..4640a4140 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java @@ -4,10 +4,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.SectionPos; -import java.util.BitSet; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class SectionPosUtils { @@ -46,8 +43,8 @@ public class SectionPosUtils { return nearby; } - public static Set calculateAffectedRegions(int sx, int sy, int sz, int x) { - Set regions = new HashSet<>(); + public static List calculateAffectedRegions(int sx, int sy, int sz, int x) { + List regions = new ArrayList<>(); int rxStart = (sx - x) >> 4; int rxEnd = (sx + x) >> 4; int ryStart = (sy - x) >> 4; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java index 461ef2699..5dac8cf13 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java @@ -1,7 +1,9 @@ package net.momirealms.craftengine.core.world; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; -import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; public interface BlockInWorld { @@ -13,9 +15,15 @@ public interface BlockInWorld { return false; } - Key owner(); + @Nullable + CustomBlock customBlock(); - String getAsString(); + @Nullable + ImmutableBlockState customBlockState(); + + default WorldPosition position() { + return new WorldPosition(world(), x(), y(), z()); + } World world(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index e36abc7ce..2a636bd69 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -9,10 +9,10 @@ import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; +import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; public abstract class CEWorld { @@ -22,7 +22,7 @@ public abstract class CEWorld { protected final WorldDataStorage worldDataStorage; protected final ReentrantReadWriteLock loadedChunkMapLock = new ReentrantReadWriteLock(); protected final WorldHeight worldHeightAccessor; - protected final Set updatedSectionPositions = Collections.synchronizedSet(new HashSet<>()); + protected final Set updatedSectionSet = ConcurrentHashMap.newKeySet(128); private CEChunk lastChunk; private long lastChunkPos; @@ -165,11 +165,11 @@ public abstract class CEWorld { } public void sectionLightUpdated(SectionPos pos) { - this.updatedSectionPositions.add(pos); + this.updatedSectionSet.add(pos); } - public void sectionLightUpdated(Set pos) { - this.updatedSectionPositions.addAll(pos); + public void sectionLightUpdated(Collection pos) { + this.updatedSectionSet.addAll(pos); } public abstract void tick(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/HitResult.java b/core/src/main/java/net/momirealms/craftengine/core/world/HitResult.java index 4c1d08e71..a7a4aae69 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/HitResult.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/HitResult.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.world; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.AbstractEntity; public abstract class HitResult { protected final Vec3d location; @@ -9,7 +9,7 @@ public abstract class HitResult { this.location = pos; } - public double distanceTo(Entity entity) { + public double distanceTo(AbstractEntity entity) { double d = this.location.x() - entity.x(); double e = this.location.y() - entity.y(); double f = this.location.z() - entity.z(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/World.java b/core/src/main/java/net/momirealms/craftengine/core/world/World.java index 5817a5e1f..565d93067 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/World.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/World.java @@ -27,13 +27,15 @@ public interface World { UUID uuid(); - void dropItemNaturally(Vec3d location, Item item); + void dropItemNaturally(Position location, Item item); - void dropExp(Vec3d location, int amount); + void dropExp(Position location, int amount); - void playBlockSound(Vec3d location, Key sound, float volume, float pitch); + void playBlockSound(Position location, Key sound, float volume, float pitch); - default void playBlockSound(Vec3d location, SoundData data) { + default void playBlockSound(Position location, SoundData data) { playBlockSound(location, data.id(), data.volume(), data.pitch()); } + + long time(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java new file mode 100644 index 000000000..868538ea6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java @@ -0,0 +1,73 @@ +package net.momirealms.craftengine.core.world; + +public class WorldPosition implements Position { + private final World world; + private final double x; + private final double y; + private final double z; + private final float xRot; + private final float yRot; + + public WorldPosition(World world, Position position) { + this.x = position.x(); + this.y = position.y(); + this.z = position.z(); + this.world = world; + this.xRot = 0f; + this.yRot = 0f; + } + + public WorldPosition(World world, Position position, float xRot, float yRot) { + this.x = position.x(); + this.y = position.y(); + this.z = position.z(); + this.world = world; + this.xRot = xRot; + this.yRot = yRot; + } + + public WorldPosition(World world, double x, double y, double z) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.xRot = 0f; + this.yRot = 0f; + } + + public WorldPosition(World world, double x, double y, double z, float xRot, float yRot) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.xRot = xRot; + this.yRot = yRot; + } + + @Override + public double x() { + return x; + } + + @Override + public double y() { + return y; + } + + @Override + public double z() { + return z; + } + + public World world() { + return world; + } + + public float xRot() { + return xRot; + } + + public float yRot() { + return yRot; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java index 0ee384791..b2e8ad51b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java @@ -11,7 +11,6 @@ import java.io.*; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; -import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; diff --git a/gradle.properties b/gradle.properties index f57da2123..d97675755 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.53 +project_version=0.0.54 config_version=32 lang_version=12 project_group=net.momirealms @@ -21,7 +21,7 @@ asm_version=9.8 asm_commons_version=9.8 jar_relocator_version=1.7 adventure_bundle_version=4.21.0 -adventure_platform_version=4.3.4 +adventure_platform_version=4.4.0 cloud_core_version=2.0.0 cloud_services_version=2.0.0 cloud_brigadier_version=2.0.0-beta.10 @@ -39,8 +39,9 @@ lz4_version=1.8.0 geantyref_version=1.3.16 zstd_version=1.5.7-2 commons_io_version=2.18.0 +commons_imaging_version=1.0.0-alpha6 sparrow_nbt_version=0.7.3 -sparrow_util_version=0.40 +sparrow_util_version=0.47 fastutil_version=8.5.15 netty_version=4.1.119.Final joml_version=1.10.8 @@ -50,11 +51,12 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.15 -nms_helper_version=0.65.15 +nms_helper_version=0.65.24 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23 amazon_awssdk_eventstream_version=1.0.1 +jimfs_version=1.3.0 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7 diff --git a/server-mod/v1_20_1/build.gradle.kts b/server-mod/v1_20_1/build.gradle.kts index 8589cbe10..0266dd510 100644 --- a/server-mod/v1_20_1/build.gradle.kts +++ b/server-mod/v1_20_1/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java-library") - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" id("io.papermc.paperweight.userdev") version "2.0.0-beta.16" } diff --git a/server-mod/v1_20_1/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java b/server-mod/v1_20_1/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java index 09f390769..52214c52a 100644 --- a/server-mod/v1_20_1/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java +++ b/server-mod/v1_20_1/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java @@ -18,15 +18,15 @@ import net.momirealms.craftengine.mod.CraftEnginePlugin; import net.momirealms.craftengine.mod.util.NoteBlockUtils; import net.momirealms.craftengine.shared.ObjectHolder; import net.momirealms.craftengine.shared.block.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @SuppressWarnings("deprecation") -public class CraftEngineBlock - extends Block - implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock - //TODO , SimpleWaterloggedBlock -{ +public class CraftEngineBlock extends Block + implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock { private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState()); + private static final Logger LOGGER = LogManager.getLogger(CraftEngineBlock.class); private boolean isNoteBlock; public ObjectHolder behaviorHolder; public ObjectHolder shapeHolder; @@ -39,30 +39,30 @@ public class CraftEngineBlock } public void setNoteBlock(boolean noteBlock) { - isNoteBlock = noteBlock; + this.isNoteBlock = noteBlock; } @Override public ObjectHolder getBehaviorHolder() { - return behaviorHolder; + return this.behaviorHolder; } @Override public ObjectHolder getShapeHolder() { - return shapeHolder; + return this.shapeHolder; } @Override public boolean isNoteBlock() { - return isClientSideNoteBlock; + return this.isClientSideNoteBlock; } @Override public @NotNull VoxelShape getShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) { try { - return (VoxelShape) shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); + return (VoxelShape) this.shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.getShape(state, level, pos, context); } } @@ -72,7 +72,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().rotate(this, new Object[]{state, rotation}, () -> super.rotate(state, rotation)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.rotate(state, rotation); } } @@ -82,7 +82,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().mirror(this, new Object[]{state, mirror}, () -> super.mirror(state, mirror)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.mirror(state, mirror); } } @@ -95,7 +95,7 @@ public class CraftEngineBlock return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.tick(state, level, pos, random); } } @@ -103,12 +103,12 @@ public class CraftEngineBlock @Override public void randomTick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) { try { - behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> { + this.behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> { super.randomTick(state, level, pos, random); return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.randomTick(state, level, pos, random); } } @@ -116,12 +116,12 @@ public class CraftEngineBlock @Override public void onPlace(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState oldState, boolean movedByPiston) { try { - behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> { + this.behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> { super.onPlace(state, level, pos, oldState, movedByPiston); return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.onPlace(state, level, pos, oldState, movedByPiston); } } @@ -129,9 +129,9 @@ public class CraftEngineBlock @Override public void onBrokenAfterFall(@NotNull Level level, @NotNull BlockPos pos, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); + this.behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); Fallable.super.onBrokenAfterFall(level, pos, fallingBlock); } } @@ -139,27 +139,27 @@ public class CraftEngineBlock @Override public boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) { try { - return behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); + return this.behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.canSurvive(state, level, pos); } } @Override - public BlockState updateShape(@NotNull BlockState state, - @NotNull Direction direction, - @NotNull BlockState neighborState, - @NotNull LevelAccessor world, - @NotNull BlockPos pos, - @NotNull BlockPos neighborPos) { + public @NotNull BlockState updateShape(@NotNull BlockState state, + @NotNull Direction direction, + @NotNull BlockState neighborState, + @NotNull LevelAccessor world, + @NotNull BlockPos pos, + @NotNull BlockPos neighborPos) { try { - if (isNoteBlock && world instanceof ServerLevel serverLevel) { + if (this.isNoteBlock && world instanceof ServerLevel serverLevel) { startNoteBlockChain(direction, serverLevel, pos); } - return (BlockState) behaviorHolder.value().updateShape(this, new Object[]{state, direction, neighborState, world, pos, neighborPos}, () -> super.updateShape(state, direction, neighborState, world, pos, neighborPos)); + return (BlockState) this.behaviorHolder.value().updateShape(this, new Object[]{state, direction, neighborState, world, pos, neighborPos}, () -> super.updateShape(state, direction, neighborState, world, pos, neighborPos)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.updateShape(state, direction, neighborState, world, pos, neighborPos); } } @@ -191,9 +191,9 @@ public class CraftEngineBlock @Override public boolean isValidBonemealTarget(@NotNull LevelReader world, @NotNull BlockPos pos, @NotNull BlockState state, boolean isClient) { try { - return behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{world, pos, state, isClient}); + return this.behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{world, pos, state, isClient}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -203,7 +203,7 @@ public class CraftEngineBlock try { return behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -211,49 +211,18 @@ public class CraftEngineBlock @Override public void performBonemeal(@NotNull ServerLevel serverLevel, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); + this.behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } @Override public void onLand(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull BlockState replaceableState, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); + this.behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } - -// @Override -// public boolean canPlaceLiquid(@Nullable Player player, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull Fluid fluid) { -// try { -// return behaviorHolder.value().canPlaceLiquid(this, new Object[]{player, level, pos, state, fluid}, () -> SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid); -// } -// } -// -// @Override -// public boolean placeLiquid(@NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull FluidState fluidState) { -// try { -// return behaviorHolder.value().placeLiquid(this, new Object[]{level, pos, state, fluidState}, () -> SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState); -// } -// } -// -// @NotNull -// @Override -// public ItemStack pickupBlock(@Nullable Player player, @NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state) { -// try { -// return (ItemStack) behaviorHolder.value().pickupBlock(this, new Object[]{player, level, pos, state}, () -> SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state); -// } -// } } diff --git a/server-mod/v1_20_5/build.gradle.kts b/server-mod/v1_20_5/build.gradle.kts index 272257de9..db31453d2 100644 --- a/server-mod/v1_20_5/build.gradle.kts +++ b/server-mod/v1_20_5/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java-library") - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" id("io.papermc.paperweight.userdev") version "2.0.0-beta.16" } diff --git a/server-mod/v1_20_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java b/server-mod/v1_20_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java index 8b3039c7b..4e37a60ef 100644 --- a/server-mod/v1_20_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java +++ b/server-mod/v1_20_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java @@ -18,14 +18,14 @@ import net.momirealms.craftengine.mod.CraftEnginePlugin; import net.momirealms.craftengine.mod.util.NoteBlockUtils; import net.momirealms.craftengine.shared.ObjectHolder; import net.momirealms.craftengine.shared.block.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -public class CraftEngineBlock - extends Block - implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock - //TODO , SimpleWaterloggedBlock -{ +public class CraftEngineBlock extends Block + implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock { private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState()); + private static final Logger LOGGER = LogManager.getLogger(CraftEngineBlock.class); private boolean isNoteBlock; public ObjectHolder behaviorHolder; public ObjectHolder shapeHolder; @@ -38,30 +38,30 @@ public class CraftEngineBlock } public void setNoteBlock(boolean noteBlock) { - isNoteBlock = noteBlock; + this.isNoteBlock = noteBlock; } @Override public ObjectHolder getBehaviorHolder() { - return behaviorHolder; + return this.behaviorHolder; } @Override public ObjectHolder getShapeHolder() { - return shapeHolder; + return this.shapeHolder; } @Override public boolean isNoteBlock() { - return isClientSideNoteBlock; + return this.isClientSideNoteBlock; } @Override protected @NotNull VoxelShape getShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) { try { - return (VoxelShape) shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); + return (VoxelShape) this.shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.getShape(state, level, pos, context); } } @@ -71,7 +71,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().rotate(this, new Object[]{state, rotation}, () -> super.rotate(state, rotation)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.rotate(state, rotation); } } @@ -81,7 +81,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().mirror(this, new Object[]{state, mirror}, () -> super.mirror(state, mirror)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.mirror(state, mirror); } } @@ -94,7 +94,7 @@ public class CraftEngineBlock return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.tick(state, level, pos, random); } } @@ -102,12 +102,12 @@ public class CraftEngineBlock @Override protected void randomTick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) { try { - behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> { + this.behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> { super.randomTick(state, level, pos, random); return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.randomTick(state, level, pos, random); } } @@ -115,12 +115,12 @@ public class CraftEngineBlock @Override protected void onPlace(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState oldState, boolean movedByPiston) { try { - behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> { + this.behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> { super.onPlace(state, level, pos, oldState, movedByPiston); return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.onPlace(state, level, pos, oldState, movedByPiston); } } @@ -128,9 +128,9 @@ public class CraftEngineBlock @Override public void onBrokenAfterFall(@NotNull Level level, @NotNull BlockPos pos, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); + this.behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); Fallable.super.onBrokenAfterFall(level, pos, fallingBlock); } } @@ -138,29 +138,29 @@ public class CraftEngineBlock @Override protected boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) { try { - return behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); + return this.behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.canSurvive(state, level, pos); } } @Override - protected BlockState updateShape(@NotNull BlockState state, - @NotNull LevelReader level, - @NotNull ScheduledTickAccess scheduledTickAccess, - @NotNull BlockPos pos, - @NotNull Direction direction, - @NotNull BlockPos neighborPos, - @NotNull BlockState neighborState, - @NotNull RandomSource random) { + protected @NotNull BlockState updateShape(@NotNull BlockState state, + @NotNull LevelReader level, + @NotNull ScheduledTickAccess scheduledTickAccess, + @NotNull BlockPos pos, + @NotNull Direction direction, + @NotNull BlockPos neighborPos, + @NotNull BlockState neighborState, + @NotNull RandomSource random) { try { - if (isNoteBlock && level instanceof ServerLevel serverLevel) { + if (this.isNoteBlock && level instanceof ServerLevel serverLevel) { startNoteBlockChain(direction, serverLevel, pos); } - return (BlockState) behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random)); + return (BlockState) this.behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); } } @@ -189,22 +189,12 @@ public class CraftEngineBlock } } -// @Override -// protected @NotNull FluidState getFluidState(@NotNull BlockState state) { -// try { -// return (FluidState) behaviorHolder.value().getFluidState(this, new Object[]{state}, () -> super.getFluidState(state)); -// } catch (Exception e) { -// e.printStackTrace(); -// return super.getFluidState(state); -// } -// } - @Override public boolean isValidBonemealTarget(@NotNull LevelReader levelReader, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - return behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState}); + return this.behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -212,9 +202,9 @@ public class CraftEngineBlock @Override public boolean isBonemealSuccess(@NotNull Level level, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - return behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState}); + return this.behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -222,49 +212,18 @@ public class CraftEngineBlock @Override public void performBonemeal(@NotNull ServerLevel serverLevel, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); + this.behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } @Override public void onLand(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull BlockState replaceableState, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); + this.behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } - -// @Override -// public boolean canPlaceLiquid(@Nullable Player player, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull Fluid fluid) { -// try { -// return behaviorHolder.value().canPlaceLiquid(this, new Object[]{player, level, pos, state, fluid}, () -> SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid); -// } -// } -// -// @Override -// public boolean placeLiquid(@NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull FluidState fluidState) { -// try { -// return behaviorHolder.value().placeLiquid(this, new Object[]{level, pos, state, fluidState}, () -> SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState); -// } -// } -// -// @NotNull -// @Override -// public ItemStack pickupBlock(@Nullable Player player, @NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state) { -// try { -// return (ItemStack) behaviorHolder.value().pickupBlock(this, new Object[]{player, level, pos, state}, () -> SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state); -// } -// } } diff --git a/server-mod/v1_21_5/build.gradle.kts b/server-mod/v1_21_5/build.gradle.kts index 0be3a008a..617f52ce2 100644 --- a/server-mod/v1_21_5/build.gradle.kts +++ b/server-mod/v1_21_5/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java-library") - id("com.gradleup.shadow") version "9.0.0-beta11" + id("com.gradleup.shadow") version "9.0.0-beta13" id("io.papermc.paperweight.userdev") version "2.0.0-beta.16" } diff --git a/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/CraftEnginePlugin.java b/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/CraftEnginePlugin.java index c0b7d7bf1..6cb356a90 100644 --- a/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/CraftEnginePlugin.java +++ b/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/CraftEnginePlugin.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.mod; -import net.minecraft.world.level.chunk.LevelChunkSection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.objectweb.asm.tree.ClassNode; diff --git a/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java b/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java index 8b3039c7b..212e34087 100644 --- a/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java +++ b/server-mod/v1_21_5/src/main/java/net/momirealms/craftengine/mod/block/CraftEngineBlock.java @@ -18,14 +18,14 @@ import net.momirealms.craftengine.mod.CraftEnginePlugin; import net.momirealms.craftengine.mod.util.NoteBlockUtils; import net.momirealms.craftengine.shared.ObjectHolder; import net.momirealms.craftengine.shared.block.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -public class CraftEngineBlock - extends Block - implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock - //TODO , SimpleWaterloggedBlock -{ +public class CraftEngineBlock extends Block + implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock { private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState()); + private static final Logger LOGGER = LogManager.getLogger(CraftEngineBlock.class); private boolean isNoteBlock; public ObjectHolder behaviorHolder; public ObjectHolder shapeHolder; @@ -38,30 +38,30 @@ public class CraftEngineBlock } public void setNoteBlock(boolean noteBlock) { - isNoteBlock = noteBlock; + this.isNoteBlock = noteBlock; } @Override public ObjectHolder getBehaviorHolder() { - return behaviorHolder; + return this.behaviorHolder; } @Override public ObjectHolder getShapeHolder() { - return shapeHolder; + return this.shapeHolder; } @Override public boolean isNoteBlock() { - return isClientSideNoteBlock; + return this.isClientSideNoteBlock; } @Override protected @NotNull VoxelShape getShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) { try { - return (VoxelShape) shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); + return (VoxelShape) this.shapeHolder.value().getShape(this, new Object[]{state, level, pos, context}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.getShape(state, level, pos, context); } } @@ -71,7 +71,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().rotate(this, new Object[]{state, rotation}, () -> super.rotate(state, rotation)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.rotate(state, rotation); } } @@ -81,7 +81,7 @@ public class CraftEngineBlock try { return (BlockState) this.behaviorHolder.value().mirror(this, new Object[]{state, mirror}, () -> super.mirror(state, mirror)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.mirror(state, mirror); } } @@ -94,7 +94,7 @@ public class CraftEngineBlock return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.tick(state, level, pos, random); } } @@ -107,7 +107,7 @@ public class CraftEngineBlock return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.randomTick(state, level, pos, random); } } @@ -120,7 +120,7 @@ public class CraftEngineBlock return null; }); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); super.onPlace(state, level, pos, oldState, movedByPiston); } } @@ -128,9 +128,9 @@ public class CraftEngineBlock @Override public void onBrokenAfterFall(@NotNull Level level, @NotNull BlockPos pos, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); + this.behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); Fallable.super.onBrokenAfterFall(level, pos, fallingBlock); } } @@ -138,29 +138,29 @@ public class CraftEngineBlock @Override protected boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) { try { - return behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); + return this.behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.canSurvive(state, level, pos); } } @Override - protected BlockState updateShape(@NotNull BlockState state, - @NotNull LevelReader level, - @NotNull ScheduledTickAccess scheduledTickAccess, - @NotNull BlockPos pos, - @NotNull Direction direction, - @NotNull BlockPos neighborPos, - @NotNull BlockState neighborState, - @NotNull RandomSource random) { + protected @NotNull BlockState updateShape(@NotNull BlockState state, + @NotNull LevelReader level, + @NotNull ScheduledTickAccess scheduledTickAccess, + @NotNull BlockPos pos, + @NotNull Direction direction, + @NotNull BlockPos neighborPos, + @NotNull BlockState neighborState, + @NotNull RandomSource random) { try { - if (isNoteBlock && level instanceof ServerLevel serverLevel) { + if (this.isNoteBlock && level instanceof ServerLevel serverLevel) { startNoteBlockChain(direction, serverLevel, pos); } - return (BlockState) behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random)); + return (BlockState) this.behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random)); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); } } @@ -189,22 +189,12 @@ public class CraftEngineBlock } } -// @Override -// protected @NotNull FluidState getFluidState(@NotNull BlockState state) { -// try { -// return (FluidState) behaviorHolder.value().getFluidState(this, new Object[]{state}, () -> super.getFluidState(state)); -// } catch (Exception e) { -// e.printStackTrace(); -// return super.getFluidState(state); -// } -// } - @Override public boolean isValidBonemealTarget(@NotNull LevelReader levelReader, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - return behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState}); + return this.behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -212,9 +202,9 @@ public class CraftEngineBlock @Override public boolean isBonemealSuccess(@NotNull Level level, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - return behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState}); + return this.behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); return false; } } @@ -222,49 +212,18 @@ public class CraftEngineBlock @Override public void performBonemeal(@NotNull ServerLevel serverLevel, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) { try { - behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); + this.behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } @Override public void onLand(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull BlockState replaceableState, @NotNull FallingBlockEntity fallingBlock) { try { - behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); + this.behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock}); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e); } } - -// @Override -// public boolean canPlaceLiquid(@Nullable Player player, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull Fluid fluid) { -// try { -// return behaviorHolder.value().canPlaceLiquid(this, new Object[]{player, level, pos, state, fluid}, () -> SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid); -// } -// } -// -// @Override -// public boolean placeLiquid(@NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull FluidState fluidState) { -// try { -// return behaviorHolder.value().placeLiquid(this, new Object[]{level, pos, state, fluidState}, () -> SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState); -// } -// } -// -// @NotNull -// @Override -// public ItemStack pickupBlock(@Nullable Player player, @NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state) { -// try { -// return (ItemStack) behaviorHolder.value().pickupBlock(this, new Object[]{player, level, pos, state}, () -> SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state)); -// } catch (Exception e) { -// e.printStackTrace(); -// return SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state); -// } -// } } diff --git a/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java b/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java index 67c22fbd8..24e08c8b3 100644 --- a/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java +++ b/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java @@ -48,20 +48,4 @@ public abstract class BlockBehavior { public void performBoneMeal(Object thisBlock, Object[] args) throws Exception { } - -// -// public Object getFluidState(Object thisBlock, Object[] args, Callable superMethod) throws Exception { -// return superMethod.call(); -// } -// public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable superMethod) throws Exception { -// return (boolean) superMethod.call(); -// } -// -// public boolean placeLiquid(Object thisBlock, Object[] args, Callable superMethod) throws Exception { -// return (boolean) superMethod.call(); -// } -// -// public Object pickupBlock(Object thisBlock, Object[] args, Callable superMethod) throws Exception { -// return superMethod.call(); -// } }