diff --git a/bukkit-loader/build.gradle.kts b/bukkit-loader/build.gradle.kts index eef46cabe..fb30824d3 100644 --- a/bukkit-loader/build.gradle.kts +++ b/bukkit-loader/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(project(":core")) implementation(project(":bukkit")) implementation(project(":bukkit:legacy")) + implementation(project(":bukkit:compatibility")) implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}") implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") diff --git a/bukkit-loader/src/main/resources/config.yml b/bukkit-loader/src/main/resources/config.yml index 125390fb7..e0f09fd16 100644 --- a/bukkit-loader/src/main/resources/config.yml +++ b/bukkit-loader/src/main/resources/config.yml @@ -92,8 +92,6 @@ resource-pack: suffix: "minecraft/items" - type: parent_path_suffix suffix: "minecraft/models/item" - - type: parent_path_suffix - suffix: "minecraft/atlases" resolution: type: merge_json deeply: true @@ -117,6 +115,11 @@ resource-pack: resolution: type: merge_json deeply: false + - term: + type: parent_path_suffix + suffix: "minecraft/atlases" + resolution: + type: merge_atlas item: # Add a tag on item name and lore diff --git a/bukkit-loader/src/main/resources/resources/default/configuration/blocks.yml b/bukkit-loader/src/main/resources/resources/default/configuration/blocks.yml index d70502472..cb1bc5c41 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/blocks.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/blocks.yml @@ -1,7 +1,7 @@ items: default:chinese_lantern: material: paper - custom-model-data: 3001 + custom-model-data: 3000 data: item-name: "" model: 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 3d38bd5e0..4834bf38d 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/categories.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/categories.yml @@ -56,4 +56,5 @@ categories: icon: default:chinese_lantern list: - default:chinese_lantern - - default:fairy_flower \ No newline at end of file + - default:fairy_flower + - default:reed \ 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 bb6d97c3f..456f15b57 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/i18n.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/i18n.yml @@ -2,6 +2,7 @@ i18n: en: item.chinese_lantern: "Chinese Lantern" item.fairy_flower: "Fairy Flower" + item.reed: "Reed" item.bench: "Bench" item.table_lamp: "Table Lamp" item.wooden_chair: "Wooden Chair" @@ -36,6 +37,7 @@ i18n: zh_cn: item.chinese_lantern: "灯笼" item.fairy_flower: "仙灵花" + item.reed: "芦苇" item.bench: "长椅" item.table_lamp: "台灯" item.wooden_chair: "木椅" diff --git a/bukkit-loader/src/main/resources/resources/default/configuration/palm_tree.yml b/bukkit-loader/src/main/resources/resources/default/configuration/palm_tree.yml index b3446fb13..d5ce8c5c4 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/palm_tree.yml @@ -235,7 +235,7 @@ items: # To prevent errors, we use tree feature from vanilla here feature: minecraft:fancy_oak bone-meal-success-chance: 0.45 - tags: + bottom-block-tags: - minecraft:dirt - minecraft:farmland - minecraft:sand diff --git a/bukkit-loader/src/main/resources/resources/default/configuration/fairy_flower.yml b/bukkit-loader/src/main/resources/resources/default/configuration/plants.yml similarity index 60% rename from bukkit-loader/src/main/resources/resources/default/configuration/fairy_flower.yml rename to bukkit-loader/src/main/resources/resources/default/configuration/plants.yml index 16f034753..328001802 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/fairy_flower.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/plants.yml @@ -1,7 +1,7 @@ items: default:fairy_flower: material: paper - custom-model-data: 3000 + custom-model-data: 3001 data: item-name: "" model: @@ -11,6 +11,18 @@ items: behavior: type: block_item block: default:fairy_flower + default:reed: + material: paper + custom-model-data: 3002 + data: + item-name: "" + model: + template: default:model/simplified_generated + arguments: + path: "minecraft:item/custom/reed" + behavior: + type: liquid_collision_block_item + block: default:reed blocks: default:fairy_flower: settings: @@ -19,6 +31,7 @@ blocks: - default:sound/grass overrides: item: default:fairy_flower + push-reaction: DESTROY behavior: type: bush_block loot: @@ -27,7 +40,7 @@ blocks: item: default:fairy_flower state: id: 0 - state: tripwire:0 + state: sugar_cane:0 models: - path: "minecraft:block/custom/fairy_flower_1" weight: 100 @@ -48,4 +61,24 @@ blocks: generation: parent: "minecraft:block/custom/fairy_flower_1" textures: - "0": "minecraft:block/custom/fairy_flower_4" \ No newline at end of file + "0": "minecraft:block/custom/fairy_flower_4" + default:reed: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + item: default:reed + push-reaction: DESTROY + behavior: + type: on_liquid_block + liquid-type: water + loot: + template: "default:loot_table/basic" + arguments: + item: default:reed + state: + id: 1 + state: sugar_cane:1 + model: + path: "minecraft:block/custom/reed" \ No newline at end of file diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json index 44c9adbde..e79d4627e 100644 --- a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json +++ b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json @@ -34,6 +34,7 @@ "from": [7, 28, 7], "to": [9, 30, 9], "rotation": {"angle": 0, "axis": "y", "origin": [7, 28, 7]}, + "shade": false, "faces": { "north": {"uv": [15, 0, 16, 1], "texture": "#0"}, "east": {"uv": [15, 0, 16, 1], "texture": "#0"}, @@ -43,15 +44,5 @@ "down": {"uv": [15, 0, 16, 1], "texture": "#0"} } } - ], - "display": { - "head": { - "translation": [0, 18.5, 0] - }, - "fixed": { - "rotation": [-90, 0, 0], - "translation": [0, 0, -15], - "scale": [2, 2, 2] - } - } + ] } \ No newline at end of file diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json new file mode 100644 index 000000000..5f872fc80 --- /dev/null +++ b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json @@ -0,0 +1,37 @@ +{ + "ambientocclusion": false, + "textures": { + "0": "block/custom/reed", + "particle": "block/custom/reed" + }, + "elements": [ + { + "from": [-0.5, -3, 0.25], + "to": [19.5, 29, 0.25], + "shade": false, + "rotation": {"angle": -45, "axis": "y", "origin": [-0.5, -3, 2.25]}, + "faces": { + "north": {"uv": [0, 0, 10, 16], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 16], "texture": "#0"}, + "south": {"uv": [0, 0, 10, 16], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 16], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 10], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 0, 0, 10], "rotation": 90, "texture": "#0"} + } + }, + { + "from": [2, -3, 14.75], + "to": [22, 29, 14.75], + "shade": false, + "rotation": {"angle": 45, "axis": "y", "origin": [2, -3, 16.75]}, + "faces": { + "north": {"uv": [0, 0, 10, 16], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 16], "texture": "#0"}, + "south": {"uv": [0, 0, 10, 16], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 16], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 10], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 0, 0, 10], "rotation": 90, "texture": "#0"} + } + } + ] +} \ No newline at end of file diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json index 21bb4288d..881f11a58 100644 --- a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json +++ b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json @@ -1,5 +1,4 @@ { - "texture_size": [64, 64], "textures": { "0": "item/custom/bench", "particle": "item/custom/bench" diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json index 25ad38f50..32067b787 100644 --- a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json +++ b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json @@ -1,5 +1,4 @@ { - "texture_size": [32, 32], "textures": { "0": "item/custom/table_lamp", "particle": "item/custom/table_lamp" diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json index f1ee8e866..81e73f921 100644 --- a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json +++ b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json @@ -1,5 +1,4 @@ { - "texture_size": [32, 32], "textures": { "0": "item/custom/wooden_chair", "particle": "item/custom/wooden_chair" diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png new file mode 100644 index 000000000..282ee6ebd Binary files /dev/null and b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png differ diff --git a/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.png b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.png new file mode 100644 index 000000000..04d930d92 Binary files /dev/null and b/bukkit-loader/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.png differ diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 0fab7cd0e..cb9f6441b 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -15,6 +15,7 @@ repositories { dependencies { compileOnly(project(":core")) compileOnly(project(":shared")) + compileOnly(project(":bukkit:compatibility")) compileOnly(project(":bukkit:legacy")) // Anti Grief compileOnly("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}") diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts new file mode 100644 index 000000000..bc8304b72 --- /dev/null +++ b/bukkit/compatibility/build.gradle.kts @@ -0,0 +1,28 @@ +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://r.irepo.space/maven/") +} + +dependencies { + compileOnly(project(":core")) + // Platform + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // NeigeItems + compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42") +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + withSourcesJar() +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(21) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java new file mode 100644 index 000000000..9fe7d0f18 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import net.momirealms.craftengine.core.item.ExternalItemProvider; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import pers.neige.neigeitems.manager.ItemManager; + +import java.util.Optional; + +public class NeigeItemsProvider implements ExternalItemProvider { + + @Override + public String plugin() { + return "NeigeItems"; + } + + @Override + public ItemStack build(String id, ItemBuildContext context) { + return ItemManager.INSTANCE.getItemStack(id, Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null)); + } +} 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 00605750f..f50ba9570 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 @@ -5,12 +5,12 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.shared.block.EmptyBlockBehavior; public class BukkitBlockBehaviors extends BlockBehaviors { - public static final Key EMPTY = Key.from("craftengine:empty"); public static final Key BUSH_BLOCK = Key.from("craftengine:bush_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"); public static final Key SAPLING_BLOCK = Key.from("craftengine:sapling_block"); + public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -19,5 +19,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(LEAVES_BLOCK, LeavesBlockBehavior.FACTORY); register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY); register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY); + register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.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 52dca81e1..d3013442a 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 @@ -83,7 +83,9 @@ public class BushBlockBehavior extends BlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - if (arguments.containsKey("tags")) { + 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; @@ -100,7 +102,7 @@ public class BushBlockBehavior extends BlockBehavior { return mayPlaceOn(belowState, world, belowPos); } - private boolean mayPlaceOn(Object belowState, Object world, Object blockPos) throws ReflectiveOperationException { + protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { for (Object tag : this.tagsCanSurviveOn) { if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) { return true; 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 new file mode 100644 index 000000000..1aca9d2bf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -0,0 +1,55 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +import java.util.List; +import java.util.Map; + +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); + this.onWater = onWater; + this.onLava = onLava; + } + + public boolean onWater() { + return this.onWater; + } + + public boolean onLava() { + return this.onLava; + } + + public static class Factory implements BlockBehaviorFactory { + @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")); + } + } + + @Override + protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { + Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos); + Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos)); + if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) { + return false; + } + if (this.onWater && (Reflections.method$FluidState$getType.invoke(fluidState) == Reflections.instance$Fluids$WATER || Reflections.field$StateHolder$owner.get(belowState) == Reflections.instance$Blocks$ICE)) { + return true; + } + if (this.onLava && Reflections.method$FluidState$getType.invoke(fluidState) == Reflections.instance$Fluids$LAVA) { + return true; + } + return false; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index b7cf50a44..60ec4845f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -1,7 +1,5 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.core.entity.furniture.*; @@ -37,11 +35,6 @@ public class BukkitFurnitureManager implements FurnitureManager { private final Map furnitureByBaseEntityId = new ConcurrentHashMap<>(256, 0.5f); private final Map furnitureByInteractionEntityId = new ConcurrentHashMap<>(512, 0.5f); - private final Map baseEntity2SubEntities = new ConcurrentHashMap<>(256, 0.5f); - - // Delay furniture cache remove for about 4-5 ticks - private static final int DELAYED_TICK = 5; - private final IntSet[] delayedRemove = new IntSet[DELAYED_TICK]; // Event listeners private final Listener dismountListener; private final FurnitureEventListener furnitureEventListener; @@ -56,9 +49,6 @@ public class BukkitFurnitureManager implements FurnitureManager { this.plugin = plugin; this.furnitureEventListener = new FurnitureEventListener(this); this.dismountListener = VersionHelper.isVersionNewerThan1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount); - for (int i = 0; i < DELAYED_TICK; i++) { - this.delayedRemove[i] = new IntOpenHashSet(); - } instance = this; } @@ -171,18 +161,6 @@ public class BukkitFurnitureManager implements FurnitureManager { } public void tick() { - IntSet first = this.delayedRemove[0]; - for (int i : first) { - // unloaded furniture might be loaded again - LoadedFurniture furniture = getLoadedFurnitureByBaseEntityId(i); - if (furniture == null) - this.baseEntity2SubEntities.remove(i); - } - first.clear(); - for (int i = 1; i < DELAYED_TICK; i++) { - this.delayedRemove[i - 1] = this.delayedRemove[i]; - } - this.delayedRemove[DELAYED_TICK-1] = first; } @Override @@ -224,12 +202,6 @@ public class BukkitFurnitureManager implements FurnitureManager { return Optional.ofNullable(this.byId.get(id)); } - @Nullable - @Override - public int[] getSubEntityIdsByBaseEntityId(int entityId) { - return this.baseEntity2SubEntities.get(entityId); - } - @Override public boolean isFurnitureBaseEntity(int entityId) { return this.furnitureByBaseEntityId.containsKey(entityId); @@ -253,7 +225,6 @@ public class BukkitFurnitureManager implements FurnitureManager { for (int sub : furniture.interactionEntityIds()) { this.furnitureByInteractionEntityId.remove(sub); } - this.delayedRemove[DELAYED_TICK-1].add(id); } } @@ -270,6 +241,7 @@ public class BukkitFurnitureManager implements FurnitureManager { if (previous != null) return; LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(entity, customFurniture)); for (Player player : display.getTrackedPlayers()) { + this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.subEntityIds()); this.plugin.networkManager().sendPacket(player, furniture.spawnPacket()); } } @@ -316,7 +288,6 @@ public class BukkitFurnitureManager implements FurnitureManager { private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture, AnchorType anchorType) { LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, anchorType); this.furnitureByBaseEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture); - this.baseEntity2SubEntities.put(loadedFurniture.baseEntityId(), loadedFurniture.subEntityIds()); for (int entityId : loadedFurniture.interactionEntityIds()) { this.furnitureByInteractionEntityId.put(entityId, loadedFurniture); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index 79de897cc..8222f585e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -74,7 +74,6 @@ public class FurnitureEventListener implements Listener { this.manager.handleEntityUnload(event.getEntity()); } - @EventHandler(ignoreCancelled = true) public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java index a39858e62..564769812 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java @@ -41,9 +41,9 @@ public class LoadedFurniture { private final WeakReference baseEntity; private final int baseEntityId; // includes elements + interactions - private final int[] subEntityIds; + private final List subEntityIds; // interactions - private final int[] interactionEntityIds; + private final List interactionEntityIds; // seats private final Set occupiedSeats = Collections.synchronizedSet(new HashSet<>()); private final Vector seats = new Vector<>(); @@ -73,14 +73,9 @@ public class LoadedFurniture { interactionEntityIds.add(entityId); this.hitBoxes.put(entityId, hitBox); } - this.subEntityIds = new int[entityIds.size()]; - for (int i = 0; i < entityIds.size(); ++i) { - this.subEntityIds[i] = entityIds.get(i); - } - this.interactionEntityIds = new int[interactionEntityIds.size()]; - for (int i = 0; i < interactionEntityIds.size(); ++i) { - this.interactionEntityIds[i] = interactionEntityIds.get(i); - } + this.subEntityIds = entityIds; + this.interactionEntityIds = interactionEntityIds; + this.resetSpawnPackets(); } private void resetSpawnPackets() { @@ -148,13 +143,6 @@ public class LoadedFurniture { this.location = location; } - public Object spawnPacket() { - if (this.cachedSpawnPacket == null) { - this.resetSpawnPackets(); - } - return this.cachedSpawnPacket; - } - @NotNull public Entity baseEntity() { Entity entity = baseEntity.get(); @@ -235,11 +223,11 @@ public class LoadedFurniture { return baseEntityId; } - public int[] interactionEntityIds() { + public List interactionEntityIds() { return interactionEntityIds; } - public int[] subEntityIds() { + public List subEntityIds() { return this.subEntityIds; } @@ -286,4 +274,8 @@ public class LoadedFurniture { this.addSeatEntity(seatEntity); seatEntity.addPassenger(player); } + + public @NotNull Object spawnPacket() { + return cachedSpawnPacket; + } } 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 02442c045..16a7458ad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -1,7 +1,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.BoneMealBehavior; 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,7 +50,6 @@ public class BukkitItemManager extends AbstractItemManager { registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES); registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); - registerVanillaItemExtraBehavior(BoneMealBehavior.INSTANCE, ItemKeys.BONE_MEAL); } private static BukkitItemManager instance; @@ -71,8 +70,15 @@ public class BukkitItemManager extends AbstractItemManager { @Override public void delayedInit() { - Bukkit.getPluginManager().registerEvents(this.itemEventListener, plugin.bootstrap()); - Bukkit.getPluginManager().registerEvents(this.debugStickListener, plugin.bootstrap()); + Bukkit.getPluginManager().registerEvents(this.itemEventListener, this.plugin.bootstrap()); + Bukkit.getPluginManager().registerEvents(this.debugStickListener, this.plugin.bootstrap()); + this.hookExternalPlugins(); + } + + private void hookExternalPlugins() { + if (this.plugin.isPluginEnabled("NeigeItems")) { + registerExternalItemProvider(new NeigeItemsProvider()); + } } @Override 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 af2690183..1add69fe2 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 @@ -79,7 +79,7 @@ public class BlockItemBehavior extends ItemBehavior { BlockPos pos = placeContext.getClickedPos(); BlockPos againstPos = placeContext.getAgainstPos(); - World world = (World) placeContext.getLevel().getHandle(); + World world = (World) placeContext.getLevel().platformWorld(); Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z()); Block bukkitBlock = world.getBlockAt(placeLocation); @@ -151,7 +151,7 @@ public class BlockItemBehavior extends ItemBehavior { Object blockState = state.customBlockState().handle(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); Object voxelShape = Reflections.method$CollisionContext$of.invoke(null, player); - Object world = Reflections.field$CraftWorld$ServerLevel.get(context.getLevel().getHandle()); + Object world = Reflections.field$CraftWorld$ServerLevel.get(context.getLevel().platformWorld()); boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) Reflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos)) && (boolean) Reflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true)); Block block = (Block) Reflections.method$CraftBlock$at.invoke(null, world, blockPos); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java deleted file mode 100644 index f01e85f05..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.momirealms.craftengine.bukkit.item.behavior; - -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.util.Key; - -import java.nio.file.Path; -import java.util.Map; - -public class BoneMealBehavior extends ItemBehavior { - public static final BoneMealBehavior INSTANCE = new BoneMealBehavior(); - - @Override - public InteractionResult useOnBlock(UseOnContext context) { - return super.useOnBlock(context); - } - - 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 f11054634..40bed4810 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 @@ -5,16 +5,17 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviors; import net.momirealms.craftengine.core.util.Key; public class BukkitItemBehaviors extends ItemBehaviors { - public static final Key EMPTY = Key.from("craftengine:empty"); public static final Key BLOCK_ITEM = Key.from("craftengine:block_item"); + public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item"); public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); 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 void init() { - register(EMPTY, (pack, path, args, id) -> EmptyItemBehavior.INSTANCE); + register(EMPTY, EmptyItemBehavior.FACTORY); register(BLOCK_ITEM, BlockItemBehavior.FACTORY); + register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 227cedae8..c8cfe78af 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -77,7 +77,7 @@ public class FurnitureItemBehavior extends ItemBehavior { // trigger event org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); - World world = (World) context.getLevel().getHandle(); + World world = (World) context.getLevel().platformWorld(); // get position and rotation for placement Vec3d finalPlacePosition; 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 new file mode 100644 index 000000000..194a1bbba --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -0,0 +1,76 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +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.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.world.BlockHitResult; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; + +import java.nio.file.Path; +import java.util.Map; + +public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + private final int offsetY; + + public LiquidCollisionBlockItemBehavior(Key blockId, int offsetY) { + super(blockId); + this.offsetY = offsetY; + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return use(context.getLevel(), context.getPlayer(), context.getHand()); + } + + @Override + public InteractionResult use(World world, Player player, InteractionHand hand) { + 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)); + 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)); + if (miss) { + return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above))); + } else { + boolean inside = Reflections.field$BlockHitResul$inside.getBoolean(blockHitResult); + return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside))); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error handling use", e); + return InteractionResult.FAIL; + } + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + Object id = arguments.get("block"); + if (id == null) { + throw new IllegalArgumentException("Missing required parameter 'block' for on_liquid_block_item behavior"); + } + int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1)); + if (id instanceof Map map) { + BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); + return new LiquidCollisionBlockItemBehavior(key, offset); + } else { + return new LiquidCollisionBlockItemBehavior(Key.of(id.toString()), offset); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java index 98db91ca1..e2e6cff48 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java @@ -299,7 +299,7 @@ public class ComponentItemFactory extends BukkitItemFactory { } @Override - protected ItemWrapper merge(ItemWrapper item1, ItemWrapper item2) { + protected ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2) { Object itemStack1 = item1.getLiteralObject(); Object itemStack2 = item2.getLiteralObject(); try { @@ -311,4 +311,17 @@ public class ComponentItemFactory extends BukkitItemFactory { } return null; } + + @Override + protected void merge(ItemWrapper item1, ItemWrapper item2) { + // load previous changes on nms items + item1.load(); + Object itemStack1 = item1.getLiteralObject(); + Object itemStack2 = item2.getLiteralObject(); + try { + Reflections.method$ItemStack$applyComponents.invoke(itemStack1, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2)); + } catch (Exception e) { + plugin.logger().warn("Failed to merge item", e); + } + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 5550598db..085c997e8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -234,11 +234,18 @@ public class UniversalItemFactory extends BukkitItemFactory { } @Override - protected ItemWrapper merge(ItemWrapper item1, ItemWrapper item2) { + protected ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2) { Object itemStack = ItemObject.copy(item2.getLiteralObject()); ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject()))); // one more step than vanilla TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count()); } + + @Override + protected void merge(ItemWrapper item1, ItemWrapper item2) { + // load previous changes on nms items + item1.load(); + TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java index 78ba75961..2aad44aad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java @@ -44,8 +44,10 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature appearances = blockManager.blockAppearanceArranger().get(baseBlockId); if (appearances == null) return; int i = 0; - Component component = Component.text(baseBlockId + ": "); - List children = new ArrayList<>(); + Component block = Component.text(baseBlockId + ": "); + plugin().senderFactory().wrap(context.sender()).sendMessage(block); + + List batch = new ArrayList<>(); for (int appearance : appearances) { Component text = Component.text("|"); List reals = blockManager.appearanceToRealStates(appearance); @@ -64,11 +66,19 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature reals = blockManager.realBlockArranger().get(baseBlockId); if (reals == null) return; int i = 0; - Component component = Component.text(baseBlockId + ": "); - List children = new ArrayList<>(); + Component block = Component.text(baseBlockId + ": "); + plugin().senderFactory().wrap(context.sender()).sendMessage(block); + + List batch = new ArrayList<>(100); for (int real : reals) { ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(real); if (state.isEmpty()) { Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.GREEN); - children.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover))); + batch.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover))); } else { Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.RED); hover = hover.append(Component.newline()).append(Component.text(state.toString()).color(NamedTextColor.GRAY)); - children.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover))); + batch.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover))); } i++; + if (batch.size() == 100) { + plugin().senderFactory().wrap(context.sender()) + .sendMessage(Component.text("").children(batch)); + batch.clear(); + } + } + if (!batch.isEmpty()) { + plugin().senderFactory().wrap(context.sender()) + .sendMessage(Component.text("").children(batch)); + batch.clear(); } - plugin().senderFactory().wrap(context.sender()) - .sendMessage(component.children(children)); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java index ff3722abd..c57ffc6da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; 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.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -50,6 +51,8 @@ public class DebugTargetBlockCommand extends BukkitCommandFeature if (immutableBlockState != null) { sender.sendMessage(Component.text(immutableBlockState.toString())); } + ImmutableBlockState dataInCache = plugin().worldManager().getWorld(block.getWorld().getUID()).getBlockStateAtIfLoaded(LocationUtils.toBlockPos(block.getLocation())); + sender.sendMessage(Component.text("cache-state: " + !dataInCache.isEmpty())); try { @SuppressWarnings("unchecked") Set tags = (Set) Reflections.field$Holder$Reference$tags.get(holder); 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 a6bd3107a..606f31b5a 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 @@ -125,6 +125,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener { registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos); registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket); registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket); + registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket); + registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket); + registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket()); @@ -265,6 +268,28 @@ public class BukkitNetworkManager implements NetworkManager, Listener { this.packetsConsumer.accept(player.serverPlayer(), packet); } + public void receivePacket(@NotNull NetWorkUser player, Object packet) { + Channel channel = player.nettyChannel(); + if (channel.isOpen()) { + List handlerNames = channel.pipeline().names(); + if (handlerNames.contains("via-encoder")) { + channel.pipeline().context("via-decoder").fireChannelRead(packet); + } else if (handlerNames.contains("ps_decoder_transformer")) { + channel.pipeline().context("ps_decoder_transformer").fireChannelRead(packet); + } else if (handlerNames.contains("decompress")) { + channel.pipeline().context("decompress").fireChannelRead(packet); + } else { + if (handlerNames.contains("decrypt")) { + channel.pipeline().context("decrypt").fireChannelRead(packet); + } else { + channel.pipeline().context("splitter").fireChannelRead(packet); + } + } + } else { + ((ByteBuf) packet).release(); + } + } + private void injectServerChannel(Channel serverChannel) { ChannelPipeline pipeline = serverChannel.pipeline(); ChannelHandler connectionHandler = pipeline.get(CONNECTION_HANDLER_NAME); 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 4c45def46..7d8048590 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 @@ -14,6 +14,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.font.ImageManager; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; @@ -32,10 +33,9 @@ import org.bukkit.util.RayTraceResult; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class PacketConsumers { private static int[] mappings; @@ -344,7 +344,7 @@ public class PacketConsumers { } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); } - }, (World) player.level().getHandle(), (MCUtils.fastFloor(player.x())) >> 4, (MCUtils.fastFloor(player.z())) >> 4); + }, (World) player.level().platformWorld(), (MCUtils.fastFloor(player.x())) >> 4, (MCUtils.fastFloor(player.z())) >> 4); } else { handleSetCreativeSlotPacketOnMainThread(player, packet); } @@ -532,6 +532,7 @@ public class PacketConsumers { int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet); LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(entityId); if (furniture != null) { + user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.subEntityIds()); user.sendPacket(furniture.spawnPacket(), false); } } @@ -566,7 +567,7 @@ public class PacketConsumers { try { IntList intList = (IntList) Reflections.field$ClientboundRemoveEntitiesPacket$entityIds.get(packet); for (int i = 0, size = intList.size(); i < size; i++) { - int[] entities = BukkitFurnitureManager.instance().getSubEntityIdsByBaseEntityId(intList.getInt(i)); + List entities = user.furnitureView().remove(intList.getInt(i)); if (entities == null) continue; for (int entityId : entities) { intList.add(entityId); @@ -661,4 +662,92 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundSoundPacket", e); } }; + + // we handle it on packet level to prevent it from being captured by plugins (most are chat plugins) + public static final TriConsumer CHAT = (user, event, packet) -> { + try { + String message = (String) Reflections.field$ServerboundChatPacket$message.get(packet); + if (message != null && !message.isEmpty()) { + ImageManager manager = CraftEngine.instance().imageManager(); + if (!manager.isDefaultFontInUse()) return; + runIfContainsIllegalCharacter(message, manager, (s) -> { + event.setCancelled(true); + try { + Object newPacket = Reflections.constructor$ServerboundChatPacket.newInstance( + s, + Reflections.field$ServerboundChatPacket$timeStamp.get(packet), + Reflections.field$ServerboundChatPacket$salt.get(packet), + Reflections.field$ServerboundChatPacket$signature.get(packet), + Reflections.field$ServerboundChatPacket$lastSeenMessages.get(packet) + ); + user.receivePacket(newPacket); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to create replaced chat packet", e); + } + }); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundChatPacket", e); + } + }; + + // we handle it on packet level to prevent it from being captured by plugins + public static final TriConsumer RENAME_ITEM = (user, event, packet) -> { + try { + String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet); + if (message != null && !message.isEmpty()) { + ImageManager manager = CraftEngine.instance().imageManager(); + if (!manager.isDefaultFontInUse()) return; + runIfContainsIllegalCharacter(message, manager, (s) -> { + try { + Reflections.field$ServerboundRenameItemPacket$name.set(packet, s); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to replace chat", e); + } + }); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); + } + }; + + // we handle it on packet level to prevent it from being captured by plugins + public static final TriConsumer SIGN_UPDATE = (user, event, packet) -> { + try { + String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); + ImageManager manager = CraftEngine.instance().imageManager(); + if (!manager.isDefaultFontInUse()) return; + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (line != null && !line.isEmpty()) { + try { + int lineIndex = i; + runIfContainsIllegalCharacter(line, manager, (s) -> lines[lineIndex] = s); + } catch (Exception ignore) { + } + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e); + } + }; + + private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer callback) { + char[] chars = string.toCharArray(); + int[] codepoints = CharacterUtils.charsToCodePoints(chars); + int[] newCodepoints = new int[codepoints.length]; + boolean hasIllegal = false; + for (int i = 0; i < codepoints.length; i++) { + int codepoint = codepoints[i]; + if (!manager.isIllegalCharacter(codepoint)) { + newCodepoints[i] = codepoint; + } else { + newCodepoints[i] = '*'; + hasIllegal = true; + } + } + if (hasIllegal) { + callback.accept(new String(newCodepoints, 0, newCodepoints.length)); + } + } } 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 d6cc5e3bf..07e3fccad 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 @@ -16,20 +16,23 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.*; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { private final Channel channel; @@ -60,6 +63,8 @@ public class BukkitServerPlayer extends Player { private Key lastUsedRecipe = null; + private Map> furnitureView = new ConcurrentHashMap<>(); + public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) { this.channel = channel; this.plugin = plugin; @@ -201,11 +206,41 @@ public class BukkitServerPlayer extends Player { platformPlayer().closeInventory(); } + // TODO DO NOT USE BUKKIT API + @Override + public BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule) { + RayTraceResult result = platformPlayer().rayTraceBlocks(distance, FluidUtils.toCollisionRule(collisionRule)); + if (result == null) { + Location eyeLocation = platformPlayer().getEyeLocation(); + Location targetLocation = eyeLocation.clone(); + targetLocation.add(eyeLocation.getDirection().multiply(distance)); + return BlockHitResult.miss(new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ()), + Direction.getApproximateNearest(eyeLocation.getX() - targetLocation.getX(), eyeLocation.getY() - targetLocation.getY(), eyeLocation.getZ() - targetLocation.getZ()), + new BlockPos(targetLocation.getBlockX(), targetLocation.getBlockY(), targetLocation.getBlockZ()) + ); + } else { + Vector hitPos = result.getHitPosition(); + Block hitBlock = result.getHitBlock(); + Location hitBlockLocation = hitBlock.getLocation(); + return new BlockHitResult( + new Vec3d(hitPos.getX(), hitPos.getY(), hitPos.getZ()), + DirectionUtils.toDirection(result.getHitBlockFace()), + new BlockPos(hitBlockLocation.getBlockX(), hitBlockLocation.getBlockY(), hitBlockLocation.getBlockZ()), + false + ); + } + } + @Override public void sendPacket(Object packet, boolean immediately) { this.plugin.networkManager().sendPacket(this, packet, immediately); } + @Override + public void receivePacket(Object packet) { + this.plugin.networkManager().receivePacket(this, packet); + } + @Override public ConnectionState decoderState() { return decoderState; @@ -553,6 +588,11 @@ public class BukkitServerPlayer extends Player { return playerRef.get(); } + @Override + public Map> furnitureView() { + return this.furnitureView; + } + public void setResendSound() { resentSoundTick = gameTicks(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FluidUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FluidUtils.java new file mode 100644 index 000000000..3af0cadc1 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FluidUtils.java @@ -0,0 +1,24 @@ +package net.momirealms.craftengine.bukkit.util; + +import net.momirealms.craftengine.core.world.FluidCollisionRule; +import org.bukkit.FluidCollisionMode; + +public class FluidUtils { + + private FluidUtils() {} + + public static FluidCollisionMode toCollisionRule(FluidCollisionRule rule) { + switch (rule) { + case NONE -> { + return FluidCollisionMode.NEVER; + } + case ALWAYS -> { + return FluidCollisionMode.ALWAYS; + } + case SOURCE_ONLY -> { + return FluidCollisionMode.SOURCE_ONLY; + } + } + return FluidCollisionMode.NEVER; + } +} 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 4fa7782e9..b960901d0 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 @@ -13,6 +13,14 @@ public class LocationUtils { return new Vec3d(loc.getX(), loc.getY(), loc.getZ()); } + public static Vec3d fromVec(Object vec) throws ReflectiveOperationException { + return new Vec3d( + Reflections.field$Vec3$x.getDouble(vec), + Reflections.field$Vec3$y.getDouble(vec), + Reflections.field$Vec3$z.getDouble(vec) + ); + } + public static Object toBlockPos(BlockPos pos) { try { return Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z()); @@ -21,6 +29,14 @@ public class LocationUtils { } } + 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) + ); + } + public static Object toBlockPos(int x, int y, int z) { try { return Reflections.constructor$BlockPos.newInstance(x, y, z); 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 0e474bebd..4ed697c8e 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 @@ -7,6 +7,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; +import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; @@ -26,6 +27,7 @@ import sun.misc.Unsafe; import java.io.BufferedReader; import java.lang.reflect.*; +import java.time.Instant; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -134,9 +136,7 @@ public class Reflections { ); public static final Constructor constructor$ClientboundSystemChatPacket = requireNonNull( - ReflectionUtils.getConstructor( - clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class - ) + ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class) ); public static final Field field$ClientboundSystemChatPacket$overlay = requireNonNull( @@ -189,6 +189,11 @@ public class Reflections { clazz$ClientboundSystemChatPacket, clazz$Component, 0 ); + public static final Field field$ClientboundSystemChatPacket$adventure$content = + ReflectionUtils.getDeclaredField( + clazz$ClientboundSystemChatPacket, Component.class, 0 + ); + public static final Field field$ClientboundSystemChatPacket$text = ReflectionUtils.getDeclaredField( clazz$ClientboundSystemChatPacket, String.class, 0 @@ -1455,6 +1460,12 @@ public class Reflections { ) ); + public static final Method method$Direction$ordinal = requireNonNull( + ReflectionUtils.getMethod( + clazz$Direction, new String[]{"ordinal"} + ) + ); + public static final Method method$Direction$values = requireNonNull( ReflectionUtils.getStaticMethod( clazz$Direction, clazz$Direction.arrayType() @@ -3326,6 +3337,7 @@ public class Reflections { public static final Object instance$Blocks$STONE; public static final Object instance$Blocks$STONE$defaultState; public static final Object instance$Blocks$FIRE; + public static final Object instance$Blocks$ICE; static { try { @@ -3337,6 +3349,8 @@ public class Reflections { Object stone = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "stone"); instance$Blocks$STONE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, stone); instance$Blocks$STONE$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$STONE); + Object ice = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "ice"); + instance$Blocks$ICE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, ice); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -3862,11 +3876,17 @@ public class Reflections { ); public static final Object instance$Fluids$WATER; + public static final Object instance$Fluids$LAVA; + public static final Object instance$Fluids$EMPTY; static { try { Object waterId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "water"); instance$Fluids$WATER = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, waterId); + Object lavaId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "lava"); + instance$Fluids$LAVA = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, lavaId); + Object emptyId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "empty"); + instance$Fluids$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, emptyId); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -5015,4 +5035,170 @@ public class Reflections { clazz$ItemStack, clazz$Item ) ); + + public static final Class clazz$BlockHitResult = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.phys.BlockHitResult"), + BukkitReflectionUtils.assembleMCClass("world.phys.MovingObjectPositionBlock") + ) + ); + + public static final Class clazz$ClipContext$Fluid = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.ClipContext$Fluid"), + BukkitReflectionUtils.assembleMCClass("world.level.RayTrace$FluidCollisionOption") + ) + ); + + public static final Method method$ClipContext$Fluid$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$ClipContext$Fluid, clazz$ClipContext$Fluid.arrayType() + ) + ); + + public static final Object instance$ClipContext$Fluid$NONE; + public static final Object instance$ClipContext$Fluid$SOURCE_ONLY; + public static final Object instance$ClipContext$Fluid$ANY; + + static { + try { + Object[] values = (Object[]) method$ClipContext$Fluid$values.invoke(null); + instance$ClipContext$Fluid$NONE = values[0]; + instance$ClipContext$Fluid$SOURCE_ONLY = values[1]; + instance$ClipContext$Fluid$ANY = values[2]; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public static final Method method$Item$getPlayerPOVHitResult = requireNonNull( + ReflectionUtils.getDeclaredMethod( + clazz$Item, clazz$BlockHitResult, clazz$Level, clazz$Player, clazz$ClipContext$Fluid + ) + ); + + public static final Method method$BlockHitResult$withPosition = requireNonNull( + ReflectionUtils.getMethod( + clazz$BlockHitResult, clazz$BlockHitResult, clazz$BlockPos + ) + ); + + public static final Field field$BlockHitResul$blockPos = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$BlockHitResult, clazz$BlockPos, 0 + ) + ); + + public static final Field field$BlockHitResul$direction = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$BlockHitResult, clazz$Direction, 0 + ) + ); + + public static final Field field$BlockHitResul$miss = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$BlockHitResult, boolean.class, 0 + ) + ); + + public static final Field field$BlockHitResul$inside = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$BlockHitResult, boolean.class, 1 + ) + ); + + public static final Class clazz$HitResult = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.phys.HitResult"), + BukkitReflectionUtils.assembleMCClass("world.phys.MovingObjectPosition") + ) + ); + + public static final Field field$HitResult$location = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$HitResult, clazz$Vec3, 0 + ) + ); + + public static final Class clazz$MessageSignature = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.MessageSignature") + ) + ); + + public static final Class clazz$LastSeenMessages$Update = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$Update"), + BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$b") + ) + ); + + public static final Class clazz$ServerboundChatPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundChatPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInChat") + ) + ); + + public static final Constructor constructor$ServerboundChatPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ServerboundChatPacket, String.class, Instant.class, long.class, clazz$MessageSignature, clazz$LastSeenMessages$Update + ) + ); + + public static final Field field$ServerboundChatPacket$message = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, String.class, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$timeStamp = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, Instant.class, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$salt = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, long.class, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$signature = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, clazz$MessageSignature, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$lastSeenMessages = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, clazz$LastSeenMessages$Update, 0 + ) + ); + + public static final Class clazz$ServerboundRenameItemPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundRenameItemPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInItemName") + ) + ); + + public static final Field field$ServerboundRenameItemPacket$name = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundRenameItemPacket, String.class, 0 + ) + ); + + public static final Class clazz$ServerboundSignUpdatePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundSignUpdatePacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInUpdateSign") + ) + ); + + public static final Field field$ServerboundSignUpdatePacket$lines = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundSignUpdatePacket, String[].class, 0 + ) + ); } 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 6c886d116..b5cddf244 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 @@ -15,7 +15,7 @@ public class BukkitCEWorld extends CEWorld { @Override public void tick() { if (ConfigManager.enableLightSystem()) { - LightUtils.updateChunkLight((org.bukkit.World) world.getHandle(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1)); + LightUtils.updateChunkLight((org.bukkit.World) world.platformWorld(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1)); super.updatedSectionPositions.clear(); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java index f4fee5aca..89192c5ac 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.world; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.ItemUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -28,36 +29,45 @@ public class BukkitWorld implements World { } @Override - public org.bukkit.World getHandle() { + public org.bukkit.World platformWorld() { return world.get(); } + @Override + public Object serverWorld() { + try { + return Reflections.field$CraftWorld$ServerLevel.get(platformWorld()); + } catch (Exception e) { + throw new RuntimeException("Failed to get server world", e); + } + } + @Override public WorldHeight worldHeight() { if (this.worldHeight == null) { - this.worldHeight = WorldHeight.create(getHandle().getMinHeight(), getHandle().getMaxHeight() - getHandle().getMinHeight()); + this.worldHeight = WorldHeight.create(platformWorld().getMinHeight(), platformWorld().getMaxHeight() - platformWorld().getMinHeight()); } return this.worldHeight; } @Override public WorldBlock getBlockAt(int x, int y, int z) { - return new BukkitWorldBlock(getHandle().getBlockAt(x, y, z)); + return new BukkitWorldBlock(platformWorld().getBlockAt(x, y, z)); } @Override public String name() { - return getHandle().getName(); + return platformWorld().getName(); } @Override public Path directory() { - return getHandle().getWorldFolder().toPath(); + return platformWorld().getWorldFolder().toPath(); } @Override public UUID uuid() { - return getHandle().getUID(); + return platformWorld().getUID(); } @Override @@ -65,16 +75,16 @@ public class BukkitWorld implements World { ItemStack itemStack = (ItemStack) item.load(); if (ItemUtils.isEmpty(itemStack)) return; if (VersionHelper.isVersionNewerThan1_21_2()) { - getHandle().dropItemNaturally(new Location(null, location.x(), location.y(), location.z()), (ItemStack) item.getItem()); + platformWorld().dropItemNaturally(new Location(null, location.x(), location.y(), location.z()), (ItemStack) item.getItem()); } else { - getHandle().dropItemNaturally(new Location(null, location.x() - 0.5, location.y() - 0.5, location.z() - 0.5), (ItemStack) item.getItem()); + platformWorld().dropItemNaturally(new Location(null, location.x() - 0.5, location.y() - 0.5, location.z() - 0.5), (ItemStack) item.getItem()); } } @Override public void dropExp(Vec3d location, int amount) { if (amount <= 0) return; - EntityUtils.spawnEntity(getHandle(), new Location(getHandle(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> { + EntityUtils.spawnEntity(platformWorld(), new Location(platformWorld(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> { ExperienceOrb orb = (ExperienceOrb) e; orb.setExperience(amount); }); @@ -82,6 +92,6 @@ public class BukkitWorld implements World { @Override public void playBlockSound(Vec3d location, Key sound, float volume, float pitch) { - getHandle().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch); + platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java index 8217b0000..5cbc64295 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviors.java @@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; public class BlockBehaviors { + public static final Key EMPTY = Key.from("craftengine:empty"); public static void register(Key key, BlockBehaviorFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK_BEHAVIOR_FACTORY) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index 639a61bcf..ada46b3c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.plugin.Reloadable; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.util.Key; -import javax.annotation.Nullable; import java.util.Optional; public interface FurnitureManager extends Reloadable, ConfigSectionParser { @@ -25,8 +24,5 @@ public interface FurnitureManager extends Reloadable, ConfigSectionParser { Optional getFurniture(Key id); - @Nullable - int[] getSubEntityIdsByBaseEntityId(int entityId); - boolean isFurnitureBaseEntity(int entityId); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 67cf0d33f..7465a7fbd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.FluidCollisionRule; import org.jetbrains.annotations.Nullable; public abstract class Player extends Entity implements NetWorkUser { @@ -68,4 +70,6 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract void giveItem(Item item); public abstract void closeInventory(); + + public abstract BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java index ffc3f6dd2..eb6ebc802 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.font; import net.momirealms.craftengine.core.util.Key; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -19,6 +20,10 @@ public class Font { return this.idToCodepoint.containsKey(codepoint); } + public Collection codepointsInUse() { + return Collections.unmodifiableCollection(this.idToCodepoint.keySet()); + } + public BitmapImage getImageByCodepoint(int codepoint) { return this.idToCodepoint.get(codepoint); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java index c4916334c..ac114d578 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java @@ -13,11 +13,18 @@ import java.util.function.BiFunction; public interface ImageManager extends Reloadable, ConfigSectionParser { String CONFIG_SECTION_NAME = "images"; + Key DEFAULT_FONT = Key.of("minecraft:default"); default String sectionId() { return CONFIG_SECTION_NAME; } + void delayedLoad(); + + boolean isDefaultFontInUse(); + + boolean isIllegalCharacter(int codepoint); + Collection fontsInUse(); Optional bitmapImageByCodepoint(Key font, int codepoint); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java index 4c4436362..ace8f47e0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java @@ -18,6 +18,7 @@ public class ImageManagerImpl implements ImageManager { private final HashMap fonts = new HashMap<>(); // namespace:id image private final HashMap images = new HashMap<>(); + private final Set illegalChars = new HashSet<>(); private OffsetFont offsetFont; @@ -36,6 +37,24 @@ public class ImageManagerImpl implements ImageManager { public void unload() { this.fonts.clear(); this.images.clear(); + this.illegalChars.clear(); + } + + @Override + public void delayedLoad() { + Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> { + this.illegalChars.addAll(font.codepointsInUse()); + }); + } + + @Override + public boolean isDefaultFontInUse() { + return !this.illegalChars.isEmpty(); + } + + @Override + public boolean isIllegalCharacter(int codepoint) { + return this.illegalChars.contains(codepoint); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java index ae652add7..1d5ffbcbd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java @@ -280,7 +280,13 @@ public class AbstractItem, I> implements Item { @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public Item merge(Item another) { - return new AbstractItem<>(this.factory, this.factory.merge(this.item, ((AbstractItem) another).item)); + public Item mergeCopy(Item another) { + return new AbstractItem<>(this.factory, this.factory.mergeCopy(this.item, ((AbstractItem) another).item)); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public void merge(Item another) { + this.factory.merge(this.item, ((AbstractItem) another).item); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index baa2330b1..eb8d76b67 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -23,6 +23,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl protected static final List VANILLA_ITEMS = new ArrayList<>(); protected static final Map>> VANILLA_ITEM_TAGS = new HashMap<>(); + protected final Map> externalItemProviders = new HashMap<>(); protected final Map>> dataFunctions = new HashMap<>(); protected final Map> customItems = new HashMap<>(); protected final Map>> customItemTags; @@ -47,6 +48,18 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } + @Override + public ExternalItemProvider getExternalItemProvider(String name) { + return this.externalItemProviders.get(name); + } + + @Override + public boolean registerExternalItemProvider(ExternalItemProvider externalItemProvider) { + if (this.externalItemProviders.containsKey(externalItemProvider.plugin())) return false; + this.externalItemProviders.put(externalItemProvider.plugin(), externalItemProvider); + return true; + } + @Override public void unload() { super.clearModelsToGenerate(); @@ -165,6 +178,13 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } private void registerFunctions() { + registerDataFunction((obj) -> { + Map data = MiscUtils.castToMap(obj, false); + String plugin = data.get("plugin").toString(); + String id = data.get("id").toString(); + ExternalItemProvider provider = AbstractItemManager.this.getExternalItemProvider(plugin); + return new ExternalModifier<>(id, Objects.requireNonNull(provider, "Item provider " + plugin + " not found")); + }, "external"); registerDataFunction((obj) -> { String name = obj.toString(); return new DisplayNameModifier<>(name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java b/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java new file mode 100644 index 000000000..f5cea3ada --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.item; + +import org.jetbrains.annotations.Nullable; + +public interface ExternalItemProvider { + + String plugin(); + + @Nullable + I build(String id, ItemBuildContext context); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 7de763c22..3895da0db 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -117,5 +117,7 @@ public interface Item { Object getLiteralObject(); - Item merge(Item another); + Item mergeCopy(Item another); + + void merge(Item another); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java index 8b59f1636..c65a15740 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java @@ -112,5 +112,7 @@ public abstract class ItemFactory

