diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 356626a34..7b21b01a6 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -5,9 +5,7 @@ plugins { repositories { maven("https://jitpack.io/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi maven("https://repo.papermc.io/repository/maven-public/") - maven("https://maven.enginehub.org/repo/") // worldguard worldedit maven("https://repo.momirealms.net/releases/") mavenCentral() } @@ -24,8 +22,6 @@ dependencies { compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") // NMS compileOnly("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}") - // Placeholder - compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") // Platform compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") // OpenGL Math @@ -56,8 +52,6 @@ dependencies { compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") { exclude("com.google.code.gson", "gson") } - compileOnly("com.sk89q.worldedit:worldedit-core:7.2.19") - compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19") // Data Fixer Upper compileOnly("com.mojang:datafixerupper:${rootProject.properties["datafixerupper_version"]}") // BStats diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index bc8304b72..71a1c68ae 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -2,14 +2,27 @@ repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") maven("https://r.irepo.space/maven/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi + maven("https://maven.enginehub.org/repo/") // worldguard worldedit + maven("https://repo.rapture.pw/repository/maven-releases/") // slime world + maven("https://repo.infernalsuite.com/repository/maven-snapshots/") // slime world + maven("https://repo.momirealms.net/releases/") } dependencies { compileOnly(project(":core")) + compileOnly("net.momirealms:sparrow-nbt:${rootProject.properties["sparrow_nbt_version"]}") // Platform compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") // NeigeItems compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42") + // Placeholder + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + // WorldEdit + compileOnly("com.sk89q.worldedit:worldedit-core:7.2.19") + compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19") + // SlimeWorld + compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT") } java { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ImageExpansion.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ImageExpansion.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ImageExpansion.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ImageExpansion.java index 9a5ca429c..4f527f7af 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ImageExpansion.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ImageExpansion.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.papi; +package net.momirealms.craftengine.bukkit.compatibility.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.momirealms.craftengine.core.font.BitmapImage; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlaceholderAPIUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java similarity index 50% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlaceholderAPIUtils.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java index 200ab0b88..824236dda 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlaceholderAPIUtils.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java @@ -1,6 +1,7 @@ -package net.momirealms.craftengine.bukkit.util; +package net.momirealms.craftengine.bukkit.compatibility.papi; import me.clip.placeholderapi.PlaceholderAPI; +import net.momirealms.craftengine.core.plugin.CraftEngine; import org.bukkit.OfflinePlayer; public class PlaceholderAPIUtils { @@ -10,4 +11,9 @@ public class PlaceholderAPIUtils { public static String parse(OfflinePlayer player, String text) { return PlaceholderAPI.setPlaceholders(player, text); } + + public static void registerExpansions(CraftEngine plugin) { + new ImageExpansion(plugin).register(); + new ShiftExpansion(plugin).register(); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ShiftExpansion.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ShiftExpansion.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ShiftExpansion.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ShiftExpansion.java index f8650a55d..ea10750f9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/papi/ShiftExpansion.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/ShiftExpansion.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.papi; +package net.momirealms.craftengine.bukkit.compatibility.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.momirealms.craftengine.core.plugin.CraftEngine; diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java new file mode 100644 index 000000000..b01a6e1c1 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java @@ -0,0 +1,63 @@ +package net.momirealms.craftengine.bukkit.compatibility.slimeworld; + +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.events.LoadSlimeWorldEvent; +import com.infernalsuite.asp.api.world.SlimeWorld; +import net.momirealms.craftengine.core.util.ReflectionUtils; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldManager; +import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; + +public class SlimeFormatStorageAdaptor extends DefaultStorageAdaptor implements Listener { + private final WorldManager worldManager; + private final Class byteArrayTagClass = ReflectionUtils.getClazz("net{}kyori{}adventure{}nbt{}ByteArrayBinaryTag".replace("{}", ".")); + private final Method method$ByteArrayBinaryTag$byteArrayBinaryTag = ReflectionUtils.getStaticMethod(byteArrayTagClass, byteArrayTagClass, byte.class.arrayType()); + private final Method method$ByteArrayBinaryTag$value = ReflectionUtils.getMethod(byteArrayTagClass, byte.class.arrayType()); + + @EventHandler + public void onWorldLoad(LoadSlimeWorldEvent event) { + org.bukkit.World world = Bukkit.getWorld(event.getSlimeWorld().getName()); + this.worldManager.loadWorld(this.worldManager.createWorld(this.worldManager.wrap(world), new SlimeWorldDataStorage(event.getSlimeWorld(), this))); + } + + public SlimeFormatStorageAdaptor(WorldManager worldManager) { + this.worldManager = worldManager; + } + + public SlimeWorld getWorld(String name) { + return AdvancedSlimePaperAPI.instance().getLoadedWorld(name); + } + + // 请注意,在加载事件的时候,无法通过AdvancedSlimePaperAPI.instance().getLoadedWorld来判断是否为slime世界 + @Override + public @NotNull WorldDataStorage adapt(@NotNull World world) { + SlimeWorld slimeWorld = getWorld(world.name()); + if (slimeWorld == null) { + return super.adapt(world); + } + return new SlimeWorldDataStorage(slimeWorld, this); + } + + public byte[] byteArrayTagToBytes(Object byteArrayTag) { + try { + return (byte[]) method$ByteArrayBinaryTag$value.invoke(byteArrayTag); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to convert byte array tag to byte[]", e); + } + } + + public Object bytesToByteArrayTag(byte[] bytes) { + try { + return method$ByteArrayBinaryTag$byteArrayBinaryTag.invoke(null, (Object) bytes); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to convert byte array tag to byte[]", e); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java new file mode 100644 index 000000000..feeb6505f --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java @@ -0,0 +1,70 @@ +package net.momirealms.craftengine.bukkit.compatibility.slimeworld; + +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeWorld; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Map; + +public class SlimeWorldDataStorage implements WorldDataStorage { + private final WeakReference slimeWorld; + private final SlimeFormatStorageAdaptor adaptor; + + public SlimeWorldDataStorage(SlimeWorld slimeWorld, SlimeFormatStorageAdaptor adaptor) { + this.slimeWorld = new WeakReference<>(slimeWorld); + this.adaptor = adaptor; + } + + public SlimeWorld getWorld() { + return slimeWorld.get(); + } + + @Nullable + @Override + public CompoundTag readChunkTagAt(ChunkPos pos) { + SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); + if (slimeChunk == null) return null; + Object tag = slimeChunk.getExtraData().get("craftengine"); + if (tag == null) return null; + try { + return NBT.readCompound(new DataInputStream(new ByteArrayInputStream(adaptor.byteArrayTagToBytes(tag)))); + } catch (Exception e) { + throw new RuntimeException("Failed to read chunk tag from slime world. " + pos, e); + } + } + + @SuppressWarnings("unchecked") + @Override + public void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) { + SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); + if (slimeChunk == null) return; + if (nbt == null) { + slimeChunk.getExtraData().remove("craftengine"); + } else { + try { + Object tag = adaptor.bytesToByteArrayTag(NBT.toBytes(nbt)); + Map data1 = slimeChunk.getExtraData(); + Map data2 = (Map) data1; + data2.put("craftengine", tag); + } catch (Exception e) { + throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e); + } + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws IOException { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditBlockRegister.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java similarity index 93% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditBlockRegister.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java index 279ada1c2..6905c77c7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditBlockRegister.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.block.worldedit; +package net.momirealms.craftengine.bukkit.compatibility.worldedit; import com.sk89q.worldedit.bukkit.BukkitBlockRegistry; import com.sk89q.worldedit.util.concurrency.LazyReference; diff --git a/bukkit/loader/src/main/resources/additional-real-blocks.yml b/bukkit/loader/src/main/resources/additional-real-blocks.yml index 91993a87d..4fe5d6d61 100644 --- a/bukkit/loader/src/main/resources/additional-real-blocks.yml +++ b/bukkit/loader/src/main/resources/additional-real-blocks.yml @@ -14,9 +14,7 @@ # Q: When do I need to configure this file? # A: When the number of real block IDs is insufficient, but there are still available appearances. -# By default, the plugin only registers an additional 112 oak leaf block states (for the default configuration needs [>=28 states]). minecraft:oak_leaves: 112 - minecraft:oak_sapling: 1 minecraft:birch_sapling: 1 minecraft:spruce_sapling: 1 @@ -24,5 +22,5 @@ minecraft:jungle_sapling: 1 minecraft:dark_oak_sapling: 1 minecraft:acacia_sapling: 1 minecraft:cherry_sapling: 1 - -minecraft:anvil: 2 \ No newline at end of file +minecraft:anvil: 2 +minecraft:sugarcane: 14 \ 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 7df2d92c5..3c8d218d6 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/categories.yml @@ -59,5 +59,7 @@ categories: - default:netherite_anvil - default:fairy_flower - default:reed + - default:flame_cane - default:gunpowder_block - - default:solid_gunpowder_block \ No newline at end of file + - default:solid_gunpowder_block + - default:ender_pearl_flower_seeds \ 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 c3ad09c0a..51b178e1b 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml @@ -3,6 +3,8 @@ i18n: item.chinese_lantern: "Chinese Lantern" item.fairy_flower: "Fairy Flower" item.reed: "Reed" + item.flame_cane: "Flame Cane" + item.ender_pearl_flower_seeds: "Ender Pearl Flower Seeds" item.bench: "Bench" item.table_lamp: "Table Lamp" item.wooden_chair: "Wooden Chair" @@ -41,6 +43,8 @@ i18n: item.chinese_lantern: "灯笼" item.fairy_flower: "仙灵花" item.reed: "芦苇" + item.flame_cane: "烈焰甘蔗" + item.ender_pearl_flower_seeds: "末影珍珠花种子" item.bench: "长椅" item.table_lamp: "台灯" item.wooden_chair: "木椅" diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml b/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml index 47cda9d4c..6b2db4498 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml @@ -2,7 +2,7 @@ images: default:icons: height: 10 ascent: 9 - font: minecraft:default # Consider using other fonts if other plugins support custom font! + font: minecraft:icons # Do not use 'minecraft:default' unless other plugins don't support custom font! file: minecraft:font/image/icons.png chars: - '\ub000\ub001' diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml index 8f4d7e973..6cb154363 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml @@ -23,6 +23,30 @@ items: behavior: type: liquid_collision_block_item block: default:reed + default:flame_cane: + material: paper + custom-model-data: 4002 + data: + item-name: "" + model: + template: default:model/simplified_generated + arguments: + path: "minecraft:item/custom/flame_cane" + behavior: + type: block_item + block: default:flame_cane + default:ender_pearl_flower_seeds: + material: paper + custom-model-data: 4003 + data: + item-name: "" + model: + template: default:model/simplified_generated + arguments: + path: "minecraft:item/custom/ender_pearl_flower_seeds" + behavior: + type: block_item + block: default:ender_pearl_flower blocks: default:fairy_flower: settings: @@ -32,8 +56,12 @@ blocks: overrides: item: default:fairy_flower push-reaction: DESTROY + map-color: 19 behavior: type: bush_block + bottom-block-tags: + - minecraft:dirt + - minecraft:farmland loot: template: "default:loot_table/basic" arguments: @@ -70,6 +98,7 @@ blocks: overrides: item: default:reed push-reaction: DESTROY + map-color: 60 behavior: type: on_liquid_block liquid-type: water @@ -82,6 +111,137 @@ blocks: state: sugar_cane:1 model: path: "minecraft:block/custom/reed" + default:flame_cane: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + item: default:flame_cane + push-reaction: DESTROY + map-color: 15 + is-randomly-ticking: true + behavior: + type: sugar_cane_block + max-height: 4 + required-adjacent-liquids: lava + grow-speed: 0.333 + bottom-blocks: + - minecraft:netherrack + - minecraft:soul_sand + - minecraft:soul_soil + - minecraft:magma_block + - minecraft:warped_nylium + - minecraft:crimson_nylium + - minecraft:basalt + loot: + template: "default:loot_table/basic" + arguments: + item: default:flame_cane + states: + properties: + age: + type: int + default: 0 + range: 0~5 + appearances: + default: + state: "sugar_cane:2" + models: + - path: "minecraft:block/custom/flame_cane_1" + weight: 1 + generation: + parent: "minecraft:block/sugar_cane" + textures: + "cross": "minecraft:block/custom/flame_cane_1" + - path: "minecraft:block/custom/flame_cane_2" + weight: 1 + generation: + parent: "minecraft:block/sugar_cane" + textures: + "cross": "minecraft:block/custom/flame_cane_2" + variants: + age=0: + appearance: default + id: 2 + age=1: + appearance: default + id: 3 + age=2: + appearance: default + id: 4 + age=3: + appearance: default + id: 5 + age=4: + appearance: default + id: 6 + age=5: + appearance: default + id: 7 + default:ender_pearl_flower: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + item: default:ender_pearl_flower_seeds + push-reaction: DESTROY + map-color: 24 + is-randomly-ticking: true + behavior: + type: crop_block + grow-speed: 0.25 + light-requirement: 9 + bottom-blocks: + - minecraft:end_stone + loot: + template: default:loot_table/seed_crop + arguments: + crop_item: minecraft:ender_pearl + crop_seed: default:ender_pearl_flower_seeds + ripe_age: 2 + states: + properties: + age: + type: int + default: 0 + range: 0~2 + appearances: + stage_0: + state: "tripwire:1" + models: + - path: "minecraft:block/custom/ender_pearl_flower_stage_0" + generation: + parent: "minecraft:block/cross" + textures: + "cross": "minecraft:block/custom/ender_pearl_flower_stage_0" + stage_1: + state: "tripwire:0" + models: + - path: "minecraft:block/custom/ender_pearl_flower_stage_1" + generation: + parent: "minecraft:block/cross" + textures: + "cross": "minecraft:block/custom/ender_pearl_flower_stage_1" + stage_2: + state: "sugar_cane:3" + models: + - path: "minecraft:block/custom/ender_pearl_flower_stage_2" + generation: + parent: "minecraft:block/cross" + textures: + "cross": "minecraft:block/custom/ender_pearl_flower_stage_2" + variants: + age=0: + appearance: stage_0 + id: 0 + age=1: + appearance: stage_1 + id: 1 + age=2: + appearance: stage_2 + id: 8 recipes: default:paper_from_reed: type: shaped @@ -91,4 +251,46 @@ recipes: A: "default:reed" result: id: minecraft:paper - count: 3 \ No newline at end of file + count: 3 + default:magma_cream: + type: shaped + pattern: + - " A " + - "ABA" + - " A " + ingredients: + A: "default:flame_cane" + B: "minecraft:slime_ball" + result: + id: minecraft:magma_cream + count: 1 + default:magma_block: + type: shapeless + ingredients: + A1: "minecraft:cobblestone" + A2: "minecraft:cobblestone" + B1: "default:flame_cane" + B2: "default:flame_cane" + result: + id: minecraft:magma_block + count: 2 + +vanilla-loots: + minecraft:ender_pearl_flower_seeds_from_endermite: + type: entity + target: "minecraft:endermite" + override: false + loot: + pools: + - rolls: 1 + conditions: + - type: table_bonus + enchantment: minecraft:looting + chances: + - 0.1 + - 0.5 + - 0.8 + - 1 + entries: + - type: item + item: "default:ender_pearl_flower_seeds" \ 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 f20f99fcc..641bf7396 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/templates.yml @@ -953,7 +953,12 @@ templates#recipes: # loot tables templates#loot_tables: + # drop one item + + # template: default:loot_table/basic + # arguments: + # item: the item default:loot_table/basic: pools: - rolls: 1 @@ -962,7 +967,12 @@ templates#loot_tables: entries: - type: item item: "{item}" + # drop with silk touch + + # template: default:loot_table/silk_touch + # arguments: + # item: the item default:loot_table/silk_touch: pools: - rolls: 1 @@ -972,7 +982,78 @@ templates#loot_tables: entries: - type: item item: "{item}" - # drop ores + + # crop drops + + # template: default:loot_table/seed_crop + # arguments: + # crop_item: the ripe crop + # crop_seed: the seed of the crop + # ripe_age: the max age + default:loot_table/seed_crop: + pools: + - rolls: 1 + entries: + - type: alternatives + children: + - type: item + item: "{crop_item}" + conditions: + - type: match_block_property + properties: + age: "{ripe_age}" + - type: item + item: "{crop_seed}" + - rolls: 1 + conditions: + - type: match_block_property + properties: + age: "{ripe_age}" + entries: + - type: item + item: "{crop_seed}" + functions: + - type: apply_bonus + enchantment: minecraft:fortune + formula: + type: binomial_with_bonus_count + extra: 3 + probability: 0.5714286 + + # template: default:loot_table/crop + # arguments: + # crop_item: the ripe crop + # ripe_age: the max age + default:loot_table/crop: + pools: + - rolls: 1 + entries: + - type: item + item: "{crop_item}" + - rolls: 1 + conditions: + - type: match_block_property + properties: + age: "{ripe_age}" + entries: + - type: item + item: "{crop_item}" + functions: + - type: apply_bonus + enchantment: minecraft:fortune + formula: + type: binomial_with_bonus_count + extra: 3 + probability: 0.5714286 + + # ore drops + + # template: default:loot_table/ore + # arguments: + # ore_block: the ore block + # ore_drop: the drops of the ore + # min_exp: the min exp to drop + # max_exp: the max exp to drop default:loot_table/ore: pools: - rolls: 1 @@ -997,7 +1078,37 @@ templates#loot_tables: type: uniform min: "{min_exp}" max: "{max_exp}" - # leaves + + # template: default:loot_table/ore_no_exp + # arguments: + # ore_block: the ore block + # ore_drop: the drops of the ore + default:loot_table/ore_no_exp: + pools: + - rolls: 1 + entries: + - type: alternatives + children: + - type: item + item: "{ore_block}" + conditions: + - type: enchantment + predicate: minecraft:silk_touch>=1 + - type: item + item: "{ore_drop}" + functions: + - type: apply_bonus + enchantment: minecraft:fortune + formula: + type: ore_drops + - type: explosion_decay + + # leaves drops + + # template: default:loot_table/leaves + # arguments: + # leaves: the leaves block + # sapling: the sapling item default:loot_table/leaves: pools: - rolls: 1 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 new file mode 100644 index 000000000..c65bc426f --- /dev/null +++ b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/ender_pearl_flower_stage_3.json @@ -0,0 +1,43 @@ +{ + "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/textures/block/custom/ender_pearl_flower_stage_0.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_0.png new file mode 100644 index 000000000..4cd8a2d81 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_0.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_1.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_1.png new file mode 100644 index 000000000..93c0ebc31 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_1.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_2.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_2.png new file mode 100644 index 000000000..1fd5925e4 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_2.png differ 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 new file mode 100644 index 000000000..358b7874c Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_3.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png new file mode 100644 index 000000000..98e0e595b Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png new file mode 100644 index 000000000..ba13d4c71 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png new file mode 100644 index 000000000..48b377350 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png differ diff --git a/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flame_cane.png b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flame_cane.png new file mode 100644 index 000000000..0bf612e67 Binary files /dev/null and b/bukkit/loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flame_cane.png differ 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 cf8ef367f..ed4dc7d2e 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.api; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +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.BlockStateUtils; @@ -108,9 +109,9 @@ public final class CraftEngineBlocks { boolean success; try { Object worldServer = Reflections.field$CraftWorld$ServerLevel.get(location.getWorld()); - Object blockPos = Reflections.constructor$BlockPos.newInstance(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); Object blockState = block.customBlockState().handle(); - Object oldBlockState = Reflections.method$BlockGetter$getBlockState.invoke(worldServer, blockPos); + Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos); success = (boolean) Reflections.method$LevelWriter$setBlock.invoke(worldServer, blockPos, blockState, option.flags()); if (success) { Reflections.method$BlockStateBase$onPlace.invoke(blockState, worldServer, blockPos, oldBlockState, true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index d41a97e73..befb9a1e2 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 @@ -318,7 +318,7 @@ public class BlockEventListener implements Listener { if (direction == BlockFace.UP || direction == BlockFace.DOWN) { Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world); Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(serverLevel); - Object blockPos = Reflections.constructor$BlockPos.newInstance(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, blockPos); if (direction == BlockFace.UP) { NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$UP, blockPos, 0); 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 014a5a5d5..0757e0cf4 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 @@ -7,8 +7,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.dejvokep.boostedyaml.YamlDocument; -import net.momirealms.craftengine.bukkit.block.worldedit.WorldEditBlockRegister; -import net.momirealms.craftengine.bukkit.block.worldedit.WorldEditCommandHelper; +import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditCommandHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditCommandHelper.java similarity index 95% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditCommandHelper.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditCommandHelper.java index cdd32e55d..88839c776 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditCommandHelper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditCommandHelper.java @@ -1,6 +1,5 @@ -package net.momirealms.craftengine.bukkit.block.worldedit; +package net.momirealms.craftengine.bukkit.block; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.BlockStateParser; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index fc6fd3488..6d8de9243 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.shared.block.EmptyBlockBehavior; public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key BUSH_BLOCK = Key.from("craftengine:bush_block"); + public static final Key HANGING_BLOCK = Key.from("craftengine:hanging_block"); public static final Key FALLING_BLOCK = Key.from("craftengine:falling_block"); public static final Key LEAVES_BLOCK = Key.from("craftengine:leaves_block"); public static final Key STRIPPABLE_BLOCK = Key.from("craftengine:strippable_block"); @@ -13,16 +14,21 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block"); public static final Key WATERLOGGED_BLOCK = Key.from("craftengine:waterlogged_block"); public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block"); + public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block"); + public static final Key CROP_BLOCK = Key.from("craftengine:crop_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); register(FALLING_BLOCK, FallingBlockBehavior.FACTORY); register(BUSH_BLOCK, BushBlockBehavior.FACTORY); + register(HANGING_BLOCK, HangingBlockBehavior.FACTORY); register(LEAVES_BLOCK, LeavesBlockBehavior.FACTORY); register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY); register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY); register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY); register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY); register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY); + register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY); + register(CROP_BLOCK, CropBlockBehavior.FACTORY); } } 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 6de0a63b8..13b6e32ae 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockTags; import net.momirealms.craftengine.bukkit.util.LocationUtils; @@ -14,26 +15,32 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.parameter.LootParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.shared.block.BlockBehavior; -import org.bukkit.World; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.Callable; public class BushBlockBehavior extends AbstractBlockBehavior { public static final Factory FACTORY = new Factory(); - protected static final Object DIRT_TAG = BlockTags.getOrCreate(Key.of("minecraft", "dirt")); - protected static final Object FARMLAND = BlockTags.getOrCreate(Key.of("minecraft", "farmland")); - public static final BushBlockBehavior INSTANCE = new BushBlockBehavior(List.of(DIRT_TAG, FARMLAND)); protected final List tagsCanSurviveOn; + protected final Set blocksCansSurviveOn; + protected final Set customBlocksCansSurviveOn; + protected final boolean any; - public BushBlockBehavior(List tagsCanSurviveOn) { + public BushBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { this.tagsCanSurviveOn = tagsCanSurviveOn; + this.blocksCansSurviveOn = blocksCansSurviveOn; + this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; + this.any = this.tagsCanSurviveOn.isEmpty() && this.blocksCansSurviveOn.isEmpty() && this.customBlocksCansSurviveOn.isEmpty(); } @Override @@ -61,7 +68,7 @@ public class BushBlockBehavior extends AbstractBlockBehavior { ContextHolder.Builder builder = ContextHolder.builder(); BlockPos pos = LocationUtils.fromBlockPos(blockPos); Vec3d vec3d = Vec3d.atCenterOf(pos); - net.momirealms.craftengine.core.world.World world = new BukkitWorld((World) Reflections.method$Level$getCraftWorld.invoke(level)); + net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); builder.withParameter(LootParameters.LOCATION, vec3d); builder.withParameter(LootParameters.WORLD, world); for (Item item : previousState.getDrops(builder, world)) { @@ -82,33 +89,72 @@ public class BushBlockBehavior extends AbstractBlockBehavior { } public static class Factory implements BlockBehaviorFactory { + @Override public BlockBehavior create(CustomBlock block, Map arguments) { - if (arguments.containsKey("bottom-block-tags")) { - return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList()); - } else if (arguments.containsKey("tags")) { - return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList()); - } else { - return INSTANCE; - } + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new BushBlockBehavior(tuple.left(), tuple.mid(), tuple.right()); } } - private boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { - int y = Reflections.field$Vec3i$y.getInt(blockPos); - int x = Reflections.field$Vec3i$x.getInt(blockPos); - int z = Reflections.field$Vec3i$z.getInt(blockPos); - Object belowPos = Reflections.constructor$BlockPos.newInstance(x, y - 1, z); - Object belowState = Reflections.method$BlockGetter$getBlockState.invoke(world, belowPos); + public static Tuple, Set, Set> readTagsAndState(Map arguments) { + List mcTags = new ArrayList<>(); + for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-block-tags", List.of()))) { + mcTags.add(BlockTags.getOrCreate(Key.of(tag))); + } + Set mcBlocks = new HashSet<>(); + Set customBlocks = new HashSet<>(); + for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-blocks", List.of()))) { + int index = blockStateStr.indexOf('['); + Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr); + Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value())); + if (material != null) { + if (index == -1) { + // vanilla + mcBlocks.addAll(BlockStateUtils.getAllVanillaBlockStates(blockType)); + } else { + mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr))); + } + } else { + // custom maybe + customBlocks.add(blockStateStr); + } + } + return new Tuple<>(mcTags, mcBlocks, customBlocks); + } + + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); return mayPlaceOn(belowState, world, belowPos); } protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { + if (this.any) return true; for (Object tag : this.tagsCanSurviveOn) { if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) { return true; } } + int id = BlockStateUtils.blockStateToId(belowState); + if (BlockStateUtils.isVanillaBlock(id)) { + if (!this.blocksCansSurviveOn.isEmpty() && this.blocksCansSurviveOn.contains(belowState)) { + return true; + } + } else { + ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(id); + if (previousState != null && !previousState.isEmpty()) { + if (this.customBlocksCansSurviveOn.contains(previousState.owner().value().id().toString())) { + return true; + } + if (this.customBlocksCansSurviveOn.contains(previousState.toString())) { + return true; + } + } + } return false; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index 801e8605a..3df82844d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; @@ -65,7 +66,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { Object level = context.getLevel().serverWorld(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); try { - Object previousState = Reflections.method$BlockGetter$getBlockState.invoke(level, blockPos); + Object previousState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos); if (!shouldSolidify(level, blockPos, previousState)) { return super.updateStateForPlacement(context, state); } else { @@ -137,10 +138,10 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { int j = Direction.values().length; for (int k = 0; k < j; k++) { Object direction = Reflections.instance$Directions[k]; - Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(level, mutablePos); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos); if (direction != Reflections.instance$Direction$DOWN || canSolidify(blockState)) { Reflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, pos, direction); - blockState = Reflections.method$BlockGetter$getBlockState.invoke(level, mutablePos); + blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos); if (canSolidify(blockState) && !(boolean) Reflections.method$BlockStateBase$isFaceSturdy.invoke(blockState, level, pos, Reflections.getOppositeDirection(direction), Reflections.instance$SupportType$FULL)) { flag = true; break; 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 new file mode 100644 index 000000000..b4cde1bd8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -0,0 +1,146 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.ParticleUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.Tuple; +import net.momirealms.craftengine.shared.block.BlockBehavior; +import org.bukkit.World; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +public class CropBlockBehavior extends BushBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final IntegerProperty ageProperty; + private final float growSpeed; + private final int minGrowLight; + + public CropBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, + Property ageProperty, float growSpeed, int minGrowLight) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + this.ageProperty = (IntegerProperty) ageProperty; + this.growSpeed = growSpeed; + this.minGrowLight = minGrowLight; + } + + public final int getAge(ImmutableBlockState state) { + return state.get(ageProperty); + } + + public boolean isMaxAge(ImmutableBlockState state) { + return state.get(ageProperty) == ageProperty.max; + } + + private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { + return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0); + } + + private boolean hasSufficientLight(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { + return getRawBrightness(level, pos) >= this.minGrowLight - 1; + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + if (getRawBrightness(level, pos) >= this.minGrowLight) { + ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (currentState != null && !currentState.isEmpty()) { + int age = this.getAge(currentState); + if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { + Reflections.method$Level$setBlock.invoke(level, pos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + } + } + } + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos); + } + + @Override + public boolean isBoneMealSuccess(Object thisBlock, Object[] args) { + return true; + } + + @Override + public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) { + Object state = args[2]; + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (immutableBlockState != null && !immutableBlockState.isEmpty()) { + return getAge(immutableBlockState) != this.ageProperty.max; + } else { + return false; + } + } + + @Override + public void performBoneMeal(Object thisBlock, Object[] args) throws Exception { + this.performBoneMeal(args[0], args[2], args[3]); + } + + private void performBoneMeal(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (immutableBlockState == null || immutableBlockState.isEmpty()) { + return; + } + boolean sendParticles = false; + Object visualState = immutableBlockState.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, pos, visualState); + if (!is) { + sendParticles = true; + } + } else { + sendParticles = true; + } + + int i = this.getAge(immutableBlockState) + RandomUtils.generateRandomInt(2, 5); + int maxAge = this.ageProperty.max; + if (i > maxAge) { + i = maxAge; + } + Reflections.method$Level$setBlock.invoke(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + if (sendParticles) { + World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level); + int x = FastNMS.INSTANCE.field$Vec3i$x(pos); + int y = FastNMS.INSTANCE.field$Vec3i$y(pos); + int z = FastNMS.INSTANCE.field$Vec3i$z(pos); + world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.25, 0.25, 0.25); + } + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Tuple, Set, Set> tuple = readTagsAndState(arguments); + Property ageProperty = (Property) block.getProperty("age"); + if (ageProperty == null) { + throw new IllegalArgumentException("age property not set for crop"); + } + // 存活条件是最小生长亮度-1 + int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("light-requirement", 9)); + float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.25f)); + return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight); + } + } +} 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 9517544c6..08bbc708a 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; @@ -16,7 +17,6 @@ import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.shared.block.BlockBehavior; -import org.bukkit.World; import java.util.Map; import java.util.concurrent.Callable; @@ -56,17 +56,17 @@ public class FallingBlockBehavior extends AbstractBlockBehavior { @Override public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Object blockPos = args[2]; - int y = Reflections.field$Vec3i$y.getInt(blockPos); + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); Object world = args[1]; Object dimension = Reflections.method$$LevelReader$dimensionType.invoke(world); int minY = Reflections.field$DimensionType$minY.getInt(dimension); if (y < minY) { return; } - int x = Reflections.field$Vec3i$x.getInt(blockPos); - int z = Reflections.field$Vec3i$z.getInt(blockPos); - Object belowPos = Reflections.constructor$BlockPos.newInstance(x, y - 1, z); - Object belowState = Reflections.method$BlockGetter$getBlockState.invoke(world, belowPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + Object belowPos = LocationUtils.toBlockPos(x, y - 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); boolean isFree = (boolean) Reflections.method$FallingBlock$isFree.invoke(null, belowState); if (!isFree) { return; @@ -96,7 +96,7 @@ public class FallingBlockBehavior extends AbstractBlockBehavior { 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((World) Reflections.method$Level$getCraftWorld.invoke(level)); + net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); builder.withParameter(LootParameters.LOCATION, vec3d); builder.withParameter(LootParameters.WORLD, world); for (Item item : immutableBlockState.getDrops(builder, world)) { @@ -121,7 +121,7 @@ public class FallingBlockBehavior extends AbstractBlockBehavior { if (immutableBlockState == null || immutableBlockState.isEmpty()) return; Object level = args[0]; Object pos = args[1]; - net.momirealms.craftengine.core.world.World world = new BukkitWorld((World) Reflections.method$Level$getCraftWorld.invoke(level)); + net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.sounds().landSound()); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java new file mode 100644 index 000000000..2c43e4ea8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.util.Tuple; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class HangingBlockBehavior extends BushBlockBehavior { + public static final Factory FACTORY = new Factory(); + + public HangingBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y + 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); + return mayPlaceOn(belowState, world, belowPos); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new HangingBlockBehavior(tuple.left(), tuple.mid(), tuple.right()); + } + } +} 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 34dd0d031..d454b2c1b 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockTags; import net.momirealms.craftengine.bukkit.util.LocationUtils; @@ -108,7 +109,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior { Object blockPos = args[2]; ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0])); if (immutableBlockState != null && immutableBlockState.behavior() instanceof LeavesBlockBehavior behavior && behavior.isDecaying(immutableBlockState)) { - World bukkitWorld = (World) Reflections.method$Level$getCraftWorld.invoke(level); + World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level); BlockPos pos = LocationUtils.fromBlockPos(blockPos); // call bukkit event LeavesDecayEvent event = new LeavesDecayEvent(bukkitWorld.getBlockAt(pos.x(), pos.y(), pos.z())); @@ -142,7 +143,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior { for (int k = 0; k < j; ++k) { Object direction = Reflections.instance$Directions[k]; Reflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, blockPos, direction); - Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(world, mutablePos); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, mutablePos); i = Math.min(i, getDistanceAt(blockState) + 1); if (i == 1) { break; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index 1aca9d2bf..cb5934a2e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -9,14 +9,15 @@ import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.List; import java.util.Map; +import java.util.Set; public class OnLiquidBlockBehavior extends BushBlockBehavior { public static final Factory FACTORY = new Factory(); private final boolean onWater; private final boolean onLava; - public OnLiquidBlockBehavior(List tagsCanSurviveOn, boolean onWater, boolean onLava) { - super(tagsCanSurviveOn); + public OnLiquidBlockBehavior(boolean onWater, boolean onLava) { + super(List.of(), Set.of(), Set.of()); this.onWater = onWater; this.onLava = onLava; } @@ -33,7 +34,7 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); - return new OnLiquidBlockBehavior(List.of(), liquidTypes.contains("water"), liquidTypes.contains("lava")); + return new OnLiquidBlockBehavior(liquidTypes.contains("water"), liquidTypes.contains("lava")); } } 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 2998fa762..04df34dc8 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -12,6 +13,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Location; import org.bukkit.World; @@ -19,6 +21,7 @@ import org.bukkit.World; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.Callable; public class SaplingBlockBehavior extends BushBlockBehavior { @@ -26,12 +29,14 @@ public class SaplingBlockBehavior extends BushBlockBehavior { private final Key feature; private final Property stageProperty; private final double boneMealSuccessChance; + private final float growSpeed; - public SaplingBlockBehavior(Key feature, Property stageProperty, List tagsCanSurviveOn, double boneMealSuccessChance) { - super(tagsCanSurviveOn); + public SaplingBlockBehavior(Key feature, Property stageProperty, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); this.feature = feature; this.stageProperty = stageProperty; this.boneMealSuccessChance = boneMealSuccessChance; + this.growSpeed = growSpeed; } public Key treeFeature() { @@ -43,11 +48,8 @@ public class SaplingBlockBehavior extends BushBlockBehavior { Object world = args[1]; Object blockPos = args[2]; Object blockState = args[0]; - int x = (int) Reflections.field$Vec3i$x.get(blockPos); - int y = (int) Reflections.field$Vec3i$y.get(blockPos); - int z = (int) Reflections.field$Vec3i$z.get(blockPos); - Object aboveBlockPos = LocationUtils.toBlockPos(x, y + 1, z); - if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) Reflections.method$RandomSource$nextFloat.invoke(args[3]) < (1.0f / 7.0f)) { + Object aboveBlockPos = LocationUtils.above(blockPos); + if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) RandomUtils.generateRandomFloat(0, 1) < growSpeed) { increaseStage(world, blockPos, blockState, args[3]); } } @@ -58,10 +60,10 @@ public class SaplingBlockBehavior extends BushBlockBehavior { int currentStage = immutableBlockState.get(this.stageProperty); if (currentStage != this.stageProperty.possibleValues().get(this.stageProperty.possibleValues().size() - 1)) { ImmutableBlockState nextStage = immutableBlockState.cycle(this.stageProperty); - World bukkitWorld = (World) Reflections.method$Level$getCraftWorld.invoke(world); - int x = (int) Reflections.field$Vec3i$x.get(blockPos); - int y = (int) Reflections.field$Vec3i$y.get(blockPos); - int z = (int) Reflections.field$Vec3i$z.get(blockPos); + World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); CraftEngineBlocks.place(new Location(bukkitWorld, x, y, z), nextStage, UpdateOption.UPDATE_NONE, false); } else { generateTree(world, blockPos, blockState, randomSource); @@ -83,7 +85,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior { Object legacyState = Reflections.method$FluidState$createLegacyBlock.invoke(fluidState); Reflections.method$Level$setBlock.invoke(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags()); if ((boolean) Reflections.method$ConfiguredFeature$place.invoke(configuredFeature, world, chunkGenerator, randomSource, blockPos)) { - if (Reflections.method$BlockGetter$getBlockState.invoke(world, blockPos) == legacyState) { + if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, blockPos) == legacyState) { Reflections.method$ServerLevel$sendBlockUpdated.invoke(world, blockPos, blockState, legacyState, 2); } } else { @@ -93,8 +95,34 @@ public class SaplingBlockBehavior extends BushBlockBehavior { } @Override - public boolean isBoneMealSuccess(Object thisBlock, Object[] args) { - return RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance; + public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception { + boolean success = RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance; + Object level = args[0]; + Object blockPos = args[2]; + Object blockState = args[3]; + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (immutableBlockState == null || immutableBlockState.isEmpty()) { + return false; + } + boolean sendParticles = false; + Object visualState = immutableBlockState.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, blockPos, visualState); + if (!is) { + sendParticles = true; + } + } else { + sendParticles = true; + } + if (sendParticles) { + World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.25, 0.25, 0.25); + } + return success; } @Override @@ -121,13 +149,9 @@ public class SaplingBlockBehavior extends BushBlockBehavior { throw new IllegalArgumentException("stage property not set for sapling"); } double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45)); - if (arguments.containsKey("bottom-block-tags")) { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance); - } else if (arguments.containsKey("tags")) { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance); - } else { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, List.of(DIRT_TAG, FARMLAND), boneMealSuccessChance); - } + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new SaplingBlockBehavior(Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance, + MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0))); } } } 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 new file mode 100644 index 000000000..c8f0dfc67 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java @@ -0,0 +1,215 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.loot.parameter.LootParameters; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.Tuple; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +public class SugarCaneBlockBehavior extends BushBlockBehavior { + public static final Factory FACTORY = new Factory(); + private static final List WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER); + private static final List LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA); + private static final List HORIZON_DIRECTIONS = List.of(Reflections.instance$Direction$NORTH, Reflections.instance$Direction$EAST, Reflections.instance$Direction$SOUTH, Reflections.instance$Direction$WEST); + private final int maxHeight; + private final boolean nearWater; + private final boolean nearLava; + private final IntegerProperty ageProperty; + private final float growSpeed; + private final CustomBlock customBlock; + + public SugarCaneBlockBehavior(CustomBlock customBlock, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, Property ageProperty, + int maxHeight, boolean nearWater, boolean nearLava, float growSpeed) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + this.nearWater = nearWater; + this.nearLava = nearLava; + this.maxHeight = maxHeight; + this.ageProperty = (IntegerProperty) ageProperty; + this.growSpeed = growSpeed; + this.customBlock = customBlock; + } + + @Override + public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object blockState = args[0]; + Object level = args[1]; + Object blockPos = args[2]; + if (!canSurvive(thisBlock, blockState, level, blockPos)) { + ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (currentState != null && !currentState.isEmpty()) { + // break the sugar cane + Reflections.method$Level$removeBlock.invoke(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)); + // TODO client side particles? + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(LootParameters.LOCATION, vec3d) + .withParameter(LootParameters.WORLD, world); + for (Item item : currentState.getDrops(builder, world)) { + world.dropItemNaturally(vec3d, item); + } + } + } + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object world; + Object blockPos; + if (VersionHelper.isVersionNewerThan1_21_2()) { + world = args[1]; + blockPos = args[3]; + } else { + world = args[3]; + blockPos = args[4]; + } + Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1); + // return state, do not call super. + return superMethod.call(); + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object blockState = args[0]; + Object level = args[1]; + Object blockPos = args[2]; + // above block is empty + if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.above(blockPos)) == Reflections.instance$Blocks$AIR$defaultState) { + int currentHeight = 1; + BlockPos currentPos = LocationUtils.fromBlockPos(blockPos); + ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (currentState != null && !currentState.isEmpty()) { + while (true) { + Object belowPos = LocationUtils.toBlockPos(currentPos.x(), currentPos.y() - currentHeight, currentPos.z()); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos); + ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(belowState)); + if (belowImmutableState != null && !belowImmutableState.isEmpty() && belowImmutableState.owner() == currentState.owner()) { + currentHeight++; + } else { + break; + } + } + } else { + return; + } + + if (currentHeight < this.maxHeight) { + int age = currentState.get(ageProperty); + if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { + Object abovePos = LocationUtils.above(blockPos); + if (VersionHelper.isVersionNewerThan1_21_5()) { + Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + } else { + Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle()); + } + Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + } else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { + Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + } + } + } + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); + int id = BlockStateUtils.blockStateToId(belowState); + // 如果下方是同种方块 + if (!BlockStateUtils.isVanillaBlock(id)) { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id); + if (immutableBlockState.owner().value() == this.customBlock) { + return true; + } + } + if (!super.mayPlaceOn(belowState, world, belowPos)) { + return false; + } + // 如果不需要依靠流体 + if (!this.nearWater && !this.nearLava) { + return true; + } + // 需要流体 + if (this.nearWater) { + if (hasNearbyLiquid(world, belowPos, true)) { + return true; + } + } + if (this.nearLava) { + if (hasNearbyLiquid(world, belowPos, false)) { + return true; + } + } + return false; + } + + private boolean hasNearbyLiquid(Object world, Object blockPos, boolean waterOrLava) throws ReflectiveOperationException { + for (Object direction : HORIZON_DIRECTIONS) { + Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction); + if (waterOrLava) { + // water + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, relativePos); + if (Reflections.method$BlockStateBase$getBlock.invoke(blockState) == Reflections.instance$Blocks$ICE) { + return true; + } + Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos); + Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState); + if (WATER.contains(fluidType)) { + return true; + } + } else { + // lava + Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos); + Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState); + if (LAVA.contains(fluidType)) { + return true; + } + } + } + return false; + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Tuple, Set, Set> tuple = readTagsAndState(arguments); + Property ageProperty = (Property) block.getProperty("age"); + if (ageProperty == null) { + throw new IllegalArgumentException("age property not set for sugar cane"); + } + int maxHeight = MiscUtils.getAsInt(arguments.getOrDefault("max-height", 3)); + List nearbyLiquids = MiscUtils.getAsStringList(arguments.getOrDefault("required-adjacent-liquids", List.of())); + boolean nearWater = nearbyLiquids.contains("water"); + boolean nearLava = nearbyLiquids.contains("lava"); + return new SugarCaneBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, maxHeight, nearWater, nearLava, + MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1))); + } + } +} 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 0f965dbc8..bba404771 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item; import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider; 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; @@ -50,6 +51,7 @@ public class BukkitItemManager extends AbstractItemManager { registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES); registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); + registerVanillaItemExtraBehavior(BoneMealItemBehavior.INSTANCE, ItemKeys.BONE_MEAL); } private static BukkitItemManager instance; 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 312486736..e19d88049 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 @@ -40,7 +40,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import javax.annotation.Nullable; -import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.util.Map; import java.util.Optional; @@ -147,12 +146,7 @@ public class BlockItemBehavior extends ItemBehavior { protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) { try { - Object player; - try { - player = Reflections.method$CraftPlayer$getHandle.invoke(context.getPlayer().platformPlayer()); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to get server player", e); - } + Object player = context.getPlayer().serverPlayer(); Object blockState = state.customBlockState().handle(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); Object voxelShape = Reflections.method$CollisionContext$of.invoke(null, player); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java new file mode 100644 index 000000000..4a71f53bf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java @@ -0,0 +1,73 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior; +import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.world.BukkitWorldBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +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.util.Key; +import org.bukkit.block.Block; + +import java.nio.file.Path; +import java.util.Map; + +public class BoneMealItemBehavior extends ItemBehavior { + public static final Factory FACTORY = new Factory(); + public static final BoneMealItemBehavior INSTANCE = new BoneMealItemBehavior(); + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos()); + Block block = clicked.block(); + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); + if (state == null || state.isEmpty()) return InteractionResult.PASS; + + boolean shouldHandle =false; + if (state.behavior() instanceof CropBlockBehavior blockBehavior) { + if (!blockBehavior.isMaxAge(state)) { + shouldHandle = true; + } + } else if (state.behavior() instanceof SaplingBlockBehavior) { + shouldHandle = true; + } + + if (!shouldHandle) return InteractionResult.PASS; + + boolean sendSwing = false; + try { + Object visualState = state.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); + if (!is) { + sendSwing = true; + } + } else { + sendSwing = true; + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e); + } + if (sendSwing) { + context.getPlayer().swingHand(context.getHand()); + } + return InteractionResult.SUCCESS; + } + + public static class Factory implements ItemBehaviorFactory { + + @Override + public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { + return INSTANCE; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index 40bed4810..ed3e35737 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -11,6 +11,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item"); public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item"); + public static final Key BONE_MEAL_ITEM = Key.from("craftengine:bone_meal_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -20,5 +21,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(AXE_ITEM, AxeItemBehavior.FACTORY); register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); register(BUCKET_ITEM, BucketItemBehavior.FACTORY); + register(BONE_MEAL_ITEM, BoneMealItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java index 194a1bbba..1163a1ab8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -41,7 +42,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { try { Object blockHitResult = Reflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), Reflections.instance$ClipContext$Fluid$SOURCE_ONLY); Object blockPos = Reflections.field$BlockHitResul$blockPos.get(blockHitResult); - BlockPos above = new BlockPos(Reflections.field$Vec3i$x.getInt(blockPos), Reflections.field$Vec3i$y.getInt(blockPos) + offsetY, Reflections.field$Vec3i$z.getInt(blockPos)); + BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); Direction direction = Direction.values()[(int) Reflections.method$Direction$ordinal.invoke(Reflections.field$BlockHitResul$direction.get(blockHitResult))]; boolean miss = Reflections.field$BlockHitResul$miss.getBoolean(blockHitResult); Vec3d hitPos = LocationUtils.fromVec(Reflections.field$HitResult$location.get(blockHitResult)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index 07fc4fab6..a6d749316 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -39,6 +39,7 @@ import org.jetbrains.annotations.Nullable; import java.io.Reader; import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Path; import java.util.*; @@ -988,8 +989,22 @@ public class BukkitRecipeManager implements RecipeManager { } } + // 1.21.5+ + private static Object toTransmuteResult(ItemStack item) throws InvocationTargetException, IllegalAccessException, InstantiationException { + Object itemStack = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, item); + Object nmsItem = Reflections.method$ItemStack$getItem.invoke(itemStack); + return Reflections.constructor$TransmuteResult.newInstance(nmsItem); + } + private static Object createMinecraftSmithingTransformRecipe(CustomSmithingTransformRecipe ceRecipe) throws ReflectiveOperationException { - if (VersionHelper.isVersionNewerThan1_21_2()) { + if (VersionHelper.isVersionNewerThan1_21_5()) { + return Reflections.constructor$SmithingTransformRecipe.newInstance( + toOptionalMinecraftIngredient(ceRecipe.template()), + toMinecraftIngredient(ceRecipe.base()), + toOptionalMinecraftIngredient(ceRecipe.addition()), + toTransmuteResult(ceRecipe.result(ItemBuildContext.EMPTY)) + ); + } else if (VersionHelper.isVersionNewerThan1_21_2()) { return Reflections.constructor$SmithingTransformRecipe.newInstance( toOptionalMinecraftIngredient(ceRecipe.template()), toOptionalMinecraftIngredient(ceRecipe.base()), 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 b477c47bc..9a47edb92 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 @@ -118,7 +118,7 @@ public class BukkitVanillaLootManager implements VanillaLootManager, Listener { VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK)); vanillaLoot.addLootTable(lootTable); } else { - for (Object blockState : BlockStateUtils.getAllBlockStates(Key.of(target))) { + for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) { if (blockState == Reflections.instance$Blocks$AIR$defaultState) { this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target); return; 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 b86f0af22..9bb2e79a6 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 @@ -4,6 +4,7 @@ import net.momirealms.antigrieflib.AntiGriefLib; import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; +import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.font.BukkitImageManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; @@ -17,13 +18,10 @@ import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory; import net.momirealms.craftengine.bukkit.plugin.gui.BukkitGuiManager; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; -import net.momirealms.craftengine.bukkit.plugin.papi.ImageExpansion; -import net.momirealms.craftengine.bukkit.plugin.papi.ShiftExpansion; import net.momirealms.craftengine.bukkit.plugin.scheduler.BukkitSchedulerAdapter; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager; import net.momirealms.craftengine.bukkit.util.EventUtils; -import net.momirealms.craftengine.bukkit.util.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.entity.player.Player; @@ -176,8 +174,7 @@ public class BukkitCraftEngine extends CraftEngine { // compatibility // register expansion if (this.isPluginEnabled("PlaceholderAPI")) { - new ShiftExpansion(this).register(); - new ImageExpansion(this).register(); + PlaceholderAPIUtils.registerExpansions(this); this.hasPlaceholderAPI = true; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 388f70160..a2656ed50 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 @@ -96,6 +96,8 @@ public class BukkitInjector { .method(ElementMatchers.any() .and(ElementMatchers.not(ElementMatchers.is(Reflections.method$PalettedContainer$getAndSet))) .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))) + // TODO Requires Paper Patch + //.and(ElementMatchers.not(ElementMatchers.named("get").and(ElementMatchers.takesArguments(int.class)).and(ElementMatchers.returns(Object.class)))) ) .intercept(MethodDelegation.toField("target")) .method(ElementMatchers.is(Reflections.method$PalettedContainer$getAndSet)) 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 f7fa9cddd..939cf62f8 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 @@ -6,7 +6,7 @@ import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.impl.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; @@ -72,7 +72,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public BukkitNetworkManager(BukkitCraftEngine plugin) { this.plugin = plugin; - if (VersionHelper.isVersionNewerThan1_21_2()) { + if (VersionHelper.isVersionNewerThan1_21_5()) { + this.packetIds = new PacketIds1_21_5(); + } else if (VersionHelper.isVersionNewerThan1_21_2()) { this.packetIds = new PacketIds1_21_2(); } else if (VersionHelper.isVersionNewerThan1_20_5()) { this.packetIds = new PacketIds1_20_5(); @@ -254,7 +256,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return (Channel) Reflections.field$Channel.get( Reflections.field$NetworkManager.get( Reflections.field$ServerPlayer$connection.get( - Reflections.method$CraftPlayer$getHandle.invoke(player) + FastNMS.INSTANCE.method$CraftPlayer$getHandle(player) ) ) ); @@ -265,7 +267,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public void sendPacket(@NotNull Player player, @NotNull Object packet) { try { - Object serverPlayer = Reflections.method$CraftPlayer$getHandle.invoke(player); + Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); this.immediatePacketConsumer.accept(serverPlayer, packet); } catch (Exception e) { this.plugin.logger().warn("Failed to send packet", e); 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 a220f5f1b..36a5bb575 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 @@ -11,6 +11,7 @@ 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.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; @@ -268,11 +269,7 @@ public class PacketConsumers { Player platformPlayer = player.platformPlayer(); World world = platformPlayer.getWorld(); Object blockPos = Reflections.field$ServerboundPlayerActionPacket$pos.get(packet); - BlockPos pos = new BlockPos( - (int) Reflections.field$Vec3i$x.get(blockPos), - (int) Reflections.field$Vec3i$y.get(blockPos), - (int) Reflections.field$Vec3i$z.get(blockPos) - ); + BlockPos pos = LocationUtils.fromBlockPos(blockPos); if (VersionHelper.isFolia()) { BukkitCraftEngine.instance().scheduler().sync().run(() -> { try { @@ -293,7 +290,7 @@ public class PacketConsumers { Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet); if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) { Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world); - Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(serverLevel, LocationUtils.toBlockPos(pos)); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, LocationUtils.toBlockPos(pos)); int stateId = BlockStateUtils.blockStateToId(blockState); // not a custom block if (BlockStateUtils.isVanillaBlock(stateId)) { @@ -311,7 +308,7 @@ public class PacketConsumers { } if (player.isAdventureMode()) { Object itemStack = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, player.platformPlayer().getInventory().getItemInMainHand()); - Object blockPos = Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z()); + Object blockPos = LocationUtils.toBlockPos(pos); Object blockInWorld = Reflections.constructor$BlockInWorld.newInstance(serverLevel, blockPos, false); if (VersionHelper.isVersionNewerThan1_20_5()) { if (Reflections.method$ItemStack$canBreakBlockInAdventureMode != null @@ -523,8 +520,8 @@ public class PacketConsumers { if (player == null) return; Object pos = Reflections.field$ServerboundPickItemFromBlockPacket$pos.get(packet); if (VersionHelper.isFolia()) { - int x = (int) Reflections.field$Vec3i$x.get(pos); - int z = (int) Reflections.field$Vec3i$z.get(pos); + int x = FastNMS.INSTANCE.field$Vec3i$x(pos); + int z = FastNMS.INSTANCE.field$Vec3i$z(pos); BukkitCraftEngine.instance().scheduler().sync().run(() -> { try { handlePickItemFromBlockPacketOnMainThread(player, pos); @@ -548,7 +545,7 @@ public class PacketConsumers { private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Exception { Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(player.getWorld()); - Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(serverLevel, pos); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos); ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); if (state == null) return; Key itemId = state.settings().itemId(); @@ -603,7 +600,7 @@ public class PacketConsumers { } assert Reflections.method$ServerGamePacketListenerImpl$tryPickItem != null; Reflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( - Reflections.field$ServerPlayer$connection.get(Reflections.method$CraftPlayer$getHandle.invoke(player)), Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack)); + Reflections.field$ServerPlayer$connection.get(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack)); } public static final TriConsumer ADD_ENTITY = (user, event, packet) -> { @@ -909,9 +906,14 @@ public class PacketConsumers { Object id = Reflections.method$CustomPacketPayload$Type$id.invoke(type); String channel = id.toString(); if (!channel.equals(NetworkManager.MOD_CHANNEL)) return; - ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload); - byte[] data = new byte[buf.readableBytes()]; - buf.readBytes(data); + byte[] data; + if (Reflections.method$DiscardedPayload$data != null) { + ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload); + data = new byte[buf.readableBytes()]; + buf.readBytes(data); + } 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(':'); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_21_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_21_5.java new file mode 100644 index 000000000..f54faf5cb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_21_5.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.bukkit.plugin.network.impl; + +import net.momirealms.craftengine.bukkit.plugin.network.PacketIds; + +public class PacketIds1_21_5 implements PacketIds { + + @Override + public int clientboundBlockUpdatePacket() { + return 8; + } + + @Override + public int clientboundSectionBlocksUpdatePacket() { + return 72; + } + + @Override + public int clientboundLevelParticlesPacket() { + return 40; + } + + @Override + public int clientboundLevelEventPacket() { + return 39; + } + + @Override + public int clientboundAddEntityPacket() { + return 1; + } +} 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 3fb0627e6..030a31379 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 @@ -4,6 +4,7 @@ import com.google.common.collect.Lists; import io.netty.channel.Channel; import net.kyori.adventure.text.Component; 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.util.*; import net.momirealms.craftengine.bukkit.world.BukkitWorld; @@ -75,11 +76,7 @@ public class BukkitServerPlayer extends Player { public void setPlayer(org.bukkit.entity.Player player) { playerRef = new WeakReference<>(player); - try { - serverPlayerRef = new WeakReference<>(Reflections.method$CraftPlayer$getHandle.invoke(player)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); } @Override @@ -298,7 +295,7 @@ public class BukkitServerPlayer extends Player { public float getDestroyProgress(Object blockState, BlockPos pos) { try { Object serverPlayer = serverPlayer(); - Object blockPos = Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z()); + Object blockPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z()); return (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(blockState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get destroy progress for player " + platformPlayer().getName()); 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 0cdb7db6e..52b298618 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 @@ -1,18 +1,23 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.block.BlockStateParser; +import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.PushReaction; import net.momirealms.craftengine.core.util.Instrument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MapColor; import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.event.block.BlockPhysicsEvent; import java.util.IdentityHashMap; import java.util.List; +import java.util.Optional; public class BlockStateUtils { public static final IdentityHashMap CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>(); @@ -27,8 +32,36 @@ public class BlockStateUtils { hasInit = true; } - @SuppressWarnings("unchecked") + public static List getAllBlockStates(String blockState) { + int index = blockState.indexOf('['); + if (index == -1) { + return getAllBlockStates(Key.of(blockState)); + } else { + String blockTypeString = blockState.substring(0, index); + Key block = Key.of(blockTypeString); + Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + if (optionalCustomBlock.isPresent()) { + ImmutableBlockState state = BlockStateParser.deserialize(blockState); + if (state == null) { + return List.of(); + } else { + return List.of(state.customBlockState().handle()); + } + } else { + BlockData blockData = Bukkit.createBlockData(blockState); + return List.of(blockDataToBlockState(blockData)); + } + } + } + public static List getAllBlockStates(Key block) { + Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + return optionalCustomBlock.map(customBlock -> customBlock.variantProvider().states().stream().map(it -> it.customBlockState().handle()).toList()) + .orElseGet(() -> getAllVanillaBlockStates(block)); + } + + @SuppressWarnings("unchecked") + public static List getAllVanillaBlockStates(Key block) { try { Object blockIns = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, block.namespace(), block.value())); Object definition = Reflections.field$Block$StateDefinition.get(blockIns); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index cb44a126b..c5fe84760 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; import org.bukkit.World; @@ -14,13 +15,9 @@ public class EntityUtils { public static BlockPos getOnPos(Player player) { try { - Object serverPlayer = Reflections.method$CraftPlayer$getHandle.invoke(player); + Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); Object blockPos = Reflections.method$Entity$getOnPos.invoke(serverPlayer, 1.0E-5F); - return new BlockPos( - (int) Reflections.field$Vec3i$x.get(blockPos), - (int) Reflections.field$Vec3i$y.get(blockPos), - (int) Reflections.field$Vec3i$z.get(blockPos) - ); + return LocationUtils.fromBlockPos(blockPos); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } 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 11d772e19..27bb4e598 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 @@ -11,6 +11,7 @@ public class LightUtils { private LightUtils() {} + @SuppressWarnings("unchecked") public static void updateChunkLight(World world, Map sectionPosSet) { try { Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world); @@ -19,8 +20,13 @@ public class LightUtils { long chunkKey = entry.getKey(); Object chunkHolder = Reflections.method$ServerChunkCache$getVisibleChunkIfPresent.invoke(chunkSource, chunkKey); if (chunkHolder == null) continue; - @SuppressWarnings("unchecked") - List players = (List) Reflections.method$ChunkHolder$getPlayers.invoke(chunkHolder, false); + List players; + if (Reflections.method$ChunkHolder$getPlayers != null) { + players = (List) Reflections.method$ChunkHolder$getPlayers.invoke(chunkHolder, false); + } else { + Object chunkHolder$PlayerProvider = Reflections.field$ChunkHolder$playerProvider.get(chunkHolder); + players = (List) Reflections.method$ChunkHolder$PlayerProvider$getPlayers.invoke(chunkHolder$PlayerProvider, false); + } if (players.isEmpty()) continue; Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder); BitSet blockChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$blockChangedLightSectionFilter.get(chunkHolder); 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 b960901d0..01c719118 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,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import org.bukkit.Location; @@ -13,48 +14,39 @@ public class LocationUtils { return new Vec3d(loc.getX(), loc.getY(), loc.getZ()); } - public static Vec3d fromVec(Object vec) throws ReflectiveOperationException { + public static Vec3d fromVec(Object vec) { return new Vec3d( - Reflections.field$Vec3$x.getDouble(vec), - Reflections.field$Vec3$y.getDouble(vec), - Reflections.field$Vec3$z.getDouble(vec) + FastNMS.INSTANCE.field$Vec3$x(vec), + FastNMS.INSTANCE.field$Vec3$y(vec), + FastNMS.INSTANCE.field$Vec3$y(vec) ); } public static Object toBlockPos(BlockPos pos) { - try { - return Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z()); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to create BlockPos", e); - } + return toBlockPos(pos.x(), pos.y(), pos.z()); } public static Object above(Object blockPos) throws ReflectiveOperationException { return toBlockPos( - Reflections.field$Vec3i$x.getInt(blockPos), - Reflections.field$Vec3i$y.getInt(blockPos) + 1, - Reflections.field$Vec3i$z.getInt(blockPos) + 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) { - try { - return Reflections.constructor$BlockPos.newInstance(x, y, z); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to create BlockPos", e); - } + return FastNMS.INSTANCE.constructor$BlockPos(x, y, z); } - public static BlockPos toBlockPos(Location pos) { return new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); } public static BlockPos fromBlockPos(Object pos) throws ReflectiveOperationException { return new BlockPos( - Reflections.field$Vec3i$x.getInt(pos), - Reflections.field$Vec3i$y.getInt(pos), - Reflections.field$Vec3i$z.getInt(pos) + FastNMS.INSTANCE.field$Vec3i$x(pos), + FastNMS.INSTANCE.field$Vec3i$y(pos), + FastNMS.INSTANCE.field$Vec3i$z(pos) ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java index badab57bf..5b4f28f4a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.plugin.config.ConfigManager; public class NoteBlockChainUpdateUtils { @@ -9,7 +10,7 @@ public class NoteBlockChainUpdateUtils { public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) throws ReflectiveOperationException { if (times >= ConfigManager.maxChainUpdate()) return; Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction); - Object state = Reflections.method$BlockGetter$getBlockState.invoke(level, relativePos); + Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos); if (BlockStateUtils.isClientSideNoteBlock(state)) { Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, relativePos); noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1); 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 new file mode 100644 index 000000000..efe651614 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.bukkit.util; + +import org.bukkit.Particle; + +public class ParticleUtils { + + public static Particle getParticle(String particle) { + try { + return Particle.valueOf(particle); + } catch (IllegalArgumentException e) { + return switch (particle) { + case "REDSTONE" -> Particle.valueOf("DUST"); + case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER"); + default -> Particle.valueOf(particle); + }; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 672dc1922..d859d3bf8 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 @@ -471,9 +471,9 @@ public class Reflections { BukkitReflectionUtils.assembleCBClass("entity.CraftPlayer") )); - public static final Method method$CraftPlayer$getHandle = requireNonNull( - ReflectionUtils.getMethod(clazz$CraftPlayer, new String[] { "getHandle" }) - ); +// public static final Method method$CraftPlayer$getHandle = requireNonNull( +// ReflectionUtils.getMethod(clazz$CraftPlayer, new String[] { "getHandle" }) +// ); public static final Field field$ServerPlayer$connection = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0) @@ -1263,9 +1263,18 @@ public class Reflections { ) ); + public static final Class clazz$Team$Visibility = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.scores.Team$Visibility"), + BukkitReflectionUtils.assembleMCClass("world.scores.ScoreboardTeamBase$EnumTeamPush") + ) + ); + public static final Field field$ClientboundSetPlayerTeamPacket$Parameters$nametagVisibility = requireNonNull( ReflectionUtils.getInstanceDeclaredField( - clazz$ClientboundSetPlayerTeamPacket$Parameters, String.class, 0 + clazz$ClientboundSetPlayerTeamPacket$Parameters, + VersionHelper.isVersionNewerThan1_21_5() ? clazz$Team$Visibility : String.class, + 0 ) ); @@ -1355,17 +1364,17 @@ public class Reflections { ) ); - public static final Field field$Vec3i$x = requireNonNull( - ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 0) - ); - - public static final Field field$Vec3i$y = requireNonNull( - ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 1) - ); - - public static final Field field$Vec3i$z = requireNonNull( - ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 2) - ); +// public static final Field field$Vec3i$x = requireNonNull( +// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 0) +// ); +// +// public static final Field field$Vec3i$y = requireNonNull( +// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 1) +// ); +// +// public static final Field field$Vec3i$z = requireNonNull( +// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 2) +// ); public static final Class clazz$BlockState = requireNonNull( ReflectionUtils.getClazz( @@ -1588,11 +1597,11 @@ public class Reflections { ) ); - public static final Constructor constructor$BlockPos = requireNonNull( - ReflectionUtils.getConstructor( - clazz$BlockPos, int.class, int.class, int.class - ) - ); +// public static final Constructor constructor$BlockPos = requireNonNull( +// ReflectionUtils.getConstructor( +// clazz$BlockPos, int.class, int.class, int.class +// ) +// ); public static final Method method$Vec3i$relative = requireNonNull( ReflectionUtils.getMethod( @@ -2462,11 +2471,11 @@ public class Reflections { ) ); - public static final Method method$BlockGetter$getBlockState = requireNonNull( - ReflectionUtils.getMethod( - clazz$BlockGetter, clazz$BlockState, clazz$BlockPos - ) - ); +// public static final Method method$BlockGetter$getBlockState = requireNonNull( +// ReflectionUtils.getMethod( +// clazz$BlockGetter, clazz$BlockState, clazz$BlockPos +// ) +// ); public static final Method method$LevelAccessor$scheduleTick = requireNonNull( ReflectionUtils.getMethod( @@ -2660,12 +2669,31 @@ public class Reflections { ) ); - public static final Method method$ChunkHolder$getPlayers = requireNonNull( - ReflectionUtils.getMethod( - clazz$ChunkHolder, List.class, boolean.class + public static final Class clazz$ChunkHolder$PlayerProvider = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("server.level.ChunkHolder$PlayerProvider"), + BukkitReflectionUtils.assembleMCClass("server.level.PlayerChunk$d") ) ); + public static final Field field$ChunkHolder$playerProvider = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ChunkHolder, clazz$ChunkHolder$PlayerProvider, 0 + ) + ); + + public static final Method method$ChunkHolder$PlayerProvider$getPlayers = requireNonNull( + ReflectionUtils.getMethod( + clazz$ChunkHolder$PlayerProvider, List.class, clazz$ChunkPos, boolean.class + ) + ); + + // 1.20 ~ 1.21.4 + public static final Method method$ChunkHolder$getPlayers = + ReflectionUtils.getMethod( + clazz$ChunkHolder, List.class, boolean.class + ); + public static final Field field$ChunkHolder$lightEngine = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ChunkHolder, clazz$LevelLightEngine, 0 @@ -3407,6 +3435,7 @@ public class Reflections { ) ); + @Deprecated public static final Method method$Level$getCraftWorld = requireNonNull( ReflectionUtils.getMethod( clazz$Level, clazz$CraftWorld @@ -3923,9 +3952,9 @@ public class Reflections { ); public static final Method method$CraftEventFactory$callBlockPlaceEvent = requireNonNull( - ReflectionUtils.getStaticMethod( - clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, int.class, int.class, int.class - ) + VersionHelper.isVersionNewerThan1_21_5() + ? ReflectionUtils.getStaticMethod(clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, clazz$BlockPos) + : ReflectionUtils.getStaticMethod(clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, int.class, int.class, int.class) ); public static final Class clazz$Abilities = requireNonNull( @@ -4816,9 +4845,9 @@ public class Reflections { ); public static final Method method$ServerLevel$levelEvent = requireNonNull( - ReflectionUtils.getMethod( - clazz$ServerLevel, void.class, clazz$Player, int.class, clazz$BlockPos, int.class - ) + VersionHelper.isVersionNewerThan1_21_5() + ? ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Entity, int.class, clazz$BlockPos, int.class) + : ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Player, int.class, clazz$BlockPos, int.class) ); public static final Method method$PalettedContainer$getAndSet = Objects.requireNonNull( @@ -5104,12 +5133,24 @@ public class Reflections { ) ); + // 1.21.5+ + public static final Class clazz$TransmuteResult = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.item.crafting.TransmuteResult") + ); + + public static final Constructor constructor$TransmuteResult = Optional.ofNullable(clazz$TransmuteResult) + .map(it -> ReflectionUtils.getConstructor(it, clazz$Item)) + .orElse(null); + public static final Constructor constructor$SmithingTransformRecipe = requireNonNull( - VersionHelper.isVersionNewerThan1_21_2() ? - ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, Optional.class, Optional.class, clazz$ItemStack) : - VersionHelper.isVersionNewerThan1_20_2() ? - ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) : - ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$ResourceLocation, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) + VersionHelper.isVersionNewerThan1_21_5() + ? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, clazz$Ingredient, Optional.class, clazz$TransmuteResult) + : VersionHelper.isVersionNewerThan1_21_2() + ? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, Optional.class, Optional.class, clazz$ItemStack) + : VersionHelper.isVersionNewerThan1_20_2() + ? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) + : ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$ResourceLocation, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) ); public static final Method method$RecipeManager$addRecipe = requireNonNull( @@ -5415,13 +5456,11 @@ public class Reflections { ); public static final Method method$SimpleWaterloggedBlock$canPlaceLiquid = requireNonNull( - VersionHelper.isVersionNewerThan1_20_2() ? - ReflectionUtils.getMethod( - clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid - ) : - ReflectionUtils.getMethod( - clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid - ) + VersionHelper.isVersionNewerThan1_21_5() + ? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LivingEntity, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid) + : VersionHelper.isVersionNewerThan1_20_2() + ? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid) + : ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid) ); public static final Method method$SimpleWaterloggedBlock$placeLiquid = requireNonNull( @@ -5431,13 +5470,11 @@ public class Reflections { ); public static final Method method$SimpleWaterloggedBlock$pickupBlock = requireNonNull( - VersionHelper.isVersionNewerThan1_20_2() ? - ReflectionUtils.getMethod( - clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState - ) : - ReflectionUtils.getMethod( - clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState - ) + VersionHelper.isVersionNewerThan1_21_5() + ? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LivingEntity, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState) + : VersionHelper.isVersionNewerThan1_20_2() + ? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState) + : ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState) ); public static final Method method$Fluid$getTickDelay = requireNonNull( @@ -5667,11 +5704,19 @@ public class Reflections { .map(it -> ReflectionUtils.getMethod(it, clazz$ResourceLocation)) .orElse(null); - // 1.20.5+ + // 1.20.5~1.21.4#221 public static final Method method$DiscardedPayload$data = Optional.ofNullable(clazz$DiscardedPayload) .map(it -> ReflectionUtils.getMethod(it, ByteBuf.class)) .orElse(null); + // 1.21.4#222+ + public static final Method method$DiscardedPayload$dataByteArray = Optional.ofNullable(method$DiscardedPayload$data) + .map(m -> (Method) null) + .orElseGet(() -> Optional.ofNullable(clazz$DiscardedPayload) + .map(clazz -> ReflectionUtils.getMethod(clazz, byte[].class)) + .orElse(null) + ); + public static final Class clazz$ClientboundDisconnectPacket = requireNonNull( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.common.ClientboundDisconnectPacket"), @@ -5684,4 +5729,46 @@ public class Reflections { clazz$ClientboundDisconnectPacket, clazz$Component ) ); + + public static final Method method$CraftEventFactory$handleBlockGrowEvent = requireNonNull( + VersionHelper.isVersionNewerThan1_21_5() ? + ReflectionUtils.getStaticMethod( + clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState, int.class + ) : + ReflectionUtils.getStaticMethod( + clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState + ) + ); + + public static final Class clazz$BlockAndTintGetter = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.BlockAndTintGetter"), + BukkitReflectionUtils.assembleMCClass("world.level.IBlockLightAccess") + ) + ); + + public static final Method method$BlockAndTintGetter$getRawBrightness = requireNonNull( + ReflectionUtils.getMethod( + clazz$BlockAndTintGetter, int.class, clazz$BlockPos, int.class + ) + ); + + public static final Field field$Level$random = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$Level, clazz$RandomSource, 0 + ) + ); + + public static final Class clazz$Mth = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("util.Mth"), + BukkitReflectionUtils.assembleMCClass("util.MathHelper") + ) + ); + + public static final Method method$nextInt = requireNonNull( + ReflectionUtils.getMethod( + clazz$Mth, int.class, clazz$RandomSource, int.class, int.class + ) + ); } 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 b5cddf244..73ca854da 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 @@ -5,11 +5,17 @@ import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.util.SectionPosUtils; import net.momirealms.craftengine.core.world.CEWorld; 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; public class BukkitCEWorld extends CEWorld { - public BukkitCEWorld(World world) { - super(world); + public BukkitCEWorld(World world, StorageAdaptor adaptor) { + super(world, adaptor); + } + + public BukkitCEWorld(World world, WorldDataStorage dataStorage) { + super(world, dataStorage); } @Override 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 8248c227f..001dc1754 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.world; +import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; @@ -8,6 +9,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.SectionPos; @@ -15,6 +17,9 @@ import net.momirealms.craftengine.core.world.WorldManager; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.CESection; import net.momirealms.craftengine.core.world.chunk.serialization.ChunkSerializer; +import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Bukkit; import org.bukkit.Chunk; @@ -27,6 +32,7 @@ import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.HashMap; @@ -38,18 +44,34 @@ public class BukkitWorldManager implements WorldManager, Listener { private static BukkitWorldManager instance; private final BukkitCraftEngine plugin; private final Map worlds; - private CEWorld[] worldArray; + private CEWorld[] worldArray = new CEWorld[0]; private final ReentrantReadWriteLock worldMapLock = new ReentrantReadWriteLock(); private SchedulerTask tickTask; // cache private UUID lastVisitedUUID; private CEWorld lastVisitedWorld; + private StorageAdaptor storageAdaptor; public BukkitWorldManager(BukkitCraftEngine plugin) { instance = this; this.plugin = plugin; this.worlds = new HashMap<>(); - resetWorldArray(); + if (VersionHelper.isVersionNewerThan1_21_4()) { + try { + Class.forName("com.infernalsuite.asp.api.AdvancedSlimePaperAPI"); + SlimeFormatStorageAdaptor adaptor = new SlimeFormatStorageAdaptor(this); + this.storageAdaptor = adaptor; + Bukkit.getPluginManager().registerEvents(adaptor, plugin.bootstrap()); + return; + } catch (ClassNotFoundException ignored) { + } + } + this.storageAdaptor = new DefaultStorageAdaptor(); + } + + @Override + public void setStorageAdaptor(@NotNull StorageAdaptor storageAdaptor) { + this.storageAdaptor = storageAdaptor; } public static BukkitWorldManager instance() { @@ -95,7 +117,7 @@ public class BukkitWorldManager implements WorldManager, Listener { this.worldMapLock.writeLock().lock(); try { for (World world : Bukkit.getWorlds()) { - CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world)); + CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor); this.worlds.put(world.getUID(), ceWorld); this.resetWorldArray(); for (Chunk chunk : world.getLoadedChunks()) { @@ -110,8 +132,11 @@ public class BukkitWorldManager implements WorldManager, Listener { @Override public void disable() { HandlerList.unregisterAll(this); - if (tickTask != null && !tickTask.cancelled()) { - tickTask.cancel(); + if (this.storageAdaptor instanceof Listener listener) { + HandlerList.unregisterAll(listener); + } + if (this.tickTask != null && !this.tickTask.cancelled()) { + this.tickTask.cancel(); } for (World world : Bukkit.getWorlds()) { @@ -125,14 +150,18 @@ public class BukkitWorldManager implements WorldManager, Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onWorldLoad(WorldLoadEvent event) { - World world = event.getWorld(); - CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world)); + this.loadWorld(new BukkitWorld(event.getWorld())); + } + + @Override + public void loadWorld(net.momirealms.craftengine.core.world.World world) { this.worldMapLock.writeLock().lock(); try { - if (this.worlds.containsKey(world.getUID())) return; - this.worlds.put(event.getWorld().getUID(), ceWorld); + if (this.worlds.containsKey(world.uuid())) return; + CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor); + this.worlds.put(world.uuid(), ceWorld); this.resetWorldArray(); - for (Chunk chunk : world.getLoadedChunks()) { + for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { handleChunkLoad(ceWorld, chunk); } } finally { @@ -140,13 +169,37 @@ public class BukkitWorldManager implements WorldManager, Listener { } } + @Override + public void loadWorld(CEWorld world) { + this.worldMapLock.writeLock().lock(); + try { + if (this.worlds.containsKey(world.world().uuid())) return; + this.worlds.put(world.world().uuid(), world); + this.resetWorldArray(); + for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) { + handleChunkLoad(world, chunk); + } + } finally { + this.worldMapLock.writeLock().unlock(); + } + } + + @Override + public CEWorld createWorld(net.momirealms.craftengine.core.world.World world, WorldDataStorage storage) { + return new BukkitCEWorld(world, storage); + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onWorldUnload(WorldUnloadEvent event) { - World world = event.getWorld(); + unloadWorld(new BukkitWorld(event.getWorld())); + } + + @Override + public void unloadWorld(net.momirealms.craftengine.core.world.World world) { CEWorld ceWorld; this.worldMapLock.writeLock().lock(); try { - ceWorld = this.worlds.remove(world.getUID()); + ceWorld = this.worlds.remove(world.uuid()); if (ceWorld == null) { return; } @@ -158,11 +211,20 @@ public class BukkitWorldManager implements WorldManager, Listener { } finally { this.worldMapLock.writeLock().unlock(); } - for (Chunk chunk : world.getLoadedChunks()) { + for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } } + @Override + public net.momirealms.craftengine.core.world.World wrap(T world) { + if (world instanceof World w) { + return new BukkitWorld(w); + } else { + throw new IllegalArgumentException(world.getClass() + " is not a Bukkit World"); + } + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onChunkLoad(ChunkLoadEvent event) { this.worldMapLock.readLock().lock(); diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/LoggerFilter.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/LoggerFilter.java index 2dc648a6a..99f9aec0f 100644 --- a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/LoggerFilter.java +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/LoggerFilter.java @@ -6,6 +6,7 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.filter.AbstractFilter; public class LoggerFilter { + public static void filter() { Logger rootLogger = (Logger) LogManager.getRootLogger(); rootLogger.addFilter(new AbstractFilter() { diff --git a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/config/additional-real-blocks.yml b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/config/additional-real-blocks.yml index 91993a87d..4fe5d6d61 100644 --- a/client-mod/src/main/resources/assets/craft-engine-fabric-mod/config/additional-real-blocks.yml +++ b/client-mod/src/main/resources/assets/craft-engine-fabric-mod/config/additional-real-blocks.yml @@ -14,9 +14,7 @@ # Q: When do I need to configure this file? # A: When the number of real block IDs is insufficient, but there are still available appearances. -# By default, the plugin only registers an additional 112 oak leaf block states (for the default configuration needs [>=28 states]). minecraft:oak_leaves: 112 - minecraft:oak_sapling: 1 minecraft:birch_sapling: 1 minecraft:spruce_sapling: 1 @@ -24,5 +22,5 @@ minecraft:jungle_sapling: 1 minecraft:dark_oak_sapling: 1 minecraft:acacia_sapling: 1 minecraft:cherry_sapling: 1 - -minecraft:anvil: 2 \ No newline at end of file +minecraft:anvil: 2 +minecraft:sugarcane: 14 \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java index 94bc14963..0af5630ca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootTable.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunction; import net.momirealms.craftengine.core.loot.function.LootFunctions; import net.momirealms.craftengine.core.loot.number.NumberProvider; import net.momirealms.craftengine.core.loot.number.NumberProviders; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.World; import org.jetbrains.annotations.Nullable; @@ -43,19 +44,19 @@ public class LootTable { NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1)); NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0)); List conditions = Optional.ofNullable(pool.get("conditions")) - .map(it -> LootConditions.fromMapList((List>) it)) + .map(it -> LootConditions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'conditions' should be a map list, current type: " + it.getClass().getSimpleName())))) .orElse(Lists.newArrayList()); List> containers = Optional.ofNullable(pool.get("entries")) - .map(it -> (List>) new ArrayList>(LootEntryContainers.fromMapList((List>) it))) + .map(it -> (List>) new ArrayList>(LootEntryContainers.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'entries' should be a map list, current type: " + it.getClass().getSimpleName()))))) .orElse(Lists.newArrayList()); List> functions = Optional.ofNullable(pool.get("functions")) - .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList((List>) it))) + .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName()))))) .orElse(Lists.newArrayList()); lootPools.add(new LootPool<>(containers, conditions, functions, rolls, bonus_rolls)); } return new LootTable<>(lootPools, Optional.ofNullable(map.get("functions")) - .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList((List>) it))) + .map(it -> (List>) new ArrayList>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName()))))) .orElse(Lists.newArrayList()) ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java index 806172e90..7fac12892 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/LootConditions.java @@ -14,8 +14,10 @@ import java.util.function.Predicate; public class LootConditions { public static final Key MATCH_ITEM = Key.from("craftengine:match_item"); + public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property"); public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus"); public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion"); + public static final Key RANDOM = Key.from("craftengine:random"); public static final Key ANY_OF = Key.from("craftengine:any_of"); public static final Key ALL_OF = Key.from("craftengine:all_of"); public static final Key ENCHANTMENT = Key.from("craftengine:enchantment"); @@ -24,6 +26,7 @@ public class LootConditions { static { register(MATCH_ITEM, MatchItemCondition.FACTORY); + register(MATCH_BLOCK_PROPERTY, MatchBlockPropertyCondition.FACTORY); register(TABLE_BONUS, TableBonusCondition.FACTORY); register(SURVIVES_EXPLOSION, SurvivesExplosionCondition.FACTORY); register(ANY_OF, AnyOfCondition.FACTORY); @@ -31,6 +34,7 @@ public class LootConditions { register(ENCHANTMENT, EnchantmentCondition.FACTORY); register(INVERTED, InvertedCondition.FACTORY); register(FALLING_BLOCK, FallingCondition.FACTORY); + register(RANDOM, RandomCondition.FACTORY); } public static void register(Key key, LootConditionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/MatchBlockPropertyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/MatchBlockPropertyCondition.java new file mode 100644 index 000000000..24c91cd6e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/MatchBlockPropertyCondition.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.core.loot.condition; + +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.loot.parameter.LootParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class MatchBlockPropertyCondition implements LootCondition { + public static final Factory FACTORY = new Factory(); + private final List> properties; + + public MatchBlockPropertyCondition(List> properties) { + this.properties = properties; + } + + @Override + public Key type() { + return LootConditions.MATCH_BLOCK_PROPERTY; + } + + @Override + public boolean test(LootContext lootContext) { + return lootContext.getOptionalParameter(LootParameters.BLOCK_STATE).map(state -> { + CustomBlock block = state.owner().value(); + for (Pair property : this.properties) { + Property propertyIns = block.getProperty(property.left()); + if (propertyIns == null) { + return false; + } + if (!state.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) { + return false; + } + } + return true; + }).orElse(false); + } + + public static class Factory implements LootConditionFactory { + + @SuppressWarnings("unchecked") + @Override + public LootCondition create(Map arguments) { + Map properties = (Map) arguments.get("properties"); + if (properties == null) { + throw new IllegalArgumentException("Missing 'properties' argument for 'match_block_property'"); + } + List> propertyList = new ArrayList<>(); + for (Map.Entry entry : properties.entrySet()) { + propertyList.add(new Pair<>(entry.getKey(), entry.getValue().toString())); + } + return new MatchBlockPropertyCondition(propertyList); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java new file mode 100644 index 000000000..547daf8ed --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/condition/RandomCondition.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.loot.condition; + +import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; + +import java.util.Map; + +public class RandomCondition implements LootCondition { + public static final Factory FACTORY = new Factory(); + private final float chance; + + public RandomCondition(float chance) { + this.chance = chance; + } + + @Override + public Key type() { + return LootConditions.RANDOM; + } + + @Override + public boolean test(LootContext lootContext) { + return RandomUtils.generateRandomFloat(0, 1) < this.chance; + } + + public static class Factory implements LootConditionFactory { + @Override + public LootCondition create(Map arguments) { + float chance = MiscUtils.getAsFloat(arguments.getOrDefault("value", 0.5f)); + return new RandomCondition(chance); + } + } +} 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 219f0f44f..180e6c100 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 @@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Collections; @@ -78,9 +79,11 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< public static class Formulas { public static final Key ORE_DROPS = Key.of("craftengine:ore_drops"); + public static final Key CROP_DROPS = Key.of("craftengine:binomial_with_bonus_count"); static { register(ORE_DROPS, OreDrops.FACTORY); + register(CROP_DROPS, CropDrops.FACTORY); } public static void register(Key key, FormulaFactory factory) { @@ -133,4 +136,40 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< } } } + + public static class CropDrops implements Formula { + public static final Factory FACTORY = new Factory(); + private final int extra; + private final float probability; + + public CropDrops(int extra, float probability) { + this.extra = extra; + this.probability = probability; + } + + @Override + public int apply(int initialCount, int enchantmentLevel) { + for (int i = 0; i < enchantmentLevel + this.extra; i++) { + if (RandomUtils.generateRandomFloat(0,1) < this.probability) { + initialCount++; + } + } + return initialCount; + } + + @Override + public Key type() { + return Formulas.CROP_DROPS; + } + + public static class Factory implements FormulaFactory { + + @Override + public Formula create(Map arguments) { + int extra = MiscUtils.getAsInt(arguments.getOrDefault("extra", 1)); + float probability = MiscUtils.getAsFloat(arguments.getOrDefault("probability", 0.5f)); + return new CropDrops(extra, probability); + } + } + } } 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 6add299f1..8e5093864 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 @@ -331,8 +331,15 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_3.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_4.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_0.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_1.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_2.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/fairy_flower.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flame_cane.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json"); // furniture diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java index 73ffa8ba7..46586334c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.PluginProperties; import net.momirealms.craftengine.core.plugin.Reloadable; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ReflectionUtils; @@ -183,6 +184,7 @@ public class ConfigManager implements Reloadable { metrics = config.getBoolean("metrics", false); checkUpdate = config.getBoolean("update-checker", false); filterConfigurationPhaseDisconnect = config.getBoolean("filter-configuration-phase-disconnect", false); + DisconnectLogFilter.instance().setEnable(filterConfigurationPhaseDisconnect); // resource pack resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java index 4e9e8136e..68338896a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java @@ -1,7 +1,5 @@ package net.momirealms.craftengine.core.plugin.logger.filter; - -import net.momirealms.craftengine.core.plugin.config.ConfigManager; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Node; @@ -13,10 +11,24 @@ import org.apache.logging.log4j.core.impl.MutableLogEvent; public class DisconnectLogFilter extends AbstractFilter { private static final String TARGET_LOGGER = "net.minecraft.server.network.ServerConfigurationPacketListenerImpl"; private static final String TARGET_MESSAGE_PATTERN = "{} lost connection: {}"; + private static DisconnectLogFilter instance; + private boolean enable = false; + + public DisconnectLogFilter() { + instance = this; + } + + public static DisconnectLogFilter instance() { + return instance; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } @Override public Result filter(LogEvent event) { - if (!ConfigManager.filterConfigurationPhaseDisconnect()) { + if (!enable) { return Result.NEUTRAL; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java index f2aa345f1..31e6f79e8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java @@ -3,8 +3,6 @@ package net.momirealms.craftengine.core.plugin.network; import io.netty.channel.Channel; import net.momirealms.craftengine.core.entity.player.Player; -import java.util.Collection; - public interface NetworkManager { String MOD_CHANNEL = "craftengine:payload"; 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 28b8f4a42..dfb84b83e 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 @@ -6,6 +6,7 @@ import org.joml.Vector3f; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Supplier; public class MiscUtils { @@ -22,6 +23,15 @@ public class MiscUtils { throw new IllegalArgumentException("Expected Map, got: " + obj.getClass().getSimpleName()); } + @SuppressWarnings("unchecked") + public static List> castToMapListOrThrow(Object obj, Supplier exceptionSupplier) { + if (obj instanceof List list) { + return (List>) list; + } else { + throw exceptionSupplier.get(); + } + } + @SuppressWarnings("unchecked") public static List castToList(Object obj, boolean allowNull) { if (allowNull && obj == null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java index 38d5f78dd..34c0b03e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java @@ -26,6 +26,10 @@ public class RandomUtils { return min + (max - min) * getInstance().random.nextFloat(); } + public static int generateRandomInt(int min, int max) { + return min >= max ? min : getInstance().random.nextInt(max - min + 1) + min; + } + public static boolean generateRandomBoolean() { return getInstance().random.nextBoolean(); } 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 cae381027..59a4978f3 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 @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.world; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.world.chunk.CEChunk; -import net.momirealms.craftengine.core.world.chunk.storage.DefaultRegionFileStorage; +import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; @@ -25,10 +25,18 @@ public abstract class CEWorld { private CEChunk lastChunk; private long lastChunkPos; - public CEWorld(World world) { + public CEWorld(World world, StorageAdaptor adaptor) { this.world = world; this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); - this.worldDataStorage = new DefaultRegionFileStorage(world.directory().resolve(REGION_DIRECTORY)); + this.worldDataStorage = adaptor.adapt(world); + this.worldHeightAccessor = world.worldHeight(); + this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; + } + + public CEWorld(World world, WorldDataStorage dataStorage) { + this.world = world; + this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + this.worldDataStorage = dataStorage; this.worldHeightAccessor = world.worldHeight(); this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java index 24f615730..58db10caf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java @@ -1,12 +1,27 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.plugin.Reloadable; +import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; +import org.jetbrains.annotations.NotNull; import java.util.UUID; public interface WorldManager extends Reloadable { + void setStorageAdaptor(@NotNull StorageAdaptor storageAdaptor); + CEWorld getWorld(UUID uuid); void delayedInit(); + + void loadWorld(World world); + + void loadWorld(CEWorld world); + + CEWorld createWorld(World world, WorldDataStorage storage); + + void unloadWorld(World world); + + World wrap(T world); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java new file mode 100644 index 000000000..9de5d571c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.world.chunk.storage; + +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.NotNull; + +public class DefaultStorageAdaptor implements StorageAdaptor { + + @Override + public @NotNull WorldDataStorage adapt(@NotNull World world) { + return new DefaultRegionFileStorage(world.directory().resolve(CEWorld.REGION_DIRECTORY)); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/StorageAdaptor.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/StorageAdaptor.java new file mode 100644 index 000000000..8a9790462 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/StorageAdaptor.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.world.chunk.storage; + +import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.NotNull; + +public interface StorageAdaptor { + + @NotNull + WorldDataStorage adapt(@NotNull World world); +} diff --git a/gradle.properties b/gradle.properties index dd0543dbb..77319d3f9 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.39 +project_version=0.0.40 config_version=18 lang_version=3 project_group=net.momirealms @@ -40,7 +40,7 @@ geantyref_version=1.3.16 zstd_version=1.5.6-9 commons_io_version=2.17.0 sparrow_nbt_version=0.3 -sparrow_util_version=0.33 +sparrow_util_version=0.34 fastutil_version=8.5.15 netty_version=4.1.119.Final joml_version=1.10.8 @@ -49,7 +49,7 @@ mojang_brigadier_version=1.0.18 byte_buddy_version=1.15.11 snake_yaml_version=2.3 anti_grief_version=0.13 -nms_helper_version=0.6 +nms_helper_version=0.12 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7