, I> protected abstract Optional repairCost(ItemWrapper item); - protected abstract ItemWrapper merge(ItemWrapper item1, ItemWrapper item2); + protected abstract ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2); + + protected abstract void merge(ItemWrapper item1, ItemWrapper item2); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index 8215252ca..3b2b406fb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -51,6 +51,10 @@ public interface ItemManager extends Reloadable, ModelGenerator, ConfigSectio Key customItemId(T itemStack); + ExternalItemProvider getExternalItemProvider(String name); + + boolean registerExternalItemProvider(ExternalItemProvider externalItemProvider); + Optional> getCustomItem(Key key); Optional> getItemBehavior(Key key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java index e9ee30aaf..68c0c1e49 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java @@ -1,5 +1,20 @@ package net.momirealms.craftengine.core.item.behavior; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.util.Key; + +import java.nio.file.Path; +import java.util.Map; + public class EmptyItemBehavior extends ItemBehavior { + public static final Factory FACTORY = new Factory(); public static final EmptyItemBehavior INSTANCE = new EmptyItemBehavior(); + + public static class Factory implements ItemBehaviorFactory { + + @Override + public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { + return INSTANCE; + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java index e83921e69..b491baf28 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java @@ -12,6 +12,7 @@ import java.nio.file.Path; import java.util.Map; public class ItemBehaviors { + public static final Key EMPTY = Key.from("craftengine:empty"); public static void register(Key key, ItemBehaviorFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.ITEM_BEHAVIOR_FACTORY) diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java new file mode 100644 index 000000000..296810e37 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ExternalItemProvider; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; + +public class ExternalModifier implements ItemModifier { + private final String id; + private final ExternalItemProvider provider; + + public ExternalModifier(String id, ExternalItemProvider provider) { + this.id = id; + this.provider = provider; + } + + @Override + public String name() { + return "external"; + } + + @SuppressWarnings("unchecked") + @Override + public void apply(Item item, ItemBuildContext context) { + I another = this.provider.build(id, context); + if (another == null) { + CraftEngine.instance().logger().warn("'" + id + "' could not be found in " + provider.plugin()); + return; + } + Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); + item.merge(anotherWrapped); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 8d72de36b..46f9e9909 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -96,7 +96,7 @@ public class CustomSmithingTransformRecipe implements Recipe { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; if (this.mergeComponents) { - finalResult = base.merge(wrappedResult); + finalResult = base.mergeCopy(wrappedResult); } for (ItemDataProcessor processor : this.processors) { processor.accept(base, wrappedResult, finalResult); 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 6e30309bf..3af5a0c7b 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 @@ -320,14 +320,17 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/stripped_palm_log.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/stripped_palm_log_top.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_leaves.png"); - // fairy flower - plugin.saveResource("resources/default/configuration/fairy_flower.yml"); + // plants + plugin.saveResource("resources/default/configuration/plants.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_1.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_2.png"); 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/item/custom/fairy_flower.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.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 plugin.saveResource("resources/default/configuration/furniture.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergeAltasResolution.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergeAltasResolution.java new file mode 100644 index 000000000..0445a763f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/MergeAltasResolution.java @@ -0,0 +1,58 @@ +package net.momirealms.craftengine.core.pack.conflict.resolution; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.Key; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Map; + +public class MergeAltasResolution implements Resolution { + public static final Factory FACTORY = new Factory(); + public static final MergeAltasResolution INSTANCE = new MergeAltasResolution(); + + @Override + public void run(Path existing, Path conflict) { + try { + JsonObject j1 = GsonHelper.readJsonFile(existing).getAsJsonObject(); + JsonObject j2 = GsonHelper.readJsonFile(conflict).getAsJsonObject(); + JsonObject j3 = new JsonObject(); + JsonArray ja1 = j1.getAsJsonArray("sources"); + JsonArray ja2 = j2.getAsJsonArray("sources"); + JsonArray ja3 = new JsonArray(); + HashSet elements = new HashSet<>(); + for (JsonElement je : ja1) { + if (elements.add(je.getAsString())) { + ja3.add(je); + } + } + for (JsonElement je : ja2) { + if (elements.add(je.getAsString())) { + ja3.add(je); + } + } + j3.add("sources", ja3); + GsonHelper.writeJsonFile(j3, existing); + } catch (IOException e) { + CraftEngine.instance().logger().severe("Failed to merge json when resolving file conflicts", e); + } + } + + @Override + public Key type() { + return Resolutions.MERGE_ATLAS; + } + + public static class Factory implements ResolutionFactory { + + @Override + public Resolution create(Map arguments) { + return INSTANCE; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java index cefef34ed..9d65cf9cc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/Resolutions.java @@ -12,6 +12,7 @@ import java.util.Map; public class Resolutions { public static final Key RETAIN_MATCHING = Key.of("craftengine:retain_matching"); public static final Key MERGE_JSON = Key.of("craftengine:merge_json"); + public static final Key MERGE_ATLAS = Key.of("craftengine:merge_atlas"); public static final Key CONDITIONAL = Key.of("craftengine:conditional"); public static final Key MERGE_PACK_MCMETA = Key.of("craftengine:merge_pack_mcmeta"); @@ -20,6 +21,7 @@ public class Resolutions { register(MERGE_JSON, MergeJsonResolution.FACTORY); register(CONDITIONAL, ConditionalResolution.FACTORY); register(MERGE_PACK_MCMETA, MergePackMcMetaResolution.FACTORY); + register(MERGE_ATLAS, MergeAltasResolution.FACTORY); } public static void register(Key key, ResolutionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 3e173c3e7..931804201 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -106,6 +106,7 @@ public abstract class CraftEngine implements Plugin { this.blockManager.delayedLoad(); this.itemBrowserManager.delayedLoad(); this.soundManager.delayedLoad(); + this.imageManager.delayedLoad(); if (ConfigManager.debug()) { this.debugger = (s) -> logger.info("[Debug] " + s.get()); } else { @@ -126,11 +127,11 @@ public abstract class CraftEngine implements Plugin { // delay the reload so other plugins can register some parsers this.scheduler.sync().runDelayed(() -> { this.registerParsers(); + this.itemManager.delayedInit(); this.reload(); this.guiManager.delayedInit(); this.recipeManager.delayedInit(); this.blockManager.delayedInit(); - this.itemManager.delayedInit(); this.worldManager.delayedInit(); this.packManager.delayedInit(); this.furnitureManager.delayedInit(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index 465c3d88e..c70b49362 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -5,6 +5,9 @@ import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.ApiStatus; +import java.util.List; +import java.util.Map; + public interface NetWorkUser { boolean isOnline(); @@ -16,6 +19,8 @@ public interface NetWorkUser { void sendPacket(Object packet, boolean immediately); + void receivePacket(Object packet); + @ApiStatus.Internal ConnectionState decoderState(); @@ -29,4 +34,6 @@ public interface NetWorkUser { Object serverPlayer(); Object platformPlayer(); + + Map> furnitureView(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java index 44d222cd9..0678d4f85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java @@ -182,6 +182,21 @@ public enum Direction { }; } + public static Direction getApproximateNearest(double x, double y, double z) { + Direction nearestDirection = null; + double maxDotProduct = -Double.MAX_VALUE; + for (Direction direction : Direction.values()) { + double dotProduct = x * direction.vec.x() + + y * direction.vec.y() + + z * direction.vec.z(); + if (dotProduct > maxDotProduct) { + maxDotProduct = dotProduct; + nearestDirection = direction; + } + } + return nearestDirection; + } + public static Direction from3DDataValue(int id) { return BY_3D_DATA[Math.abs(id % BY_3D_DATA.length)]; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/FluidCollisionRule.java b/core/src/main/java/net/momirealms/craftengine/core/world/FluidCollisionRule.java new file mode 100644 index 000000000..7d49f54b9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/FluidCollisionRule.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.world; + +public enum FluidCollisionRule { + NONE, + SOURCE_ONLY, + ALWAYS +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/World.java b/core/src/main/java/net/momirealms/craftengine/core/world/World.java index 7cabc13a5..3b1cc134e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/World.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/World.java @@ -8,7 +8,9 @@ import java.util.UUID; public interface World { - Object getHandle(); + Object platformWorld(); + + Object serverWorld(); WorldHeight worldHeight(); diff --git a/server-mod/src/main/java/net/momirealms/craftengine/mod/CraftEngineBlock.java b/server-mod/src/main/java/net/momirealms/craftengine/mod/CraftEngineBlock.java index 64afc437f..caa871ddc 100644 --- a/server-mod/src/main/java/net/momirealms/craftengine/mod/CraftEngineBlock.java +++ b/server-mod/src/main/java/net/momirealms/craftengine/mod/CraftEngineBlock.java @@ -24,7 +24,7 @@ import net.momirealms.craftengine.shared.block.*; import org.jetbrains.annotations.NotNull; public class CraftEngineBlock extends Block implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock { - private static final PaperWeightStoneBlockShape STONE = new PaperWeightStoneBlockShape(Blocks.STONE.defaultBlockState()); + private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState()); private boolean isNoteBlock; public ObjectHolder behaviorHolder; public ObjectHolder shapeHolder; diff --git a/server-mod/src/main/java/net/momirealms/craftengine/mod/PaperWeightStoneBlockShape.java b/server-mod/src/main/java/net/momirealms/craftengine/mod/StoneBlockShape.java similarity index 81% rename from server-mod/src/main/java/net/momirealms/craftengine/mod/PaperWeightStoneBlockShape.java rename to server-mod/src/main/java/net/momirealms/craftengine/mod/StoneBlockShape.java index cc2615820..be7f4aa76 100644 --- a/server-mod/src/main/java/net/momirealms/craftengine/mod/PaperWeightStoneBlockShape.java +++ b/server-mod/src/main/java/net/momirealms/craftengine/mod/StoneBlockShape.java @@ -6,10 +6,10 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.shapes.CollisionContext; import net.momirealms.craftengine.shared.block.BlockShape; -public class PaperWeightStoneBlockShape implements BlockShape { +public class StoneBlockShape implements BlockShape { private final BlockState rawBlockState; - public PaperWeightStoneBlockShape(BlockState rawBlockState) { + public StoneBlockShape(BlockState rawBlockState) { this.rawBlockState = rawBlockState; } diff --git a/settings.gradle.kts b/settings.gradle.kts index 02db6f8bf..cd96a4442 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ include(":shared") include(":core") include(":bukkit") include(":bukkit:legacy") +include(":bukkit:compatibility") include(":bukkit-loader") include(":server-mod") pluginManagement {