diff --git a/README.md b/README.md index 10bf509de..836dcd8f0 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.61") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.61") + compileOnly("net.momirealms:craft-engine-core:0.0.63") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.63") } ``` \ No newline at end of file diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index c9bf92f91..ec25aa893 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -4,11 +4,11 @@ plugins { } repositories { + mavenCentral() maven("https://jitpack.io/") maven("https://repo.momirealms.net/releases/") maven("https://libraries.minecraft.net/") maven("https://repo.papermc.io/repository/maven-public/") - mavenCentral() } dependencies { @@ -60,6 +60,8 @@ dependencies { compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}") // authlib compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") + // concurrentutil + compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}") } java { @@ -99,6 +101,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 110a0b229..1ca637479 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -14,6 +14,7 @@ repositories { maven("https://nexus.neetgames.com/repository/maven-releases/") // mcmmo maven("https://repo.dmulloy2.net/repository/public/") // mcmmo required maven("https://repo.auxilor.io/repository/maven-public/") // eco + maven("https://repo.hiusers.com/releases") // zaphkiel } dependencies { @@ -67,6 +68,10 @@ dependencies { compileOnly("com.willfp:libreforge:4.58.1") // AureliumSkills compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") + // Zaphkiel + compileOnly("ink.ptms:ZaphkielAPI:2.1.0") + // WorldGuard + compileOnly(files("${rootProject.rootDir}/libs/worldguard-bukkit-7.0.14-dist.jar")) } java { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 5e752460f..3f6d91917 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -1,10 +1,7 @@ package net.momirealms.craftengine.bukkit.compatibility; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingSource; -import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsSource; -import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsSource; -import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsSource; +import net.momirealms.craftengine.bukkit.compatibility.item.*; import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.leveler.*; import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel; @@ -14,6 +11,7 @@ import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDrop import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; +import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition; import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook; import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils; @@ -23,9 +21,12 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.core.entity.furniture.ExternalModel; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.loot.LootConditions; import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager; import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider; import net.momirealms.craftengine.core.plugin.compatibility.ModelProvider; +import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldManager; @@ -120,6 +121,15 @@ public class BukkitCompatibilityManager implements CompatibilityManager { new MythicItemDropListener(this.plugin); logHook("MythicMobs"); } + Key worldGuardRegion = Key.of("worldguard:region"); + if (this.isPluginEnabled("WorldGuard")) { + EventConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>()); + LootConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>()); + logHook("WorldGuard"); + } else { + EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); + LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); + } } @Override @@ -249,6 +259,10 @@ public class BukkitCompatibilityManager implements CompatibilityManager { itemManager.registerExternalItemSource(new CustomFishingSource()); logHook("CustomFishing"); } + if (this.isPluginEnabled("Zaphkiel")) { + itemManager.registerExternalItemSource(new ZaphkielSource()); + logHook("Zaphkiel"); + } } private Plugin getPlugin(String name) { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/ZaphkielSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/ZaphkielSource.java new file mode 100644 index 000000000..3c9329b11 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/ZaphkielSource.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import ink.ptms.zaphkiel.Zaphkiel; +import net.momirealms.craftengine.core.item.ExternalItemSource; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; +import java.util.Optional; + +/** + * @author iiabc + * @since 2025/8/30 09:39 + */ +public class ZaphkielSource implements ExternalItemSource { + + @Override + public String plugin() { + return "zaphkiel"; + } + + @Override + public @Nullable ItemStack build(String id, ItemBuildContext context) { + Player player = Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null); + return Zaphkiel.INSTANCE.api().getItemManager().generateItemStack(id, player); + } + + @Override + public String id(ItemStack item) { + return Zaphkiel.INSTANCE.api().getItemHandler().getItemId(item); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java new file mode 100644 index 000000000..86eeac55a --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.bukkit.compatibility.region; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.World; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +public class WorldGuardRegionCondition implements Condition { + private static final Key TYPE = Key.of("worldguard:region"); + private final MatchMode mode; + private final List regions; + + public WorldGuardRegionCondition(MatchMode mode, List regions) { + this.mode = mode; + this.regions = regions; + } + + @Override + public boolean test(CTX ctx) { + if (this.regions.isEmpty()) return false; + Optional optionalPos = ctx.getOptionalParameter(DirectContextParameters.POSITION); + if (optionalPos.isEmpty()) { + return false; + } + WorldPosition position = optionalPos.get(); + RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt((World) position.world().platformWorld())); + if (regionManager != null) { + ApplicableRegionSet set = regionManager.getApplicableRegions(BlockVector3.at(position.x(), position.y(), position.z())); + List regionsAtThisPos = new ArrayList<>(set.size()); + for (ProtectedRegion region : set) { + String id = region.getId(); + regionsAtThisPos.add(id); + } + Predicate predicate = regionsAtThisPos::contains; + return this.mode.matcher.apply(predicate, this.regions); + } + return false; + } + + @Override + public Key type() { + return TYPE; + } + + public enum MatchMode { + ANY((p, regions) -> { + for (String region : regions) { + if (p.test(region)) { + return true; + } + } + return false; + }), + ALL((p, regions) -> { + for (String region : regions) { + if (!p.test(region)) { + return false; + } + } + return true; + }); + + private final BiFunction, List, Boolean> matcher; + + MatchMode(BiFunction, List, Boolean> matcher) { + this.matcher = matcher; + } + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + int mode = ResourceConfigUtils.getAsInt(arguments.getOrDefault("mode", 1), "mode") - 1; + MatchMode matchMode = MatchMode.values()[mode]; + List regions = MiscUtils.getAsStringList(arguments.get("regions")); + return new WorldGuardRegionCondition<>(matchMode, regions); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java index 5b702192b..be5b1f2ed 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java @@ -90,7 +90,7 @@ public class WorldEditBlockRegister { if (state == null) return null; try { - String id = state.customBlockState().handle().toString(); + String id = state.customBlockState().literalObject().toString(); int first = id.indexOf('{'); int last = id.indexOf('}'); if (first != -1 && last != -1 && last > first) { diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index 03b971562..c46bb9ff0 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -21,6 +21,9 @@ dependencies { implementation(project(":bukkit:compatibility:legacy")) implementation(project(":common-files")) + // concurrentutil + implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar")) + implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}") implementation("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}") @@ -47,7 +50,7 @@ bukkit { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") - contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8") + contributors = listOf("jhqwqmc", "iqtesterr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8") softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit", "Skript") foliaSupported = true } @@ -77,5 +80,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index a5a269d3b..94abf7189 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -23,6 +23,9 @@ dependencies { implementation(project(":bukkit:compatibility:legacy")) implementation(project(":common-files")) + // concurrentutil + implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar")) + implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}") implementation("net.momirealms:craft-engine-nms-helper-mojmap:${rootProject.properties["nms_helper_version"]}") @@ -82,6 +85,7 @@ paper { register("MMOItems") { required = false } register("MythicMobs") { required = false } register("CustomFishing") { required = false } + register("Zaphkiel") { required = false } // leveler register("AuraSkills") { required = false } @@ -149,5 +153,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java b/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java index aadff5866..05364cd61 100644 --- a/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java +++ b/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java @@ -5,11 +5,11 @@ import io.papermc.paper.plugin.bootstrap.PluginBootstrap; import io.papermc.paper.plugin.bootstrap.PluginProviderContext; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import net.momirealms.craftengine.bukkit.plugin.agent.RuntimePatcher; -import net.momirealms.craftengine.bukkit.plugin.classpath.PaperClassPathAppender; +import net.momirealms.craftengine.bukkit.plugin.classpath.BukkitClassPathAppender; +import net.momirealms.craftengine.bukkit.plugin.classpath.PaperPluginClassPathAppender; import net.momirealms.craftengine.core.plugin.logger.PluginLogger; import net.momirealms.craftengine.core.plugin.logger.Slf4jPluginLogger; import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -39,14 +39,24 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap { } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to getLogger", e); } - this.plugin = new BukkitCraftEngine( - logger, - context.getDataDirectory(), - new PaperClassPathAppender(this.getClass().getClassLoader()) - ); + try { + this.plugin = new BukkitCraftEngine( + logger, + context.getDataDirectory(), + new BukkitClassPathAppender(), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()) + ); + } catch (UnsupportedOperationException e) { + this.plugin = new BukkitCraftEngine( + logger, + context.getDataDirectory(), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()) + ); + } this.plugin.applyDependencies(); this.plugin.setUpConfig(); - if (VersionHelper.isOrAbove1_21_4()) { + if (isDatapackDiscoveryAvailable()) { new ModernEventHandler(context, this.plugin).register(); } else { try { @@ -58,6 +68,16 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap { } } + private static boolean isDatapackDiscoveryAvailable() { + try { + Class eventsClass = Class.forName("io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents"); + eventsClass.getField("DATAPACK_DISCOVERY"); + return true; + } catch (ClassNotFoundException | NoSuchFieldException e) { + return false; + } + } + @Override public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) { return new PaperCraftEnginePlugin(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java index 3986509cf..6dcd0167a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Location; @@ -29,8 +29,8 @@ public final class BukkitAdaptors { return new BukkitEntity(entity); } - public static BukkitBlockInWorld adapt(final Block block) { - return new BukkitBlockInWorld(block); + public static BukkitExistingBlock adapt(final Block block) { + return new BukkitExistingBlock(block); } public static Location toLocation(WorldPosition position) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index ffddee806..678f1b710 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -126,7 +126,7 @@ public final class CraftEngineBlocks { boolean success; Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()); Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - Object blockState = block.customBlockState().handle(); + Object blockState = block.customBlockState().literalObject(); Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos); success = FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState, option.flags()); if (success) { @@ -249,6 +249,6 @@ public final class CraftEngineBlocks { */ @NotNull public static BlockData getBukkitBlockData(@NotNull ImmutableBlockState blockState) { - return BlockStateUtils.fromBlockData(blockState.customBlockState().handle()); + return BlockStateUtils.fromBlockData(blockState.customBlockState().literalObject()); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java index 58d5dea02..949299b48 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java @@ -8,9 +8,17 @@ import org.jetbrains.annotations.NotNull; public class CraftEngineReloadEvent extends Event { private static final HandlerList HANDLER_LIST = new HandlerList(); private final BukkitCraftEngine plugin; + private static boolean firstFlag = true; + private final boolean isFirstReload; public CraftEngineReloadEvent(BukkitCraftEngine plugin) { this.plugin = plugin; + this.isFirstReload = firstFlag; + firstFlag = false; + } + + public boolean isFirstReload() { + return this.isFirstReload; } public BukkitCraftEngine plugin() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 7052fc484..1daab5e9d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -82,7 +82,7 @@ public final class BlockEventListener implements Listener { try { Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -99,7 +99,7 @@ public final class BlockEventListener implements Listener { Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -124,7 +124,7 @@ public final class BlockEventListener implements Listener { Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); optionalCustomItem.get().execute( PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.PLAYER, serverPlayer) .withParameter(DirectContextParameters.EVENT, cancellable) @@ -156,7 +156,7 @@ public final class BlockEventListener implements Listener { // execute functions Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state) .withParameter(DirectContextParameters.EVENT, cancellable) .withParameter(DirectContextParameters.POSITION, position) @@ -174,12 +174,15 @@ public final class BlockEventListener implements Listener { // override vanilla block loots if (player.getGameMode() != GameMode.CREATIVE) { this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> { + if (!event.isDropItems()) { + return; + } if (it.override()) { event.setDropItems(false); event.setExpToDrop(0); } ContextHolder lootContext = ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.PLAYER, serverPlayer) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, ItemUtils.isEmpty(itemInHand) ? null : itemInHand).build(); @@ -198,7 +201,7 @@ public final class BlockEventListener implements Listener { try { Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); Object breakSound = CoreReflections.field$SoundType$breakSound.get(soundType); - block.getWorld().playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + block.getWorld().playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -224,7 +227,7 @@ public final class BlockEventListener implements Listener { WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)); + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)); for (LootTable lootTable : it.lootTables()) { for (Item item : lootTable.getRandomItems(builder.build(), world, null)) { world.dropItemNaturally(position, item); @@ -250,7 +253,7 @@ public final class BlockEventListener implements Listener { state.owner().value().execute(PlayerOptionalContext.of(BukkitAdaptors.adapt(player), ContextHolder.builder() .withParameter(DirectContextParameters.EVENT, cancellable) .withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location))) - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state) ), EventTrigger.STEP); if (cancellable.isCancelled()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index d3c7709fd..f32cb3a5b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -181,14 +181,14 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Nullable @Override - public BlockStateWrapper createPackedBlockState(String blockState) { + public BlockStateWrapper createBlockState(String blockState) { ImmutableBlockState state = BlockStateParser.deserialize(blockState); if (state != null) { return state.customBlockState(); } try { BlockData blockData = Bukkit.createBlockData(blockState); - return BlockStateUtils.toPackedBlockState(blockData); + return BlockStateUtils.toBlockStateWrapper(blockData); } catch (IllegalArgumentException e) { return null; } @@ -231,7 +231,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Override public Key getBlockOwnerId(BlockStateWrapper state) { - return BlockStateUtils.getBlockOwnerIdFromState(state.handle()); + return BlockStateUtils.getBlockOwnerIdFromState(state.literalObject()); } @Override @@ -258,9 +258,9 @@ public final class BukkitBlockManager extends AbstractBlockManager { int size = RegistryUtils.currentBlockRegistrySize(); BlockStateWrapper[] states = new BlockStateWrapper[size]; for (int i = 0; i < size; i++) { - states[i] = BlockStateWrapper.create(BlockStateUtils.idToBlockState(i), i, BlockStateUtils.isVanillaBlock(i)); + states[i] = new BukkitBlockStateWrapper(BlockStateUtils.idToBlockState(i), i); } - BlockRegistryMirror.init(states, BlockStateWrapper.vanilla(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState))); + BlockRegistryMirror.init(states, new BukkitBlockStateWrapper(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState))); } private void registerEmptyBlock() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java new file mode 100644 index 000000000..cafb79317 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java @@ -0,0 +1,23 @@ +package net.momirealms.craftengine.bukkit.block; + +import net.momirealms.craftengine.core.block.BlockStateWrapper; + +public class BukkitBlockStateWrapper implements BlockStateWrapper { + private final Object blockState; + private final int registryId; + + public BukkitBlockStateWrapper(Object blockState, int registryId) { + this.blockState = blockState; + this.registryId = registryId; + } + + @Override + public Object literalObject() { + return this.blockState; + } + + @Override + public int registryId() { + return this.registryId; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 14ae935fc..84f1cbfe3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -82,7 +82,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { CraftEngine.instance().logger().warn("Could not find custom block immutableBlockState for " + immutableBlockState + ". This might cause errors!"); continue; } - DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().handle(); + DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().literalObject(); nmsState.setBlockState(immutableBlockState); BlockSettings settings = immutableBlockState.settings(); @@ -98,10 +98,10 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { Object pushReaction = ((Object[]) CoreReflections.method$PushReaction$values.invoke(null))[settings.pushReaction().ordinal()]; CoreReflections.field$BlockStateBase$pushReaction.set(nmsState, pushReaction); - boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(immutableBlockState.vanillaBlockState().handle()) : settings.canOcclude().asBoolean(); + boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(immutableBlockState.vanillaBlockState().literalObject()) : settings.canOcclude().asBoolean(); CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude); - boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(immutableBlockState.vanillaBlockState().handle()) : settings.useShapeForLightOcclusion().asBoolean(); + boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(immutableBlockState.vanillaBlockState().literalObject()) : settings.useShapeForLightOcclusion().asBoolean(); CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion); CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); @@ -111,7 +111,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { // set parent block properties DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState); ObjectHolder shapeHolder = nmsBlock.shapeDelegate(); - shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().handle(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> { + shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().literalObject(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> { try { Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(it)); if (!BlockStateUtils.isVanillaBlock(blockState)) { @@ -128,6 +128,9 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { behaviorHolder.bindValue(super.behavior); // set block side properties CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance()); + CoreReflections.field$BlockBehaviour$friction.set(nmsBlock, settings.friction()); + CoreReflections.field$BlockBehaviour$speedFactor.set(nmsBlock, settings.speedFactor()); + CoreReflections.field$BlockBehaviour$jumpFactor.set(nmsBlock, settings.jumpFactor()); CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds())); // init cache CoreReflections.method$BlockStateBase$initCache.invoke(nmsState); @@ -137,7 +140,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } // modify cache if (VersionHelper.isOrAbove1_21_2()) { - int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().handle()); + int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().literalObject()); // set block light CoreReflections.field$BlockStateBase$lightBlock.set(nmsState, blockLight); // set propagates skylight @@ -146,11 +149,11 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } else if (settings.propagatesSkylightDown() == Tristate.FALSE) { CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, false); } else { - CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(immutableBlockState.vanillaBlockState().handle())); + CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(immutableBlockState.vanillaBlockState().literalObject())); } } else { Object cache = CoreReflections.field$BlockStateBase$cache.get(nmsState); - int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().handle())); + int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject())); // set block light CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight); // set propagates skylight @@ -159,7 +162,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } else if (settings.propagatesSkylightDown() == Tristate.FALSE) { CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, false); } else { - CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().handle()))); + CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject()))); } if (!isConditionallyFullOpaque) { CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java index bd8b082c9..ea36861b8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java @@ -31,11 +31,11 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Direction.Axis axis = blockState.get(axisProperty); return switch (rotation) { case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> switch (axis) { - case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().handle(); - case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().handle(); - default -> blockState.customBlockState().handle(); + case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().literalObject(); + case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().literalObject(); + default -> blockState.customBlockState().literalObject(); }; - default -> blockState.customBlockState().handle(); + default -> blockState.customBlockState().literalObject(); }; }; }); @@ -45,7 +45,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection()) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection()); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -55,7 +55,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty))) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty)); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -68,7 +68,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection()) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection()); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -139,7 +139,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { if (optionalCustomState.isEmpty()) return CoreReflections.instance$ItemStack$EMPTY; ImmutableBlockState immutableBlockState = optionalCustomState.get(); if (immutableBlockState.get(this.waterloggedProperty)) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().literalObject(), 3); return FastNMS.INSTANCE.constructor$ItemStack(MItems.WATER_BUCKET, 1); } return CoreReflections.instance$ItemStack$EMPTY; @@ -154,7 +154,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { ImmutableBlockState immutableBlockState = optionalCustomState.get(); Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(args[3]); if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == MFluids.WATER) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3); + FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().literalObject(), 3); FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[0], args[1], fluidType, 5); return true; } @@ -182,6 +182,6 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { if (optionalCustomState.isEmpty()) return false; BlockStateWrapper vanillaState = optionalCustomState.get().vanillaBlockState(); if (vanillaState == null) return false; - return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.handle(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]); + return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.literalObject(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]); } } 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 e4b503afb..d7b4b9b0d 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 @@ -27,6 +27,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block"); public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block"); public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block"); + public static final Key CHANGE_OVER_TIME_BLOCK = Key.from("craftengine:change_over_time_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -52,5 +53,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY); register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY); register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY); + register(CHANGE_OVER_TIME_BLOCK, ChangeOverTimeBlockBehavior.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 d535f0030..f20eab56d 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 @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockTags; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -76,10 +77,7 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { @SuppressWarnings("DuplicatedCode") @Override protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception { - int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); - int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); - int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); - Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowPos = LocationUtils.below(blockPos); Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); return mayPlaceOn(belowState, world, belowPos); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java new file mode 100644 index 000000000..b6a40cf8f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.core.block.*; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final float changeSpeed; + private final Key nextBlock; + + public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, Key nextBlock) { + super(customBlock); + this.changeSpeed = changeSpeed; + this.nextBlock = nextBlock; + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws ReflectiveOperationException { + if (RandomUtils.generateRandomFloat(0F, 1F) >= this.changeSpeed) return; + Optional nextState = BukkitBlockManager.instance().blockById(this.nextBlock) + .map(CustomBlock::defaultState) + .map(ImmutableBlockState::customBlockState) + .map(BlockStateWrapper::literalObject); + if (nextState.isEmpty()) return; + CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.get(), UpdateOption.UPDATE_ALL.flags()); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + float changeSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("change-speed", 0.05688889F), "change-speed"); + Key nextBlock = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block")); + return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index f1dc2529e..aa872018e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock); if (optionalCustomBlock.isPresent()) { CustomBlock customBlock = optionalCustomBlock.get(); - this.defaultBlockState = customBlock.defaultState().customBlockState().handle(); + this.defaultBlockState = customBlock.defaultState().customBlockState().literalObject(); this.defaultImmutableBlockState = customBlock.defaultState(); } else { CraftEngine.instance().logger().warn("Failed to create solid block " + this.targetBlock + " in ConcretePowderBlockBehavior"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index a2b0b3c0f..7e0746ead 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -94,7 +94,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { BlockStateUtils.getOptionalCustomBlockState(state).ifPresent(customState -> { int age = this.getAge(customState); if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } }); } @@ -133,7 +133,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { if (isMaxAge(state)) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); @@ -156,7 +156,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState); @@ -180,7 +180,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { if (i > maxAge) { i = maxAge; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); if (sendParticles) { world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java index 768f01adf..864563451 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java @@ -107,7 +107,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { return MBlocks.AIR$defaultState; } if (neighborState.get(anotherDoorBehavior.get().halfProperty) != half) { - return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().handle(); + return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().literalObject(); } return MBlocks.AIR$defaultState; } else { @@ -245,7 +245,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { public void setOpen(@Nullable Player player, Object serverLevel, ImmutableBlockState state, BlockPos pos, boolean isOpen) { if (isOpen(state) != isOpen) { org.bukkit.World world = FastNMS.INSTANCE.method$Level$getCraftWorld(serverLevel); - FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().handle(), UpdateOption.builder().updateImmediate().updateClients().build().flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().literalObject(), UpdateOption.builder().updateImmediate().updateClients().build().flags()); world.sendGameEvent(player == null ? null : (org.bukkit.entity.Player) player.platformPlayer(), isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, new Vector(pos.x(), pos.y(), pos.z())); SoundData soundData = isOpen ? this.openSound : this.closeSound; if (soundData != null) { @@ -307,7 +307,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { ); } } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java index 016979338..14305b479 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java @@ -63,7 +63,7 @@ public class DoubleHighBlockBehavior extends BukkitBlockBehavior { World level = context.getLevel(); BlockPos anotherHalfPos = context.getClickedPos().relative(Direction.UP); BlockStateWrapper blockStateWrapper = state.with(this.halfProperty, DoubleBlockHalf.UPPER).customBlockState(); - FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java index 7040f8b58..99f897792 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java @@ -113,7 +113,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { if (relativeStateIsWall) { // TODO: 连接原版方块 } - return customState.with(this.inWallProperty, flag).customBlockState().handle(); + return customState.with(this.inWallProperty, flag).customBlockState().literalObject(); } @Override @@ -148,7 +148,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } @@ -223,7 +223,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) { @@ -240,7 +240,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { } newState = blockState.with(this.openProperty, true); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); boolean open = isOpen(newState); ((org.bukkit.World) world.platformWorld()).sendGameEvent( player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null, diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java index d4009daae..20833c67c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.FeatureUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.ParticleUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -59,7 +59,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { } boolean sendParticles = false; ImmutableBlockState customState = optionalCustomState.get(); - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -86,12 +86,12 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) return InteractionResult.PASS; BlockPos pos = context.getClickedPos(); - BukkitBlockInWorld upper = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z()); + BukkitExistingBlock upper = (BukkitExistingBlock) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z()); Block block = upper.block(); if (!block.isEmpty()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java index c6e645afd..594962aca 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; @@ -20,10 +21,7 @@ public class HangingBlockBehavior extends BushBlockBehavior { @Override protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { - int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); - int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); - int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); - Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y + 1, z); + Object belowPos = LocationUtils.above(blockPos); Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); return mayPlaceOn(belowState, world, belowPos); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java index 21398bb83..432837793 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java @@ -44,7 +44,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior { if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) { return; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2); } } @@ -64,7 +64,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior { if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) { return; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index 7db9e3abb..c863c19a6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -86,10 +86,10 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior { LeavesBlockBehavior behavior = optionalBehavior.get(); ImmutableBlockState newState = behavior.updateDistance(customState, level, blockPos); if (newState != customState) { - if (blockState == newState.customBlockState().handle()) { + if (blockState == newState.customBlockState().literalObject()) { CoreReflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512); } else { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java index 9f3e513c7..da6eb9d78 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java @@ -105,7 +105,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { if (signalForState == 0) { this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock); } else { - // todo 为什么 FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime); } } @@ -122,7 +121,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { private Object setSignalForState(Object state, int strength) { Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); if (optionalCustomState.isEmpty()) return state; - return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().handle(); + return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().literalObject(); } private void checkPressed(@Nullable Object entity, Object level, Object pos, Object state, int currentSignal, Object thisBlock) { @@ -189,7 +188,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { Object pos = args[2]; Object newState = args[3]; boolean movedByPiston = (boolean) args[4]; - if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$is(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) { + if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$isBlock(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) { if (this.getSignalForState(state) > 0) { this.updateNeighbours(level, pos, thisBlock); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 324c4752b..c56fa4050 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -114,7 +114,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -151,7 +151,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java index bb7552b74..6556345dc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java @@ -68,7 +68,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior { private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item item, Player player, InteractionHand hand) { ImmutableBlockState nextStage = state.cycle(this.amountProperty); Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z()); - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); if (this.stackSound != null) { world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java index 64fc2c627..2ad532eed 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java @@ -65,7 +65,7 @@ public class StairsBlockBehavior extends BukkitBlockBehavior { Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]); StairsShape stairsShape = getStairsShape(customState, level, LocationUtils.fromBlockPos(blockPos)); return direction.axis().isHorizontal() - ? customState.with(this.shapeProperty, stairsShape).customBlockState().handle() + ? customState.with(this.shapeProperty, stairsShape).customBlockState().literalObject() : superMethod.call(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java index 5f16405dd..71db218b5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java @@ -118,7 +118,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } @@ -195,7 +195,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); if (this.waterloggedProperty != null && customState.get(this.waterloggedProperty)) { FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(level, blockPos, MFluids.WATER, 5); } @@ -203,7 +203,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) { ImmutableBlockState newState = state.cycle(this.openProperty); - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); boolean open = newState.get(this.openProperty); ((org.bukkit.World) world.platformWorld()).sendGameEvent( player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null, diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java index 4542f71d2..81f40cbd8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java @@ -4,9 +4,11 @@ import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; @@ -31,6 +33,22 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return Optional.empty(); } + @Nullable + @Override + public EntityBlockBehavior getEntityBehavior() { + EntityBlockBehavior target = null; + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior instanceof EntityBlockBehavior entityBehavior) { + if (target == null) { + target = entityBehavior; + } else { + throw new IllegalArgumentException("Multiple entity block behaviors are not allowed"); + } + } + } + return target; + } + @Override public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { for (AbstractBlockBehavior behavior : this.behaviors) { @@ -74,6 +92,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return previous; } + + @Override + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + Object container = behavior.getContainer(thisBlock, args); + if (container != null) { + return container; + } + } + return null; + } + @Override public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { for (AbstractBlockBehavior behavior : this.behaviors) { @@ -264,6 +294,30 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return false; } + @Override + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior.hasAnalogOutputSignal(thisBlock, args)) { + return true; + } + } + return false; + } + + @Override + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + int signal = 0; + int count = 0; + for (AbstractBlockBehavior behavior : this.behaviors) { + int s = behavior.getAnalogOutputSignal(thisBlock, args); + if (s != 0) { + signal += s; + count++; + } + } + return count == 0 ? 0 : signal / count; + } + @Override public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Object previous = args[0]; @@ -282,4 +336,4 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { behavior.spawnAfterBreak(thisBlock, args, superMethod); } } -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java index b5d2d9c9a..8e416ac4f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java @@ -66,13 +66,13 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior { if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { Object nextPos = this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos); if (VersionHelper.isOrAbove1_21_5()) { - CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } else { - CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle()); + CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject()); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags()); } else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java index 2193b86f9..caf677a3a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.entity; -import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.util.Direction; @@ -68,7 +68,7 @@ public class BukkitEntity extends AbstractEntity { @Override public Key type() { - return KeyUtils.namespacedKey2Key(literalObject().getType().getKey()); + return EntityUtils.getEntityType(literalObject()); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java index aa98ccc73..7c6df9cdd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.item; -import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.CustomItem; @@ -11,8 +10,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; @@ -61,7 +63,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler> optionalCustomItem = wrapped.getCustomItem(); if (optionalCustomItem.isEmpty()) { if (!Config.interceptItem()) return Optional.empty(); - return new OtherItem(wrapped, false).process(); + return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); } else { BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); @@ -71,7 +73,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler item, BiConsumer callback) { + public static boolean processCustomName(Item item, BiConsumer callback, Context context) { Optional optionalCustomName = item.customNameJson(); if (optionalCustomName.isPresent()) { String line = optionalCustomName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); callback.accept("display.Name", NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -125,18 +127,18 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler item, BiConsumer callback) { + private static boolean processLore(Item item, BiConsumer callback, Context context) { Optional> optionalLore = item.loreJson(); if (optionalLore.isPresent()) { boolean changed = false; List lore = optionalLore.get(); List newLore = new ArrayList<>(lore.size()); for (String line : lore) { - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (tokens.isEmpty()) { newLore.add(line); } else { - newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); changed = true; } } @@ -164,11 +166,11 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler> process() { - if (processLore(this.item, (s, c) -> networkTag().put(s, c))) { + public Optional> process(Context context) { + if (processLore(this.item, (s, c) -> networkTag().put(s, c), context)) { this.globalChanged = true; } - if (processCustomName(this.item, (s, c) -> networkTag().put(s, c))) { + if (processCustomName(this.item, (s, c) -> networkTag().put(s, c), context)) { this.globalChanged = true; } if (this.globalChanged) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java index a1be6a2f5..41bad6664 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.item; -import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.*; @@ -8,8 +7,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; @@ -64,7 +66,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler> optionalCustomItem = wrapped.getCustomItem(); if (optionalCustomItem.isEmpty()) { if (!Config.interceptItem()) return Optional.empty(); - return new OtherItem(wrapped, false).process(); + return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); } else { BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); @@ -74,7 +76,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler tag); - else processLegacyItemName(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag, context); + else processLegacyItemName(wrapped, () -> tag, context); } if (!tag.containsKey(ComponentIds.CUSTOM_NAME)) { - if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag); - else processLegacyCustomName(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag, context); + else processLegacyCustomName(wrapped, () -> tag, context); } if (!tag.containsKey(ComponentIds.LORE)) { - if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag); - else processLegacyLore(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag, context); + else processLegacyLore(wrapped, () -> tag, context); } } if (tag.isEmpty()) { @@ -120,18 +122,18 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyLore(Item item, Supplier tag, Context context) { Optional> optionalLore = item.loreJson(); if (optionalLore.isPresent()) { boolean changed = false; List lore = optionalLore.get(); List newLore = new ArrayList<>(lore.size()); for (String line : lore) { - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (tokens.isEmpty()) { newLore.add(line); } else { - newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); changed = true; } } @@ -148,13 +150,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyCustomName(Item item, Supplier tag, Context context) { Optional optionalCustomName = item.customNameJson(); if (optionalCustomName.isPresent()) { String line = optionalCustomName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -162,13 +164,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyItemName(Item item, Supplier tag, Context context) { Optional optionalItemName = item.itemNameJson(); if (optionalItemName.isPresent()) { String line = optionalItemName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -176,33 +178,33 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processModernItemName(Item item, Supplier tag, Context context) { Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.ITEM_NAME); if (nameTag == null) return false; String tagStr = nameTag.getAsString(); - Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (!tokens.isEmpty()) { - item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens))); + item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); return true; } return false; } - public static boolean processModernCustomName(Item item, Supplier tag) { + public static boolean processModernCustomName(Item item, Supplier tag, Context context) { Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.CUSTOM_NAME); if (nameTag == null) return false; String tagStr = nameTag.getAsString(); - Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (!tokens.isEmpty()) { - item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens))); + item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); return true; } return false; } - public static boolean processModernLore(Item item, Supplier tagSupplier) { + public static boolean processModernLore(Item item, Supplier tagSupplier, Context context) { Tag loreTag = item.getSparrowNBTComponent(ComponentTypes.LORE); boolean changed = false; if (!(loreTag instanceof ListTag listTag)) { @@ -211,11 +213,11 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (tokens.isEmpty()) { newLore.add(tag); } else { - newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens))); + newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens, context))); changed = true; } } @@ -238,20 +240,20 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler> process() { + public Optional> process(Context context) { if (VersionHelper.isOrAbove1_21_5()) { - if (processModernLore(this.item, this::getOrCreateTag)) + if (processModernLore(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processModernCustomName(this.item, this::getOrCreateTag)) + if (processModernCustomName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processModernItemName(this.item, this::getOrCreateTag)) + if (processModernItemName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; } else { - if (processLegacyLore(this.item, this::getOrCreateTag)) + if (processLegacyLore(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processLegacyCustomName(this.item, this::getOrCreateTag)) + if (processLegacyCustomName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processLegacyItemName(this.item, this::getOrCreateTag)) + if (processLegacyItemName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; } if (this.globalChanged) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index 0b8574385..e928f9233 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; @@ -82,12 +82,12 @@ public class AxeItemBehavior extends ItemBehavior { CompoundTag compoundTag = customState.propertiesNbt(); ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); - BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos()); org.bukkit.entity.Player bukkitPlayer = null; if (player != null) { bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle())); + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().literalObject())); if (EventUtils.fireAndCheckCancel(event)) { return InteractionResult.FAIL; } @@ -98,7 +98,7 @@ public class AxeItemBehavior extends ItemBehavior { if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; BlockPos pos = context.getClickedPos(); context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); - FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); Material material = MaterialUtils.getMaterial(item.vanillaId()); if (bukkitPlayer != null) { @@ -106,7 +106,7 @@ public class AxeItemBehavior extends ItemBehavior { // resend swing if it's not interactable on client side if (!InteractUtils.isInteractable( - bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().handle()), + bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().literalObject()), context.getHitResult(), item ) || player.isSecondaryUseActive()) { player.swingHand(context.getHand()); 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 faa5afefc..b6164a8ef 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 @@ -9,7 +9,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; @@ -73,7 +73,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } if (!context.canPlace()) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } Player player = context.getPlayer(); @@ -111,7 +111,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { } else { ImmutableBlockState customState = optionalCustomState.get(); // custom block - if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) { + if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().literalObject() : againstBlockState)) { return InteractionResult.FAIL; } } @@ -157,7 +157,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5); Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(bukkitBlock)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(bukkitBlock)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.EVENT, dummy) .withParameter(DirectContextParameters.HAND, context.getHand()) @@ -196,7 +196,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { try { Player cePlayer = context.getPlayer(); Object player = cePlayer != null ? cePlayer.serverPlayer() : null; - Object blockState = state.customBlockState().handle(); + Object blockState = state.customBlockState().literalObject(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); Object voxelShape; if (VersionHelper.isOrAbove1_21_6()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java index 480b4db98..e4d92d241 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; @@ -38,7 +38,7 @@ public class CompostableItemBehavior extends ItemBehavior { @SuppressWarnings("UnstableApiUsage") @Override public InteractionResult useOnBlock(UseOnContext context) { - BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + BukkitExistingBlock block = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos()); BlockData blockData = block.block().getBlockData(); Object blockOwner = BlockStateUtils.getBlockOwner(BlockStateUtils.blockDataToBlockState(blockData)); if (blockOwner != MBlocks.COMPOSTER) return InteractionResult.PASS; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java index 5365f9692..2be578ff4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.InteractUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.Item; @@ -40,7 +40,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { if (player == null) return InteractionResult.PASS; BlockPos clickedPos = context.getClickedPos(); - BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos); + BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(clickedPos); Block block = clicked.block(); BlockPos firePos = clickedPos.relative(context.getClickedFace()); Direction direction = context.getHorizontalDirection(); @@ -77,10 +77,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { // 点击对象为自定义方块 ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); // 原版外观也可燃 - if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().handle())) { + if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().literalObject())) { return InteractionResult.PASS; } - BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); + BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()); // 点击的是方块上面,则只需要判断shift和可交互 if (direction == Direction.UP) { // 客户端层面必须可交互 @@ -95,7 +95,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } else { // 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音 BlockPos belowFirePos = firePos.relative(Direction.DOWN); - BukkitBlockInWorld belowFireBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(belowFirePos); + BukkitExistingBlock belowFireBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(belowFirePos); boolean belowCanBurn; try { Block belowBlock = belowFireBlock.block(); @@ -134,7 +134,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { for (Direction dir : Direction.values()) { if (dir == relativeDirection) continue; BlockPos relPos = firePos.relative(dir); - BukkitBlockInWorld nearByBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(relPos); + BukkitExistingBlock nearByBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(relPos); BlockData nearbyBlockData = nearByBlock.block().getBlockData(); Object nearbyBlockState = BlockStateUtils.blockDataToBlockState(nearbyBlockData); int stateID = BlockStateUtils.blockStateToId(nearbyBlockState); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java index 896c742af..051838931 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; @@ -45,11 +47,15 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { try { if (player == null) return InteractionResult.FAIL; Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY); - Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult); + Object blockPos = FastNMS.INSTANCE.field$BlockHitResul$blockPos(blockHitResult); BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); - Direction direction = Direction.values()[(int) CoreReflections.method$Direction$ordinal.invoke(CoreReflections.field$BlockHitResul$direction.get(blockHitResult))]; - boolean miss = CoreReflections.field$BlockHitResul$miss.getBoolean(blockHitResult); + Direction direction = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult)); + boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult); Vec3d hitPos = LocationUtils.fromVec(CoreReflections.field$HitResult$location.get(blockHitResult)); + Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world.serverWorld(), blockPos)); + if (fluidType != MFluids.WATER && fluidType != MFluids.LAVA) { + return InteractionResult.PASS; + } if (miss) { return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above))); } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 447500af0..2231d8908 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -424,6 +424,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory getEnchantment(ComponentItemWrapper item, Key key) { Object enchant = item.getComponentExact(ComponentTypes.ENCHANTMENTS); + if (enchant == null) return Optional.empty(); try { Map map = EnchantmentUtils.toMap(enchant); Integer level = map.get(key.toString()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index db9aff35a..ce1500a38 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.listener; import io.papermc.paper.event.block.CompostItemEvent; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.item.BukkitCustomItem; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -10,7 +11,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; @@ -96,9 +97,10 @@ public class ItemEventListener implements Listener { Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) - .withParameter(DirectContextParameters.EVENT, cancellable) - .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation())) .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.ENTITY, new BukkitEntity(entity)) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation())) ); CustomItem customItem = optionalCustomItem.get(); customItem.execute(context, EventTrigger.RIGHT_CLICK); @@ -162,7 +164,7 @@ public class ItemEventListener implements Listener { // fix client side issues if (action.isRightClick() && hitResult != null && - InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()), hitResult, itemInHand)) { + InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) { player.updateInventory(); //PlayerUtils.resendItemInHand(player); } @@ -171,7 +173,7 @@ public class ItemEventListener implements Listener { // run custom functions CustomBlock customBlock = immutableBlockState.owner().value(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withParameter(DirectContextParameters.HAND, hand) .withParameter(DirectContextParameters.EVENT, dummy) @@ -253,13 +255,13 @@ public class ItemEventListener implements Listener { if (immutableBlockState != null) { // client won't have sounds if the clientside block is interactable // so we should check and resend sounds on BlockPlaceEvent - BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); + BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()); if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) { if (!serverPlayer.isSecondaryUseActive()) { serverPlayer.setResendSound(); } } else { - if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().handle()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().handle())) { + if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().literalObject())) { serverPlayer.setResendSwing(); } } @@ -316,7 +318,7 @@ public class ItemEventListener implements Listener { if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) { Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) @@ -337,7 +339,7 @@ public class ItemEventListener implements Listener { if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) { Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index 7cba8d1bc..11cd5b5a1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -68,57 +68,35 @@ public class BukkitRecipeManager extends AbstractRecipeManager { return recipe; }; - static { - try { - Key dyeRecipeId = Key.from("armor_dye"); - MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); - MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); - Key repairRecipeId = Key.from("repair_item"); - MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); - MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); - Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); - MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); - MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); - } catch (ReflectiveOperationException e) { - throw new ReflectionInitException("Failed to inject special recipes", e); - } - } - private static final Map, Object>> ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER = Map.of( RecipeSerializers.SHAPED, recipe -> { CustomShapedRecipe shapedRecipe = (CustomShapedRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createShapedRecipe(shapedRecipe); - modifyShapedRecipeIngredients(shapedRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SHAPELESS, recipe -> { CustomShapelessRecipe shapelessRecipe = (CustomShapelessRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createShapelessRecipe(shapelessRecipe); - modifyShapelessRecipeIngredients(shapelessRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SMELTING, recipe -> { CustomSmeltingRecipe smeltingRecipe = (CustomSmeltingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createSmeltingRecipe(smeltingRecipe); - modifyCookingRecipeIngredient(smeltingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.BLASTING, recipe -> { CustomBlastingRecipe blastingRecipe = (CustomBlastingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createBlastingRecipe(blastingRecipe); - modifyCookingRecipeIngredient(blastingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SMOKING, recipe -> { CustomSmokingRecipe smokingRecipe = (CustomSmokingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createSmokingRecipe(smokingRecipe); - modifyCookingRecipeIngredient(smokingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.CAMPFIRE_COOKING, recipe -> { CustomCampfireRecipe campfireRecipe = (CustomCampfireRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createCampfireRecipe(campfireRecipe); - modifyCookingRecipeIngredient(campfireRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.STONECUTTING, recipe -> { @@ -217,7 +195,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } } - private static List getIngredientLooks(List holders) { + public static List getIngredientLooks(List holders) { List itemStacks = new ArrayList<>(); for (UniqueKey holder : holders) { Optional> buildableItem = BukkitItemManager.instance().getBuildableItem(holder.key()); @@ -229,6 +207,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { Item barrier = BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null); assert barrier != null; barrier.customNameJson(AdventureHelper.componentToJson(Component.text(holder.key().asString()).color(NamedTextColor.RED))); + itemStacks.add(barrier.getLiteralObject()); } } return itemStacks; @@ -481,6 +460,22 @@ public class BukkitRecipeManager extends AbstractRecipeManager { this.plugin.logger().warn("Failed to register recipe " + recipe.id().toString(), e); } } + + // 重新注入特殊配方 + try { + Key dyeRecipeId = Key.from("armor_dye"); + MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); + Key repairRecipeId = Key.from("repair_item"); + MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); + MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); + Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); + MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to inject special recipes", e); + } + try { // give flags back on 1.21.2+ if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java index b5c977d3b..6b7332486 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; public class CustomIngredientList extends ArrayList { private final Ingredient ingredient; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 5b6359d3f..78bf6203f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -261,6 +261,7 @@ public class RecipeEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onAnvilEvent(PrepareAnvilEvent event) { + if (event.getResult() == null) return; preProcess(event); processRepairable(event); processRename(event); @@ -284,6 +285,10 @@ public class RecipeEventListener implements Listener { } // 如果第二个物品是附魔书,那么忽略 if (wrappedSecond.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { + // 禁止不可附魔的物品被附魔书附魔 + if (firstCustom.isPresent() && !firstCustom.get().settings().canEnchant()) { + event.setResult(null); + } return; } @@ -313,12 +318,24 @@ public class RecipeEventListener implements Listener { return; } - // 如果禁止在铁砧使用两个相同物品修复 - firstCustom.ifPresent(it -> { - if (it.settings().canRepair() == Tristate.FALSE) { + + if (firstCustom.isPresent()) { + CustomItem firstCustomItem = firstCustom.get(); + if (firstCustomItem.settings().canRepair() == Tristate.FALSE) { event.setResult(null); + return; } - }); + + Item wrappedResult = BukkitItemManager.instance().wrap(event.getResult()); + if (!firstCustomItem.settings().canEnchant()) { + Object previousEnchantment = wrappedFirst.getExactComponent(ComponentTypes.ENCHANTMENTS); + if (previousEnchantment != null) { + wrappedResult.setExactComponent(ComponentTypes.ENCHANTMENTS, previousEnchantment); + } else { + wrappedResult.resetComponent(ComponentTypes.ENCHANTMENTS); + } + } + } } /* @@ -482,9 +499,6 @@ public class RecipeEventListener implements Listener { if (ItemStackUtils.isEmpty(first)) { return; } - if (event.getResult() == null) { - return; - } Item wrappedFirst = BukkitItemManager.instance().wrap(first); wrappedFirst.getCustomItem().ifPresent(item -> { if (!item.settings().renameable()) { @@ -599,6 +613,35 @@ public class RecipeEventListener implements Listener { if (input == null) return; Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + if (craftingTableRecipe.hasVisualResult()) { + inventory.setResult(craftingTableRecipe.assembleVisual(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + } else { + inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + } + } + + @EventHandler(ignoreCancelled = true) + public void onCraftingFinish(CraftItemEvent event) { + if (!Config.enableRecipeSystem()) return; + org.bukkit.inventory.Recipe recipe = event.getRecipe(); + if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; + Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + // 也许是其他插件注册的配方,直接无视 + if (optionalRecipe.isEmpty()) { + return; + } + CraftingInventory inventory = event.getInventory(); + if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe craftingTableRecipe)) { + return; + } + if (!craftingTableRecipe.hasVisualResult()) { + return; + } + CraftingInput input = getCraftingInput(inventory); + if (input == null) return; + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index 9dc95e83b..68205240c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -55,7 +55,6 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme HandlerList.unregisterAll(this); } - @SuppressWarnings("UnstableApiUsage") @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onEntityDeath(EntityDeathEvent event) { Entity entity = event.getEntity(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 0ce69f22c..5b2e36c6b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -45,6 +45,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { public void onPlayerJoin(PlayerJoinEvent event) { if (Config.sendPackOnJoin() && !VersionHelper.isOrAbove1_20_2()) { Player player = BukkitAdaptors.adapt(event.getPlayer()); + if (player == null) return; this.sendResourcePack(player); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index 00b940138..b191553b8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -72,18 +72,20 @@ public class BukkitCraftEngine extends CraftEngine { private final Path dataFolderPath; protected BukkitCraftEngine(JavaPlugin plugin) { - this(new JavaPluginLogger(plugin.getLogger()), plugin.getDataFolder().toPath().toAbsolutePath(), new ReflectionClassPathAppender(plugin.getClass().getClassLoader())); + this(new JavaPluginLogger(plugin.getLogger()), plugin.getDataFolder().toPath().toAbsolutePath(), + new ReflectionClassPathAppender(plugin.getClass().getClassLoader()), new ReflectionClassPathAppender(plugin.getClass().getClassLoader())); this.setJavaPlugin(plugin); } - protected BukkitCraftEngine(PluginLogger logger, Path dataFolderPath, ClassPathAppender classPathAppender) { + protected BukkitCraftEngine(PluginLogger logger, Path dataFolderPath, ClassPathAppender sharedClassPathAppender, ClassPathAppender privateClassPathAppender) { super((p) -> { CraftEngineReloadEvent event = new CraftEngineReloadEvent((BukkitCraftEngine) p); EventUtils.fireAndForget(event); }); instance = this; this.dataFolderPath = dataFolderPath; - super.classPathAppender = classPathAppender; + super.sharedClassPathAppender = sharedClassPathAppender; + super.privateClassPathAppender = privateClassPathAppender; super.logger = logger; super.platform = new BukkitPlatform(); super.scheduler = new BukkitSchedulerAdapter(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java index cac4429ba..9b8f17333 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java @@ -34,7 +34,7 @@ public final class BlocksAgent { Method injectRegistries = plugin.getClass().getMethod("injectRegistries"); injectRegistries.invoke(plugin); } catch (Exception e) { - throw new RuntimeException("Failed to inject registries", e); + e.printStackTrace(); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java new file mode 100644 index 000000000..804947546 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java @@ -0,0 +1,37 @@ +package net.momirealms.craftengine.bukkit.plugin.classpath; + +import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; +import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; +import org.bukkit.Bukkit; + +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; + +public class BukkitClassPathAppender implements ClassPathAppender { + private final URLClassLoaderAccess libraryClassLoaderAccess; + + public BukkitClassPathAppender() { + // 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到 + ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader(); + if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + } else { + // ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader + if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + } else { + throw new UnsupportedOperationException("Unsupported classloader " + bukkitClassLoader.getClass()); + } + } + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java deleted file mode 100644 index 885b223cd..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java +++ /dev/null @@ -1,57 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.classpath; - -import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; -import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import org.bukkit.Bukkit; - -import java.lang.reflect.Field; -import java.net.MalformedURLException; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.Optional; - -public class PaperClassPathAppender implements ClassPathAppender { - public static final Class clazz$PaperPluginClassLoader = ReflectionUtils.getClazz( - "io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader" - ); - public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) - .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) - .orElse(null); - private final URLClassLoaderAccess libraryClassLoaderAccess; - - // todo 是否有更好的方法让库被其他插件共享 - public PaperClassPathAppender(ClassLoader classLoader) { - // 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到 - ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader(); - if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) { - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); - } else { - // ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader - if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) { - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); - return; - } - try { - // 最次的方案,使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离 - if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { - URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); - } else { - throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); - } - } - } - - @Override - public void addJarToClasspath(Path file) { - try { - this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java new file mode 100644 index 000000000..aa9bcb53a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java @@ -0,0 +1,44 @@ +package net.momirealms.craftengine.bukkit.plugin.classpath; + +import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; +import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; +import net.momirealms.craftengine.core.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Optional; + +public class PaperPluginClassPathAppender implements ClassPathAppender { + public static final Class clazz$PaperPluginClassLoader = ReflectionUtils.getClazz( + "io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader" + ); + public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) + .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) + .orElse(null); + private final URLClassLoaderAccess libraryClassLoaderAccess; + + public PaperPluginClassPathAppender(ClassLoader classLoader) { + try { + // 使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离 + if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { + URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); + } else { + throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); + } + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java index 03e90d034..f08c257c0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java @@ -38,7 +38,7 @@ public class DebugGetBlockInternalIdCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item == null) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java index 9425e7a3c..9cd21ae36 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.Key; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,7 +31,7 @@ public class SearchUsagePlayerCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item.isEmpty()) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index e5f42e169..437855e70 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,15 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import org.bukkit.Location; -import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.incendo.cloud.Command; -import org.incendo.cloud.parser.standard.IntegerParser; public class TestCommand extends BukkitCommandFeature { @@ -20,22 +15,9 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .required("start", IntegerParser.integerParser(0)) - .senderType(Player.class) .handler(context -> { - Player sender = context.sender(); - int start = context.get("start"); - int x = sender.getChunk().getX() * 16; - int z = sender.getChunk().getZ() * 16; - int y = (sender.getLocation().getBlockY() / 16) * 16; - for (int a = 0; a < 16; a++) { - for (int b = 0; b < 16; b++) { - for (int c = 0; c < 16; c++) { - BlockData blockData = BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(start + a + b * 16 + c * 256)); - sender.getWorld().setBlockData(new Location(sender.getWorld(), x + a, y + b, z + c), blockData); - } - } - } + // DO NOT PUSH ANY CODE FOR TEST COMMAND + // 禁止推送含有实现的Test指令 }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java index 83b94ad9f..85ba77e5b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java @@ -15,6 +15,7 @@ import net.bytebuddy.implementation.bind.annotation.This; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.block.BukkitBlockShape; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator.*; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils; @@ -34,7 +35,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.util.concurrent.Callable; -import java.util.function.BiConsumer; import java.util.function.Function; public final class BlockGenerator { @@ -63,6 +63,7 @@ public final class BlockGenerator { .implement(CoreReflections.clazz$Fallable) .implement(CoreReflections.clazz$BonemealableBlock) .implement(CoreReflections.clazz$SimpleWaterloggedBlock) + .implement(CoreReflections.clazz$WorldlyContainerHolder) // internal interfaces .method(ElementMatchers.named("behaviorDelegate")) .intercept(FieldAccessor.ofField("behaviorHolder")) @@ -90,12 +91,21 @@ public final class BlockGenerator { // rotate .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$rotate)) .intercept(MethodDelegation.to(RotateInterceptor.INSTANCE)) + // hasAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$hasAnalogOutputSignal)) + .intercept(MethodDelegation.to(HasAnalogOutputSignalInterceptor.INSTANCE)) + // getAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getAnalogOutputSignal)) + .intercept(MethodDelegation.to(GetAnalogOutputSignalInterceptor.INSTANCE)) // tick .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$tick)) .intercept(MethodDelegation.to(TickInterceptor.INSTANCE)) // isValidBoneMealTarget .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isValidBonemealTarget)) .intercept(MethodDelegation.to(IsValidBoneMealTargetInterceptor.INSTANCE)) + // getContainer + .method(ElementMatchers.is(CoreReflections.method$WorldlyContainerHolder$getContainer)) + .intercept(MethodDelegation.to(GetContainerInterceptor.INSTANCE)) // isBoneMealSuccess .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isBonemealSuccess)) .intercept(MethodDelegation.to(IsBoneMealSuccessInterceptor.INSTANCE)) @@ -106,54 +116,21 @@ public final class BlockGenerator { .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$randomTick)) .intercept(MethodDelegation.to(RandomTickInterceptor.INSTANCE)) // onPlace - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, boolean.class)) - .and(ElementMatchers.named("onPlace").or(ElementMatchers.named("a"))) - ) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onPlace)) .intercept(MethodDelegation.to(OnPlaceInterceptor.INSTANCE)) // onBrokenAfterFall - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onBrokenAfterFall)) .intercept(MethodDelegation.to(OnBrokenAfterFallInterceptor.INSTANCE)) // onLand - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onLand)) .intercept(MethodDelegation.to(OnLandInterceptor.INSTANCE)) // canSurvive - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$canSurvive) ) .intercept(MethodDelegation.to(CanSurviveInterceptor.INSTANCE)) // updateShape - .method(ElementMatchers.returns(CoreReflections.clazz$BlockState) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - // LevelReader 1.21.3+ // 1.20-1.12.2 - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader).or(ElementMatchers.takesArgument(1, CoreReflections.clazz$Direction))) - .and(ElementMatchers.named("updateShape").or(ElementMatchers.named("a")))) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$updateShape)) .intercept(MethodDelegation.to(UpdateShapeInterceptor.INSTANCE)) - // onExplosionHit 1.21+ - .method(ElementMatchers.returns(void.class) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, VersionHelper.isOrAbove1_21_2() ? CoreReflections.clazz$ServerLevel : CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$Explosion)) - .and(ElementMatchers.takesArgument(4, BiConsumer.class)) - ) - .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)) // neighborChanged .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged)) .intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE)) @@ -184,15 +161,21 @@ public final class BlockGenerator { // spawnAfterBreak .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$spawnAfterBreak)) .intercept(MethodDelegation.to(SpawnAfterBreakInterceptor.INSTANCE)); + // 1.21.5+ if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) .intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE)); } + // 1.20-1.21.4 if (CoreReflections.method$BlockBehaviour$onRemove != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) .intercept(MethodDelegation.to(OnRemoveInterceptor.INSTANCE)); } - + // 1.21+ + if (CoreReflections.method$BlockBehaviour$onExplosionHit != null) { + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onExplosionHit)) + .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)); + } Class clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded(); constructor$CraftEngineBlock = MethodHandles.publicLookup().in(clazz$CraftEngineBlock) .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$BlockBehaviour$Properties)) @@ -469,6 +452,51 @@ public final class BlockGenerator { } } + public static class GetContainerInterceptor { + public static final GetContainerInterceptor INSTANCE = new GetContainerInterceptor(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getContainer(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getContainer", e); + return null; + } + } + } + + public static class HasAnalogOutputSignalInterceptor { + public static final HasAnalogOutputSignalInterceptor INSTANCE = new HasAnalogOutputSignalInterceptor(); + + @RuntimeType + public boolean intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().hasAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run hasAnalogOutputSignal", e); + return false; + } + } + } + + public static class GetAnalogOutputSignalInterceptor { + public static final GetAnalogOutputSignalInterceptor INSTANCE = new GetAnalogOutputSignalInterceptor(); + + @RuntimeType + public int intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getAnalogOutputSignal", e); + return 0; + } + } + } + public static class PerformBoneMealInterceptor { public static final PerformBoneMealInterceptor INSTANCE = new PerformBoneMealInterceptor(); @@ -672,4 +700,4 @@ public final class BlockGenerator { } } } -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java index 33367919e..825f90a50 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java @@ -109,7 +109,7 @@ public final class BlockStateGenerator { if (optionalPlayer != null && settings.requireCorrectTool()) { if (item.isEmpty()) return List.of(); if (!settings.isCorrectTool(item.id()) && - (!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().handle()))) { + (!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().literalObject()))) { return List.of(); } } @@ -121,8 +121,10 @@ public final class BlockStateGenerator { if (!item.isEmpty()) { lootBuilder.withParameter(DirectContextParameters.ITEM_IN_HAND, item); } - BukkitServerPlayer player = optionalPlayer != null ? BukkitCraftEngine.instance().adapt(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(optionalPlayer)) : null; + if (player != null) { + lootBuilder.withParameter(DirectContextParameters.PLAYER, player); + } Float radius = (Float) FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.EXPLOSION_RADIUS); if (radius != null) { lootBuilder.withParameter(DirectContextParameters.EXPLOSION_RADIUS, radius); @@ -177,7 +179,7 @@ public final class BlockStateGenerator { if (state == null) return thisObj; Property waterloggedProperty = (Property) state.owner().value().getProperty("waterlogged"); if (waterloggedProperty == null) return thisObj; - return state.with(waterloggedProperty, (boolean) args[1]).customBlockState().handle(); + return state.with(waterloggedProperty, (boolean) args[1]).customBlockState().literalObject(); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java index e2f1f4267..7002c9084 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java @@ -10,6 +10,8 @@ import java.util.Set; public final class LootEntryInjector { + private LootEntryInjector() {} + public static void init() throws ReflectiveOperationException { Object registry = MBuiltInRegistries.LOOT_POOL_ENTRY_TYPE; CoreReflections.field$MappedRegistry$frozen.set(registry, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java index 1fdc089d0..81fdda00b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java @@ -19,10 +19,12 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.SectionPosUtils; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.SectionPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -33,6 +35,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; import java.util.function.Consumer; @@ -214,52 +217,90 @@ public final class WorldStorageInjector { } } + @SuppressWarnings("DuplicatedCode") private static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { - try { - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState); - CESection section = holder.ceSection(); - // 如果是原版方块 - if (optionalCustomState.isEmpty()) { - // 那么应该清空自定义块 - ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); - // 处理 自定义块 -> 原版块 - if (!previous.isEmpty()) { - holder.ceChunk().setDirty(true); - if (Config.enableLightSystem()) { - // 自定义块到原版块,只需要判断旧块是否和客户端一直 - BlockStateWrapper wrapper = previous.vanillaBlockState(); - if (wrapper != null) { - updateLight(holder, wrapper.handle(), previousState, x, y, z); - } + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState); + CESection section = holder.ceSection(); + // 如果是原版方块 + if (optionalCustomState.isEmpty()) { + // 那么应该清空自定义块 + ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); + // 处理 自定义块 -> 原版块 + if (!previous.isEmpty()) { + CEChunk chunk = holder.ceChunk(); + chunk.setDirty(true); + if (previous.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null) { + blockEntity.preRemove(); + chunk.removeBlockEntity(pos); } } - } else { - ImmutableBlockState immutableBlockState = optionalCustomState.get(); - ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); - if (previousImmutableBlockState == immutableBlockState) return; - // 处理 自定义块到自定义块或原版块到自定义块 - holder.ceChunk().setDirty(true); - // 不可能!绝对不可能! - if (immutableBlockState.isEmpty()) return; - // 如果新方块的光照属性和客户端认为的不同 if (Config.enableLightSystem()) { - if (previousImmutableBlockState.isEmpty()) { - // 原版块到自定义块,只需要判断新块是否和客户端视觉一致 - updateLight(holder, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z); - } else { - // 自定义块到自定义块 - updateLight$complex(holder, immutableBlockState.vanillaBlockState().handle(), newState, previousState, x, y, z); + // 自定义块到原版块,只需要判断旧块是否和客户端一直 + BlockStateWrapper wrapper = previous.vanillaBlockState(); + if (wrapper != null) { + updateLight(holder, wrapper.literalObject(), previousState, x, y, z); } } } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to intercept setBlockState", e); + } else { + ImmutableBlockState newImmutableBlockState = optionalCustomState.get(); + ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, newImmutableBlockState); + if (previousImmutableBlockState == newImmutableBlockState) return; + // 处理 自定义块到自定义块或原版块到自定义块 + CEChunk chunk = holder.ceChunk(); + chunk.setDirty(true); + // 如果两个方块没有相同的主人 且 旧方块有方块实体 + if (!previousImmutableBlockState.isEmpty()) { + if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null) { + try { + blockEntity.preRemove(); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Error removing block entity " + blockEntity.getClass().getName(), t); + } + chunk.removeBlockEntity(pos); + } + } + } + if (newImmutableBlockState.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null && !blockEntity.isValidBlockState(newImmutableBlockState)) { + chunk.removeBlockEntity(pos); + blockEntity = null; + } + if (blockEntity == null) { + blockEntity = Objects.requireNonNull(newImmutableBlockState.behavior().getEntityBehavior()).createBlockEntity(pos, newImmutableBlockState); + if (blockEntity != null) { + chunk.addBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(newImmutableBlockState); + // 方块类型未变,仅更新状态,选择性更新ticker + chunk.replaceOrCreateTickingBlockEntity(blockEntity); + } + } + // 如果新方块的光照属性和客户端认为的不同 + if (Config.enableLightSystem()) { + if (previousImmutableBlockState.isEmpty()) { + // 原版块到自定义块,只需要判断新块是否和客户端视觉一致 + updateLight(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, x, y, z); + } else { + // 自定义块到自定义块 + updateLight$complex(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, previousState, x, y, z); + } + } } } @SuppressWarnings("DuplicatedCode") - protected static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) { - CEWorld world = thisObj.ceChunk().world(); + private static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) { + CEWorld world = thisObj.ceChunk().world; Object blockPos = LocationUtils.toBlockPos(x, y, z); Object serverWorld = world.world().serverWorld(); if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState, serverWorld, blockPos)) { @@ -270,8 +311,8 @@ public final class WorldStorageInjector { } @SuppressWarnings("DuplicatedCode") - protected static void updateLight$complex(@This InjectedHolder thisObj, Object newClientState, Object newServerState, Object oldServerState, int x, int y, int z) { - CEWorld world = thisObj.ceChunk().world(); + private static void updateLight$complex(@This InjectedHolder thisObj, Object newClientState, Object newServerState, Object oldServerState, int x, int y, int z) { + CEWorld world = thisObj.ceChunk().world; Object blockPos = LocationUtils.toBlockPos(x, y, z); Object serverWorld = world.world().serverWorld(); // 如果客户端新状态和服务端新状态光照属性不同 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 e98de26d8..f97d36b3e 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 @@ -11,8 +11,9 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5; +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; +import net.momirealms.craftengine.bukkit.plugin.reflection.leaves.LeavesReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LeavesReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; @@ -105,6 +106,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.packetIds = VersionHelper.isOrAbove1_20_5() ? new PacketIds1_20_5() : new PacketIds1_20(); // register packet handlers this.registerPacketHandlers(); + PayloadHelper.registerDataTypes(); // set up packet senders this.packetConsumer = FastNMS.INSTANCE::method$Connection$send; this.packetsConsumer = ((connection, packets, sendListener) -> { @@ -202,7 +204,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, NetworkReflections.clazz$ServerboundRenameItemPacket); registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, NetworkReflections.clazz$ServerboundSignUpdatePacket); registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, NetworkReflections.clazz$ServerboundEditBookPacket); - registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, NetworkReflections.clazz$ServerboundCustomPayloadPacket); + registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD_1_20_2, VersionHelper.isOrAbove1_20_2() ? NetworkReflections.clazz$ServerboundCustomPayloadPacket : null); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, NetworkReflections.clazz$ServerboundResourcePackPacket); registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket); registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); @@ -213,6 +215,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.LOGIN_FINISHED, NetworkReflections.clazz$ClientboundLoginFinishedPacket); registerNMSPacketConsumer(PacketConsumers.UPDATE_TAGS, NetworkReflections.clazz$ClientboundUpdateTagsPacket); registerNMSPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_21_5, VersionHelper.isOrAbove1_21_5() ? NetworkReflections.clazz$ServerboundContainerClickPacket : null); + registerS2CByteBufPacketConsumer(PacketConsumers.FORGET_LEVEL_CHUNK, this.packetIds.clientboundForgetLevelChunkPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); @@ -244,6 +247,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerC2SByteBufPacketConsumer(PacketConsumers.SET_CREATIVE_MODE_SLOT, this.packetIds.serverboundSetCreativeModeSlotPacket()); registerC2SByteBufPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_20, VersionHelper.isOrAbove1_21_5() ? -1 : this.packetIds.serverboundContainerClickPacket()); registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket()); + registerC2SByteBufPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD_1_20, VersionHelper.isOrAbove1_20_2() ? -1 : this.packetIds.serverboundCustomPayloadPacket()); } @EventHandler(priority = EventPriority.LOWEST) 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 2fc91c8af..740564e0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -11,7 +11,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TranslationArgument; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; @@ -29,8 +28,8 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; import net.momirealms.craftengine.bukkit.plugin.network.handler.*; import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; -import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes; import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload; +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; @@ -56,16 +55,16 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.network.*; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.BlockHitResult; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import net.momirealms.craftengine.core.world.chunk.Palette; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; @@ -73,6 +72,7 @@ import net.momirealms.craftengine.core.world.chunk.packet.MCSection; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.*; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -140,13 +140,13 @@ public class PacketConsumers { ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.ENDERMAN$registryId] = simpleAddEntityHandler(EndermanPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.CHEST_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.CHEST_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(true); ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(true); @@ -235,7 +235,7 @@ public class PacketConsumers { int[] newMappingsMOD = Arrays.copyOf(newMappings, registrySize); for (Map.Entry entry : map.entrySet()) { newMappings[entry.getKey()] = entry.getValue(); - if (BlockStateUtils.isVanillaBlock(entry.getKey())) { + if (BlockStateUtils.isVanillaBlock((int) entry.getKey())) { newMappingsMOD[entry.getKey()] = entry.getValue(); } } @@ -259,6 +259,22 @@ public class PacketConsumers { return MOD_BLOCK_STATE_MAPPINGS[stateId]; } + public static final BiConsumer FORGET_LEVEL_CHUNK = (user, event) -> { + try { + FriendlyByteBuf buf = event.getBuffer(); + if (VersionHelper.isOrAbove1_20_2()) { + long chunkPos = buf.readLong(); + user.removeTrackedChunk(chunkPos); + } else { + int x = buf.readInt(); + int y = buf.readInt(); + user.removeTrackedChunk(ChunkPos.asLong(x, y)); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e); + } + }; + public static final BiConsumer LEVEL_CHUNK_WITH_LIGHT = (user, event) -> { try { BukkitServerPlayer player = (BukkitServerPlayer) user; @@ -308,7 +324,7 @@ public class PacketConsumers { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { - MCSection mcSection = new MCSection(SERVER_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST); + MCSection mcSection = new MCSection(user.clientBlockList(), SERVER_BLOCK_LIST, BIOME_LIST); mcSection.readPacket(friendlyByteBuf); PalettedContainer container = mcSection.blockStateContainer(); Palette palette = container.data().palette(); @@ -331,7 +347,7 @@ public class PacketConsumers { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { - MCSection mcSection = new MCSection(CLIENT_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST); + MCSection mcSection = new MCSection(user.clientBlockList(), SERVER_BLOCK_LIST, BIOME_LIST); mcSection.readPacket(friendlyByteBuf); PalettedContainer container = mcSection.blockStateContainer(); Palette palette = container.data().palette(); @@ -350,6 +366,9 @@ public class PacketConsumers { } buffer = newBuf.array(); } + + // 开始修改 + event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeInt(chunkX); @@ -378,7 +397,10 @@ public class PacketConsumers { buf.writeBitSet(emptyBlockYMask); buf.writeByteArrayList(skyUpdates); buf.writeByteArrayList(blockUpdates); - event.setChanged(true); + + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + // 记录加载的区块 + player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); } @@ -493,24 +515,24 @@ public class PacketConsumers { if (prefix == null) return; Tag suffix = buf.readNbt(false); if (suffix == null) return; - - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix.getAsString()); - Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix.getAsString()); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix.getAsString()); + Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix.getAsString()); if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); List entities = method == 0 ? buf.readStringList() : null; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(name); buf.writeByte(method); - buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1)), false); + buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, context)), false); buf.writeByte(friendlyFlags); eitherVisibility.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); eitherCollisionRule.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); buf.writeVarInt(color); - buf.writeNbt(tokens2.isEmpty() ? prefix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(prefix), tokens2)), false); - buf.writeNbt(tokens3.isEmpty() ? suffix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(suffix), tokens3)), false); + buf.writeNbt(tokens2.isEmpty() ? prefix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(prefix), tokens2, context)), false); + buf.writeNbt(tokens3.isEmpty() ? suffix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(suffix), tokens3, context)), false); if (entities != null) { buf.writeStringList(entities); } @@ -544,11 +566,12 @@ public class PacketConsumers { newEntries.add(entry); } else { String json = ComponentUtils.minecraftToJson(mcComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) { newEntries.add(entry); } else { - Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, ComponentUtils.adventureToMinecraft(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, + ComponentUtils.adventureToMinecraft(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); newEntries.add(newEntry); isChanged = true; } @@ -578,24 +601,25 @@ public class PacketConsumers { String prefix = buf.readUtf(); String suffix = buf.readUtf(); - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix); - Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix); + Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix); if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; event.setChanged(true); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); List entities = method == 0 ? buf.readStringList() : null; buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(name); buf.writeByte(method); - buf.writeUtf(tokens1.isEmpty() ? displayName : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens1))); + buf.writeUtf(tokens1.isEmpty() ? displayName : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens1, context))); buf.writeByte(friendlyFlags); buf.writeUtf(nameTagVisibility); buf.writeUtf(collisionRule); buf.writeVarInt(color); - buf.writeUtf(tokens2.isEmpty() ? prefix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(prefix), tokens2))); - buf.writeUtf(tokens3.isEmpty() ? suffix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(suffix), tokens3))); + buf.writeUtf(tokens2.isEmpty() ? prefix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(prefix), tokens2, context))); + buf.writeUtf(tokens3.isEmpty() ? suffix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(suffix), tokens3, context))); if (entities != null) { buf.writeStringList(entities); } @@ -612,7 +636,7 @@ public class PacketConsumers { int actionType = buf.readVarInt(); if (actionType == 0) { String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; float health = buf.readFloat(); int color = buf.readVarInt(); @@ -623,21 +647,21 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); buf.writeFloat(health); buf.writeVarInt(color); buf.writeVarInt(division); buf.writeByte(flag); } else if (actionType == 3) { String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundBossEventPacket", e); @@ -653,7 +677,7 @@ public class PacketConsumers { if (actionType == 0) { Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; float health = buf.readFloat(); int color = buf.readVarInt(); @@ -664,7 +688,7 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeFloat(health); buf.writeVarInt(color); buf.writeVarInt(division); @@ -672,14 +696,14 @@ public class PacketConsumers { } else if (actionType == 3) { Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundBossEventPacket", e); @@ -695,14 +719,14 @@ public class PacketConsumers { if (mode != 0 && mode != 2) return; String displayName = buf.readUtf(); int renderType = buf.readVarInt(); - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); buf.writeVarInt(renderType); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetObjectivePacket", e); @@ -723,19 +747,19 @@ public class PacketConsumers { if (optionalNumberFormat) { int format = buf.readVarInt(); if (format == 0) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(true); buf.writeVarInt(0); } else if (format == 1) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) return; Tag style = buf.readNbt(false); event.setChanged(true); @@ -743,7 +767,7 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(true); buf.writeVarInt(1); @@ -751,29 +775,29 @@ public class PacketConsumers { } else if (format == 2) { Tag fixed = buf.readNbt(false); if (fixed == null) return; - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); if (tokens1.isEmpty() && tokens2.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1)), false); + buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(true); buf.writeVarInt(2); - buf.writeNbt(tokens2.isEmpty() ? fixed : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(fixed), tokens2)), false); + buf.writeNbt(tokens2.isEmpty() ? fixed : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(fixed), tokens2, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } } else { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(false); } @@ -787,13 +811,13 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String jsonOrPlainString = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(jsonOrPlainString); + Map tokens = CraftEngine.instance().fontManager().matchTags(jsonOrPlainString); if (tokens.isEmpty()) return; boolean overlay = buf.readBoolean(); event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(jsonOrPlainString), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(jsonOrPlainString), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); buf.writeBoolean(overlay); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSystemChatPacket", e); @@ -806,13 +830,13 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; boolean overlay = buf.readBoolean(); event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeBoolean(overlay); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSystemChatPacket", e); @@ -824,12 +848,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetSubtitleTextPacket", e); } @@ -841,12 +865,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetSubtitleTextPacket", e); } @@ -857,12 +881,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetTitleTextPacket", e); } @@ -874,12 +898,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetTitleTextPacket", e); } @@ -890,12 +914,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetActionBarTextPacket", e); } @@ -907,12 +931,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetActionBarTextPacket", e); } @@ -924,14 +948,15 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); String json1 = buf.readUtf(); String json2 = buf.readUtf(); - Map tokens1 = CraftEngine.instance().fontManager().matchTags(json1); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(json2); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(json1); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(json2); if (tokens1.isEmpty() && tokens2.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeUtf(tokens1.isEmpty() ? json1 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json1), tokens1))); - buf.writeUtf(tokens2.isEmpty() ? json2 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json2), tokens2))); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + buf.writeUtf(tokens1.isEmpty() ? json1 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json1), tokens1, context))); + buf.writeUtf(tokens2.isEmpty() ? json2 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json2), tokens2, context))); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundTabListPacket", e); } @@ -945,14 +970,15 @@ public class PacketConsumers { if (nbt1 == null) return; Tag nbt2 = buf.readNbt(false); if (nbt2 == null) return; - Map tokens1 = CraftEngine.instance().fontManager().matchTags(nbt1.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(nbt2.getAsString()); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(nbt1.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(nbt2.getAsString()); if (tokens1.isEmpty() && tokens2.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeNbt(tokens1.isEmpty() ? nbt1 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt1), tokens1)), false); - buf.writeNbt(tokens2.isEmpty() ? nbt2 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt2), tokens2)), false); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + buf.writeNbt(tokens1.isEmpty() ? nbt1 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt1), tokens1, context)), false); + buf.writeNbt(tokens2.isEmpty() ? nbt2 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt2), tokens2, context)), false); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundTabListPacket", e); } @@ -965,14 +991,14 @@ public class PacketConsumers { int containerId = buf.readVarInt(); int type = buf.readVarInt(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeVarInt(containerId); buf.writeVarInt(type); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundOpenScreenPacket", e); } @@ -986,13 +1012,13 @@ public class PacketConsumers { int type = buf.readVarInt(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; buf.clear(); buf.writeVarInt(event.packetID()); buf.writeVarInt(containerId); buf.writeVarInt(type); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundOpenScreenPacket", e); } @@ -1168,7 +1194,7 @@ public class PacketConsumers { if (player.isAdventureMode()) { if (Config.simplifyAdventureBreakCheck()) { ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); - if (!player.canBreak(pos, state.vanillaBlockState().handle())) { + if (!player.canBreak(pos, state.vanillaBlockState().literalObject())) { player.preventMiningBlock(); return; } @@ -1256,6 +1282,7 @@ public class PacketConsumers { int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; player.setClientSideSectionCount(sectionCount); player.setClientSideDimension(Key.of(location.toString())); + player.clearTrackedChunks(); } else { CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); } @@ -1336,7 +1363,7 @@ public class PacketConsumers { Key itemId = state.settings().itemId(); // no item available if (itemId == null) return; - Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().handle()); + Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().literalObject()); Object vanillaBlockItem = FastNMS.INSTANCE.method$Block$asItem(vanillaBlock); if (vanillaBlockItem == null) return; Key addItemId = KeyUtils.namespacedKey2Key(item.getType().getKey()); @@ -1896,55 +1923,46 @@ public class PacketConsumers { return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); } - public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { + public static final TriConsumer CUSTOM_PAYLOAD_1_20_2 = (user, event, packet) -> { try { if (!VersionHelper.isOrAbove1_20_2()) return; Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet); Payload clientPayload; - if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { + if (VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { clientPayload = DiscardedPayload.from(payload); - } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(payload)) { + } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$ServerboundCustomPayloadPacket$UnknownPayload.isInstance(payload)) { clientPayload = UnknownPayload.from(payload); } else { return; } - if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) - return; - FriendlyByteBuf buf = clientPayload.toBuffer(); - NetWorkDataTypes dataType = buf.readEnumConstant(NetWorkDataTypes.class); - if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { - int clientBlockRegistrySize = dataType.decode(buf); - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (clientBlockRegistrySize != serverBlockRegistrySize) { - user.kick(Component.translatable( - "disconnect.craftengine.block_registry_mismatch", - TranslationArgument.numeric(clientBlockRegistrySize), - TranslationArgument.numeric(serverBlockRegistrySize) - )); - return; - } - user.setClientModState(true); - } else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { - if (dataType.decode(buf)) { - FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); - bufPayload.writeEnumConstant(dataType); - dataType.encode(bufPayload, true); - user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array()); - } - } + if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) return; + PayloadHelper.handleReceiver(clientPayload, user); } catch (Throwable e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); } }; + public static final BiConsumer CUSTOM_PAYLOAD_1_20 = (user, event) -> { + try { + if (VersionHelper.isOrAbove1_20_2()) return; + FriendlyByteBuf byteBuf = event.getBuffer(); + Key key = byteBuf.readKey(); + if (!key.equals(NetworkManager.MOD_CHANNEL_KEY)) return; + PayloadHelper.handleReceiver(new UnknownPayload(key, byteBuf.readBytes(byteBuf.readableBytes())), user); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); + } + }; + @SuppressWarnings("unchecked") public static final BiConsumer SET_ENTITY_DATA = (user, event) -> { try { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); EntityPacketHandler handler = user.entityPacketHandlers().get(id); if (handler != null) { - handler.handleSetEntityData(user, event); + handler.handleSetEntityData(serverPlayer, event); return; } if (Config.interceptEntityName()) { @@ -1958,12 +1976,10 @@ public class PacketConsumers { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); isChanged = true; @@ -1985,6 +2001,7 @@ public class PacketConsumers { public static final BiConsumer SET_SCORE_1_20_3 = (user, event) -> { try { if (!Config.interceptSetScore()) return; + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; boolean isChanged = false; FriendlyByteBuf buf = event.getBuffer(); String owner = buf.readUtf(); @@ -1997,12 +2014,10 @@ public class PacketConsumers { } outside: if (displayName != null) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) break outside; Component component = AdventureHelper.tagToComponent(displayName); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); displayName = AdventureHelper.componentToTag(component); isChanged = true; } @@ -2020,13 +2035,11 @@ public class PacketConsumers { } else if (format == 2) { fixed = buf.readNbt(false); if (fixed == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); if (tokens.isEmpty() && !isChanged) return; if (!tokens.isEmpty()) { Component component = AdventureHelper.tagToComponent(fixed); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); fixed = AdventureHelper.componentToTag(component); isChanged = true; } @@ -2569,11 +2582,12 @@ public class PacketConsumers { public static final BiConsumer UPDATE_ADVANCEMENTS = (user, event) -> { try { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; FriendlyByteBuf buf = event.getBuffer(); boolean reset = buf.readBoolean(); List added = buf.readCollection(ArrayList::new, byteBuf -> { AdvancementHolder holder = AdvancementHolder.read(byteBuf); - holder.applyClientboundData((BukkitServerPlayer) user); + holder.applyClientboundData(serverPlayer); return holder; }); Set removed = buf.readCollection(Sets::newLinkedHashSetWithExpectedSize, FriendlyByteBuf::readKey); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java index bef170327..b8b0e9cc8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java @@ -67,4 +67,8 @@ public interface PacketIds { int serverboundInteractPacket(); int clientboundUpdateRecipesPacket(); + + int clientboundForgetLevelChunkPacket(); + + int serverboundCustomPayloadPacket(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java index d7aa19418..672394811 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java @@ -4,11 +4,13 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -21,7 +23,7 @@ public class ArmorStandPacketHandler implements EntityPacketHandler { public static final ArmorStandPacketHandler INSTANCE = new ArmorStandPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { if (!Config.interceptArmorStand()) { return; } @@ -38,12 +40,9 @@ public class ArmorStandPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); isChanged = true; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java index 3f9d13d5d..953f45bf5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler { public static final BlockDisplayPacketHandler INSTANCE = new BlockDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -52,12 +54,9 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java index 17bba65f8..80a4c20cd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java @@ -5,10 +5,10 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -20,7 +20,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler { private static long lastWarningTime = 0; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java index 2d1f7f43a..3d5561c01 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class EndermanPacketHandler implements EntityPacketHandler { public static final EndermanPacketHandler INSTANCE = new EndermanPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -54,12 +56,9 @@ public class EndermanPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java index 77dff78ab..a84770a74 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java @@ -4,9 +4,9 @@ import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -17,7 +17,7 @@ public class ItemDisplayPacketHandler implements EntityPacketHandler { public static final ItemDisplayPacketHandler INSTANCE = new ItemDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java index 25c4f3871..eeefbcc48 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java @@ -5,10 +5,10 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -20,7 +20,7 @@ public class ItemFramePacketHandler implements EntityPacketHandler { private static long lastWarningTime = 0; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -41,7 +41,7 @@ public class ItemFramePacketHandler implements EntityPacketHandler { continue; } ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); - Optional optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user); + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); if (optional.isEmpty()) continue; isChanged = true; itemStack = optional.get(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/AbstractMinecartPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java similarity index 84% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/AbstractMinecartPacketHandler.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java index 7e087f395..653b1f18a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/AbstractMinecartPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java @@ -7,11 +7,14 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.VersionHelper; @@ -20,12 +23,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; -public class AbstractMinecartPacketHandler implements EntityPacketHandler { - public static final AbstractMinecartPacketHandler INSTANCE = new AbstractMinecartPacketHandler(); - private static final BlockStateHandler HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE; +public class MinecartPacketHandler implements EntityPacketHandler { + public static final MinecartPacketHandler INSTANCE = new MinecartPacketHandler(); + private static final BlockStateHandler BLOCK_STATE_HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -33,7 +36,7 @@ public class AbstractMinecartPacketHandler implements EntityPacketHandler { for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - Object blockState = HANDLER.handle(user, packedItem, entityDataId); + Object blockState = BLOCK_STATE_HANDLER.handle(user, packedItem, entityDataId); if (blockState != null) { packedItems.set(i, blockState); isChanged = true; @@ -43,12 +46,9 @@ public class AbstractMinecartPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java index f352c9620..ef3178e21 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class PrimedTNTPacketHandler implements EntityPacketHandler { public static final PrimedTNTPacketHandler INSTANCE = new PrimedTNTPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -52,12 +54,9 @@ public class PrimedTNTPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java index 9c217435d..5c7d29b86 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; @@ -38,7 +39,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { } @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); event.setChanged(true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java index a02346b58..8b7fc31b9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java @@ -5,11 +5,13 @@ import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -20,7 +22,7 @@ public class TextDisplayPacketHandler implements EntityPacketHandler { public static final TextDisplayPacketHandler INSTANCE = new TextDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { if (!Config.interceptTextDisplay()) { return; } @@ -35,12 +37,9 @@ public class TextDisplayPacketHandler implements EntityPacketHandler { Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); if (textComponent == CoreReflections.instance$Component$empty) break; String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component))); isChanged = true; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java index 34981a06b..99eb1c449 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java @@ -169,4 +169,14 @@ public class PacketIds1_20 implements PacketIds { public int clientboundUpdateAdvancementsPacket() { return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundUpdateAdvancementsPacket); } + + @Override + public int clientboundForgetLevelChunkPacket() { + return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundForgetLevelChunkPacket); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundCustomPayloadPacket); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java index 216b11d56..70596861c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java @@ -168,4 +168,14 @@ public class PacketIds1_20_5 implements PacketIds { public int serverboundInteractPacket() { return PacketIdFinder.serverboundByName("minecraft:interact"); } + + @Override + public int clientboundForgetLevelChunkPacket() { + return PacketIdFinder.clientboundByName("minecraft:forget_level_chunk"); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PacketIdFinder.serverboundByName("custom_payload"); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java deleted file mode 100644 index 7fa1d4131..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -import java.util.function.Function; - -public interface NetWorkCodec extends NetWorkEncoder, NetWorkDecoder { - - default NetWorkCodec map(Function factory, Function getter) { - return new NetWorkCodec<>() { - @Override - public O decode(ByteBuf in) { - return factory.apply(NetWorkCodec.this.decode(in)); - } - - @Override - public void encode(ByteBuf out, O value) { - NetWorkCodec.this.encode(out, getter.apply(value)); - } - }; - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java deleted file mode 100644 index 37da1618d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public enum NetWorkDataTypes { - CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER), - CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN); - - private final NetWorkCodec codec; - - NetWorkDataTypes(NetWorkCodec codec) { - this.codec = codec; - } - - public NetWorkCodec codec() { - return codec; - } - - @SuppressWarnings("unchecked") - public V decode(ByteBuf buf) { - return (V) codec.decode(buf); - } - - @SuppressWarnings("unchecked") - public void encode(ByteBuf buf, V value) { - ((NetWorkCodec) codec).encode(buf, value); - } -} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java deleted file mode 100644 index fc3024db0..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public interface NetWorkDecoder { - T decode(ByteBuf in); -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java deleted file mode 100644 index 3c346115a..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public interface NetWorkEncoder { - void encode(ByteBuf out, T value); -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java new file mode 100644 index 000000000..0fee4fb84 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java @@ -0,0 +1,55 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.Unpooled; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.CancelBlockUpdatePacket; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientBlockStateSizePacket; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientCustomBlockPacket; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.NetworkManager; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.ResourceKey; + +public class PayloadHelper { + + public static void registerDataTypes() { + registerDataType(ClientCustomBlockPacket.TYPE, ClientCustomBlockPacket.CODEC); + registerDataType(CancelBlockUpdatePacket.TYPE, CancelBlockUpdatePacket.CODEC); + registerDataType(ClientBlockStateSizePacket.TYPE, ClientBlockStateSizePacket.CODEC); + } + + public static void registerDataType(ResourceKey> key, NetworkCodec codec) { + ((WritableRegistry>) BuiltInRegistries.MOD_PACKET).register(key, codec); + } + + public static void sendData(NetWorkUser user, ModPacket data) { + @SuppressWarnings("unchecked") + NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(data.type()); + if (codec == null) { + CraftEngine.instance().logger().warn("Unknown data type class: " + data.getClass().getName()); + return; + } + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeByte(BuiltInRegistries.MOD_PACKET.getId(codec)); + codec.encode(buf, data); + user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, buf.array()); + } + + public static void handleReceiver(Payload payload, NetWorkUser user) { + FriendlyByteBuf buf = payload.toBuffer(); + byte type = buf.readByte(); + @SuppressWarnings("unchecked") + NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); + if (codec == null) { + CraftEngine.instance().logger().warn("Unknown data type received: " + type); + return; + } + + ModPacket networkData = codec.decode(buf); + networkData.handle(user); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java index 4143eea27..7eece0691 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java @@ -11,8 +11,8 @@ public record UnknownPayload(Key channel, ByteBuf rawPayload) implements Payload public static UnknownPayload from(Object payload) { try { - Object id = NetworkReflections.field$UnknownPayload$id.get(payload); - ByteBuf data = (ByteBuf) NetworkReflections.field$UnknownPayload$data.get(payload); + Object id = NetworkReflections.field$ServerboundCustomPayloadPacket$UnknownPayload$id.get(payload); + ByteBuf data = (ByteBuf) NetworkReflections.field$ServerboundCustomPayloadPacket$UnknownPayload$data.get(payload); Key channel = KeyUtils.resourceLocationToKey(id); return new UnknownPayload(channel, data); } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java new file mode 100644 index 000000000..215046487 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public record CancelBlockUpdatePacket(boolean enabled) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "cancel_block_update") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + CancelBlockUpdatePacket::encode, + CancelBlockUpdatePacket::new + ); + + private CancelBlockUpdatePacket(FriendlyByteBuf buf) { + this(buf.readBoolean()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeBoolean(this.enabled); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + if (!this.enabled) return; + PayloadHelper.sendData(user, this); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java new file mode 100644 index 000000000..e2840c816 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java @@ -0,0 +1,40 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + + +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.IntIdentityList; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public record ClientBlockStateSizePacket(int blockStateSize) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "client_block_state_size") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + ClientBlockStateSizePacket::encode, + ClientBlockStateSizePacket::new + ); + + private ClientBlockStateSizePacket(FriendlyByteBuf buf) { + this(buf.readInt()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeInt(this.blockStateSize); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + user.setClientBlockList(new IntIdentityList(this.blockStateSize)); + } + +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java new file mode 100644 index 000000000..257399aae --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java @@ -0,0 +1,77 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + + +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslationArgument; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.paper.PaperReflections; +import net.momirealms.craftengine.bukkit.util.RegistryUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.*; +import org.bukkit.entity.Player; + +public record ClientCustomBlockPacket(int size) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "client_custom_block") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + ClientCustomBlockPacket::encode, + ClientCustomBlockPacket::new + ); + + private ClientCustomBlockPacket(FriendlyByteBuf buf) { + this(buf.readInt()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeInt(this.size); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + if (user.clientModEnabled()) return; // 防止滥用 + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (this.size != serverBlockRegistrySize) { + user.kick(Component.translatable( + "disconnect.craftengine.block_registry_mismatch", + TranslationArgument.numeric(this.size), + TranslationArgument.numeric(serverBlockRegistrySize) + )); + return; + } + user.setClientModState(true); + user.setClientBlockList(new IntIdentityList(this.size)); + if (!VersionHelper.isOrAbove1_20_2()) { + // 因为旧版本没有配置阶段需要重新发送区块 + try { + Object chunkLoader = PaperReflections.field$ServerPlayer$chunkLoader.get(user.serverPlayer()); + LongOpenHashSet sentChunks = (LongOpenHashSet) PaperReflections.field$RegionizedPlayerChunkLoader$PlayerChunkLoaderData$sentChunks.get(chunkLoader); + Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(((Player) user.platformPlayer()).getWorld()); + Object lightEngine = CoreReflections.method$BlockAndTintGetter$getLightEngine.invoke(serverLevel); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); + for (long chunkPos : sentChunks) { + int chunkX = (int) chunkPos; + int chunkZ = (int) (chunkPos >> 32); + Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunk(chunkSource, chunkX, chunkZ, false); + Object packet = NetworkReflections.constructor$ClientboundLevelChunkWithLightPacket.newInstance(levelChunk, lightEngine, null, null); + user.sendPacket(packet, true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to refresh chunk for player " + user.name(), e); + } + } + } + +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/LeavesReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/LeavesReflections.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java index b7b89995c..dd5522890 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/LeavesReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; +package net.momirealms.craftengine.bukkit.plugin.reflection.leaves; //import net.momirealms.craftengine.core.util.MiscUtils; //import net.momirealms.craftengine.core.util.ReflectionUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index bff6a601b..600e1505b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -1181,6 +1181,17 @@ public final class CoreReflections { ) ); + public static final Class clazz$ServerLevel = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "server.level.WorldServer", + "server.level.ServerLevel" + ) + ); + + public static final Class clazz$Explosion = requireNonNull( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.Explosion")) + ); + public static final Constructor constructor$StateDefinition$Builder = requireNonNull( ReflectionUtils.getTheOnlyConstructor(clazz$StateDefinition$Builder) ); @@ -1451,6 +1462,18 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 0) ); + public static final Field field$BlockBehaviour$friction = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 1) + ); + + public static final Field field$BlockBehaviour$speedFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 2) + ); + + public static final Field field$BlockBehaviour$jumpFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 3) + ); + public static final Field field$BlockBehaviour$soundType = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, clazz$SoundType, 0) ); @@ -1547,6 +1570,15 @@ public final class CoreReflections { ) ); + public static final Method method$BlockBehaviour$hasAnalogOutputSignal = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, new String[]{"hasAnalogOutputSignal", + VersionHelper.isOrAbove1_20_5() ? "c_" : "d_"}, clazz$BlockState) + ); + + public static final Method method$BlockBehaviour$getAnalogOutputSignal = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getAnalogOutputSignal", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos) + ); + public static final Method method$Entity$level = requireNonNull( ReflectionUtils.getMethod(clazz$Entity, clazz$Level) ); @@ -1651,6 +1683,15 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Direction, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockPos) ); + public static final Method method$BlockBehaviour$canSurvive = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$LevelReader, clazz$BlockPos) + ); + + public static final Method method$BlockBehaviour$onExplosionHit = MiscUtils.requireNonNullIf( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, VersionHelper.isOrAbove1_21_2() ? clazz$ServerLevel : clazz$Level, clazz$BlockPos, clazz$Explosion, BiConsumer.class), + VersionHelper.isOrAbove1_21() + ); + public static final Class clazz$Fallable = requireNonNull( ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.block.Fallable")) ); @@ -1673,6 +1714,14 @@ public final class CoreReflections { ) ); + public static final Method method$Fallable$onLand = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$BlockState, clazz$BlockState, clazz$FallingBlockEntity) + ); + + public static final Method method$Fallable$onBrokenAfterFall = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$FallingBlockEntity) + ); + public static final Method method$FallingBlockEntity$fall = requireNonNull( ReflectionUtils.getStaticMethod(clazz$FallingBlockEntity, clazz$FallingBlockEntity, clazz$Level, clazz$BlockPos, clazz$BlockState) ); @@ -2274,12 +2323,30 @@ public final class CoreReflections { ) ); + public static final Class clazz$WorldlyContainerHolder = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IInventoryHolder", + "world.WorldlyContainerHolder" + ) + ); + public static final Method method$BonemealableBlock$isValidBonemealTarget = requireNonNull( VersionHelper.isOrAbove1_20_2() ? ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState) : ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState, boolean.class) ); + public static final Class clazz$WorldlyContainer = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IWorldInventory", + "world.WorldlyContainer" + ) + ); + + + public static final Method method$WorldlyContainerHolder$getContainer = requireNonNull( + ReflectionUtils.getMethod(clazz$WorldlyContainerHolder, clazz$WorldlyContainer, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos) + ); public static final Method method$BonemealableBlock$isBonemealSuccess = requireNonNull( ReflectionUtils.getMethod(clazz$BonemealableBlock, boolean.class, clazz$Level, clazz$RandomSource, clazz$BlockPos, clazz$BlockState) ); @@ -2957,13 +3024,6 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0) ); - public static final Class clazz$ServerLevel = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "server.level.WorldServer", - "server.level.ServerLevel" - ) - ); - public static final Method method$ServerLevel$getNoiseBiome = requireNonNull( ReflectionUtils.getMethod(clazz$ServerLevel, clazz$Holder, int.class, int.class, int.class) ); @@ -3446,6 +3506,11 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"randomTick", "b"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$RandomSource) ); + public static final Method method$BlockBehaviour$onPlace = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"onPlace", VersionHelper.isOrAbove1_21_5() ? "a" : "b"}, + clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class) + ); + public static final Class clazz$InsideBlockEffectApplier = BukkitReflectionUtils.findReobfOrMojmapClass( "world.entity.InsideBlockEffectApplier", "world.entity.InsideBlockEffectApplier" @@ -3603,12 +3668,6 @@ public final class CoreReflections { ) ); - public static final Class clazz$Explosion = requireNonNull( - ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("world.level.Explosion") - ) - ); - // 1.20.5+ public static final Field field$ItemStack$CODEC = ReflectionUtils.getDeclaredField(clazz$ItemStack, "CODEC", "b"); @@ -3993,18 +4052,15 @@ public final class CoreReflections { ReflectionUtils.getStaticMethod(clazz$ArmorTrim, Optional.class, clazz$RegistryAccess, clazz$ItemStack); public static final Method method$BlockBehaviour$spawnAfterBreak = requireNonNull( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class - ) + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class) ); // 1.20~1.21.4 public static final Method method$BlockBehaviour$onRemove = MiscUtils.requireNonNullIf( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class - ), + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"a", "onRemove"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class), !VersionHelper.isOrAbove1_21_5() ); + public static final Object instance$CollisionContext$empty; static { @@ -4148,4 +4204,8 @@ public final class CoreReflections { "world.level.storage.loot.entries.LootPoolEntryType" ) ); + + public static final Method method$BlockAndTintGetter$getLightEngine = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockAndTintGetter, clazz$LevelLightEngine) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 5bb4bd47d..f97ae5164 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -1057,7 +1057,6 @@ public final class NetworkReflections { ) ); - public static final Constructor constructor$ClientboundMoveEntityPacket$PosRot = requireNonNull( ReflectionUtils.getTheOnlyConstructor(clazz$ClientboundMoveEntityPacket$PosRot) ); @@ -1255,7 +1254,17 @@ public final class NetworkReflections { // 1.20.2+ public static final Constructor constructor$DiscardedPayload = Optional.ofNullable(clazz$DiscardedPayload) - .map(ReflectionUtils::getTheOnlyConstructor) + .map(it -> { + if (VersionHelper.isOrAbove1_20_5()) { + Constructor constructor1 = ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, ByteBuf.class); + if (constructor1 != null) { + return constructor1; + } + return ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, byte[].class); + } else { + return ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation); + } + }) .orElse(null); public static final Class clazz$ClientboundContainerSetContentPacket = requireNonNull( @@ -1625,7 +1634,7 @@ public final class NetworkReflections { } // 1.20.2~1.20.4 - public static final Class clazz$UnknownPayload = MiscUtils.requireNonNullIf( + public static final Class clazz$ServerboundCustomPayloadPacket$UnknownPayload = MiscUtils.requireNonNullIf( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload") ), @@ -1633,18 +1642,18 @@ public final class NetworkReflections { ); // 1.20.2~1.20.4 - public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$UnknownPayload) + public static final Field field$ServerboundCustomPayloadPacket$UnknownPayload$id = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) .map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ResourceLocation, 0)) .orElse(null); // 1.20.2~1.20.4 - public static final Field field$UnknownPayload$data = Optional.ofNullable(clazz$UnknownPayload) + public static final Field field$ServerboundCustomPayloadPacket$UnknownPayload$data = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) .map(it -> ReflectionUtils.getDeclaredField(it, ByteBuf.class, 0)) .orElse(null); // 1.20.2~1.20.4 - public static final Constructor constructor$UnknownPayload = Optional.ofNullable(clazz$UnknownPayload) - .map(ReflectionUtils::getTheOnlyConstructor) + public static final Constructor constructor$ServerboundCustomPayloadPacket$UnknownPayload = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) + .map(it -> ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, ByteBuf.class)) .orElse(null); // 1.21.5+ @@ -1655,4 +1664,11 @@ public final class NetworkReflections { ), VersionHelper.isOrAbove1_21_5() ); + + public static final Class clazz$ClientboundForgetLevelChunkPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutUnloadChunk", + "network.protocol.game.ClientboundForgetLevelChunkPacket" + ) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java index 1210342a2..d6d4095bd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.paper; import com.google.gson.Gson; import io.papermc.paper.event.player.AsyncChatDecorateEvent; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.core.util.ReflectionUtils; @@ -97,4 +98,23 @@ public final class PaperReflections { public static final Method method$BookMeta$page = requireNonNull( ReflectionUtils.getMethod(CraftBukkitReflections.clazz$BookMeta, void.class, int.class, clazz$AdventureComponent) ); + + public static final Class clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData = requireNonNull( + ReflectionUtils.getClazz( + "ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader$PlayerChunkLoaderData", + "io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader$PlayerChunkLoaderData" + ) + ); + + public static final Field field$ServerPlayer$chunkLoader = requireNonNull( + ReflectionUtils.getDeclaredField( + CoreReflections.clazz$ServerPlayer, PaperReflections.clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData, 0 + ) + ); + + public static final Field field$RegionizedPlayerChunkLoader$PlayerChunkLoaderData$sentChunks = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData, LongOpenHashSet.class, 0 + ) + ); } 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 bf6afab4b..c448b31cd 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.user; +import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import com.google.common.collect.Lists; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -31,15 +32,18 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.IntIdentityList; 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.World; import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.block.Block; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -95,6 +99,7 @@ public class BukkitServerPlayer extends Player { private int resentSwingTick; // has fabric client mod or not private boolean hasClientMod = false; + private IntIdentityList blockList = new IntIdentityList(BlockStateUtils.vanillaStateSize()); // cache if player can break blocks private boolean clientSideCanBreak = true; // prevent AFK players from consuming too much CPU resource on predicting @@ -108,8 +113,10 @@ public class BukkitServerPlayer extends Player { private double cachedInteractionRange; // cooldown data private CooldownData cooldownData; - - private final Map entityTypeView = new ConcurrentHashMap<>(); + // tracked chunks + private ConcurrentLong2ReferenceChainedHashTable trackedChunks; + // entity view + private Map entityTypeView; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -131,6 +138,8 @@ public class BukkitServerPlayer extends Player { this.uuid = player.getUniqueId(); this.name = player.getName(); byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); + this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(768, 0.5f); + this.entityTypeView = new ConcurrentHashMap<>(256); try { this.cooldownData = CooldownData.fromBytes(bytes); } catch (IOException e) { @@ -177,6 +186,26 @@ public class BukkitServerPlayer extends Player { return platformPlayer().isSneaking(); } + @Override + public boolean isSwimming() { + return platformPlayer().isSwimming(); + } + + @Override + public boolean isClimbing() { + return platformPlayer().isClimbing(); + } + + @Override + public boolean isGliding() { + return platformPlayer().isGliding(); + } + + @Override + public boolean isFlying() { + return platformPlayer().isFlying(); + } + @Override public GameMode gameMode() { return switch (platformPlayer().getGameMode()) { @@ -351,8 +380,8 @@ public class BukkitServerPlayer extends Player { Object responsePacket; if (VersionHelper.isOrAbove1_20_2()) { Object dataPayload; - if (NetworkReflections.clazz$UnknownPayload != null) { - dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); + if (!VersionHelper.isOrAbove1_20_5()) { + dataPayload = NetworkReflections.constructor$ServerboundCustomPayloadPacket$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); } else if (DiscardedPayload.useNewMethod) { dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelResourceLocation, data); } else { @@ -475,10 +504,7 @@ public class BukkitServerPlayer extends Player { ImmutableBlockState customState = optionalCustomState.get(); Item tool = getItemInHand(InteractionHand.MAIN_HAND); boolean isCorrectTool = FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool.getLiteralObject(), blockState); - // 如果自定义方块在服务端侧未使用正确地工具,那么需要还原挖掘速度 - if (!isCorrectTool) { - progress *= (10f / 3f); - } + // 如果自定义方块在服务端侧未使用正确的工具,那么需要还原挖掘速度 if (!BlockStateUtils.isCorrectTool(customState, tool)) { progress *= customState.settings().incorrectToolSpeed(); } @@ -520,7 +546,7 @@ public class BukkitServerPlayer extends Player { if (custom && getDestroyProgress(state, pos) >= 1f) { BlockStateWrapper vanillaBlockState = immutableBlockState.vanillaBlockState(); // if it's not an instant break on client side, we should resend level event - if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) { + if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.literalObject(), pos) < 1f) { Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket( WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false); sendPacket(levelEventPacket, false); @@ -682,7 +708,7 @@ public class BukkitServerPlayer extends Player { // for simplified adventure break, switch mayBuild temporarily if (isAdventureMode() && Config.simplifyAdventureBreakCheck()) { // check the appearance state - if (canBreak(hitPos, customState.vanillaBlockState().handle())) { + if (canBreak(hitPos, customState.vanillaBlockState().literalObject())) { // Error might occur so we use try here try { FastNMS.INSTANCE.field$Player$mayBuild(serverPlayer, true); @@ -886,14 +912,26 @@ public class BukkitServerPlayer extends Player { return resentSwingTick == gameTicks(); } + @Override public boolean clientModEnabled() { return this.hasClientMod; } + @Override public void setClientModState(boolean enable) { this.hasClientMod = enable; } + @Override + public void setClientBlockList(IntIdentityList blockList) { + this.blockList = blockList; + } + + @Override + public IntIdentityList clientBlockList() { + return this.blockList; + } + @Override public void addResourcePackUUID(UUID uuid) { if (VersionHelper.isOrAbove1_20_3()) { @@ -935,8 +973,28 @@ public class BukkitServerPlayer extends Player { } @Override - public void performCommand(String command) { - platformPlayer().performCommand(command); + public void performCommand(String command, boolean asOp) { + org.bukkit.entity.Player player = platformPlayer(); + if (asOp) { + boolean isOp = player.isOp(); + player.setOp(true); + try { + player.performCommand(command); + } catch (Throwable t) { + this.plugin.logger().warn("Failed to perform command '" + command + "' for " + this.name() + " as operator", t); + } + player.setOp(isOp); + } else { + player.performCommand(command); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void performCommandAsEvent(String command) { + String formattedCommand = command.startsWith("/") ? command : "/" + command; + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(platformPlayer(), formattedCommand); + Bukkit.getPluginManager().callEvent(event); } @Override @@ -948,11 +1006,6 @@ public class BukkitServerPlayer extends Player { } } - @Override - public boolean isFlying() { - return platformPlayer().isFlying(); - } - @Override public int foodLevel() { return platformPlayer().getFoodLevel(); @@ -996,4 +1049,29 @@ public class BukkitServerPlayer extends Player { public CooldownData cooldown() { return this.cooldownData; } + + @Override + public boolean isChunkTracked(long chunkPos) { + return this.trackedChunks.containsKey(chunkPos); + } + + @Override + public ChunkStatus getTrackedChunk(long chunkPos) { + return this.trackedChunks.get(chunkPos); + } + + @Override + public void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus) { + this.trackedChunks.put(chunkPos, chunkStatus); + } + + @Override + public void removeTrackedChunk(long chunkPos) { + this.trackedChunks.remove(chunkPos); + } + + @Override + public void clearTrackedChunks() { + this.trackedChunks.clear(); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 5728c14df..3d3a592f7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.block.BukkitBlockStateWrapper; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -42,10 +43,10 @@ public final class BlockStateUtils { hasInit = true; } - public static BlockStateWrapper toPackedBlockState(BlockData blockData) { + public static BlockStateWrapper toBlockStateWrapper(BlockData blockData) { Object state = blockDataToBlockState(blockData); int id = blockStateToId(state); - return BlockStateWrapper.create(state, id, isVanillaBlock(id)); + return new BukkitBlockStateWrapper(state, id); } public static boolean isCorrectTool(@NotNull ImmutableBlockState state, @Nullable Item itemInHand) { @@ -53,7 +54,7 @@ public final class BlockStateUtils { if (settings.requireCorrectTool()) { if (itemInHand == null || itemInHand.isEmpty()) return false; return settings.isCorrectTool(itemInHand.id()) || - (settings.respectToolComponent() && FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().handle())); + (settings.respectToolComponent() && FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().literalObject())); } return true; } @@ -145,8 +146,6 @@ public final class BlockStateUtils { } public static Object getBlockState(Block block) { - Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()); - Object blockPos = LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ()); - return FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos); + return FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ())); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java index cf0ef5d0c..41ce992b8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java @@ -43,12 +43,9 @@ public final class DirectionUtils { } public static Direction fromNMSDirection(Object direction) { - try { - int index = (int) CoreReflections.method$Direction$ordinal.invoke(direction); - return Direction.values()[index]; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + Enum directionEnum = (Enum) direction; + int index = directionEnum.ordinal(); + return Direction.values()[index]; } public static boolean isYAxis(Object nmsDirection) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java index 6424b21f3..9bd42c689 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import java.util.HashMap; import java.util.Map; +import java.util.Objects; public final class EnchantmentUtils { @@ -11,6 +12,7 @@ public final class EnchantmentUtils { @SuppressWarnings("unchecked") public static Map toMap(Object itemEnchantments) throws ReflectiveOperationException { + if (itemEnchantments == null) return Map.of(); Map map = new HashMap<>(); Map enchantments = (Map) CoreReflections.field$ItemEnchantments$enchantments.get(itemEnchantments); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java index d16537def..dee6874b7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java @@ -169,6 +169,7 @@ public final class InteractUtils { registerInteraction(BlockKeys.DAMAGED_ANVIL, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.FURNACE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.CRAFTING_TABLE, (player, item, blockState, result) -> true); + registerInteraction(BlockKeys.CARTOGRAPHY_TABLE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.STONECUTTER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.SMITHING_TABLE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.LOOM, (player, item, blockState, result) -> true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java index 636c5c1e3..298ca5786 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import org.bukkit.World; @@ -23,7 +22,7 @@ public final class LightUtils { if (chunkHolder == null) continue; List players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder); if (players.isEmpty()) continue; - Object lightEngine = CoreReflections.field$ChunkHolder$lightEngine.get(chunkHolder); + Object lightEngine = FastNMS.INSTANCE.method$ChunkSource$getLightEngine(chunkSource); Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, entry.getValue(), entry.getValue()); for (Object player : players) { @@ -36,4 +35,42 @@ public final class LightUtils { CraftEngine.instance().logger().warn("Could not update light for world " + world.getName(), e); } } +// +// public static void relightChunk(BukkitServerPlayer player, ChunkPos pos) { +// long chunkKey = pos.longKey; +// ChunkStatus status = player.getTrackedChunk(chunkKey); +// // 不处理未加载区块 +// if (status == null || status.relighted()) return; +// for (ChunkPos anotherPos : pos.adjacentChunkPos()) { +// // 要求周围区块必须都加载 +// if (player.getTrackedChunk(anotherPos.longKey) == null) { +// return; +// } +// } +// status.setRelighted(true); +// net.momirealms.craftengine.core.world.World world = player.world(); +// Object serverLevel = world.serverWorld(); +// Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); +// Object chunkHolder = FastNMS.INSTANCE.method$ServerChunkCache$getVisibleChunkIfPresent(chunkSource, chunkKey); +// if (chunkHolder == null) return; +// CEWorld ceWorld = BukkitWorldManager.instance().getWorld(world.uuid()); +// CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkKey); +// if (ceChunk == null) return; +// CESection[] sections = ceChunk.sections(); +// BitSet bitSet = new BitSet(); +// for (int i = 0; i < sections.length; i++) { +// if (!sections[i].statesContainer().isEmpty()) { +// bitSet.set(i); +// } +// } +// if (bitSet.isEmpty()) return; +// try { +// Object lightEngine = CoreReflections.field$ChunkHolder$lightEngine.get(chunkHolder); +// Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); +// Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, bitSet, bitSet); +// player.sendPacket(lightPacket, false); +// } catch (Throwable t) { +// CraftEngine.instance().logger().warn("Could not send relight packet for " + player.name() + " at " + player.world().name() + " " + pos.x + "," + pos.z, t); +// } +// } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java index 0703d45ea..61ef8bc5e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -47,7 +47,7 @@ public final class ParticleUtils { public static Object toBukkitParticleData(ParticleData particleData, Context context, World world, double x, double y, double z) { return switch (particleData) { - case BlockStateData data -> BlockStateUtils.fromBlockData(data.blockState().handle()); + case BlockStateData data -> BlockStateUtils.fromBlockData(data.blockState().literalObject()); case ColorData data -> ColorUtils.toBukkit(data.color()); case DustData data -> new Particle.DustOptions(ColorUtils.toBukkit(data.color()), data.size()); case DustTransitionData data -> new Particle.DustTransition(ColorUtils.toBukkit(data.from()), ColorUtils.toBukkit(data.to()), data.size()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java index 9f21529df..64ff47e55 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java @@ -37,6 +37,7 @@ public final class SoundUtils { case HOSTILE -> SoundCategory.HOSTILE; case NEUTRAL -> SoundCategory.NEUTRAL; case WEATHER -> SoundCategory.WEATHER; + case UI -> SoundCategory.UI; }; } } 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 aadb65e6f..a3f56861a 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 @@ -9,7 +9,8 @@ import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; public class BukkitCEWorld extends CEWorld { @@ -22,18 +23,18 @@ public class BukkitCEWorld extends CEWorld { } @Override - public void tick() { - HashSet poses; + public void updateLight() { + List poses; synchronized (super.updatedSectionSet) { - poses = new HashSet<>(super.updatedSectionSet); + poses = new ArrayList<>(super.updatedSectionSet); super.updatedSectionSet.clear(); } if (Config.enableLightSystem()) { LightUtils.updateChunkLight( - (org.bukkit.World) world.platformWorld(), + (org.bukkit.World) this.world.platformWorld(), SectionPosUtils.toMap(poses, - world.worldHeight().getMinSection() - 1, - world.worldHeight().getMaxSection() + 1 + this.world.worldHeight().getMinSection() - 1, + this.world.worldHeight().getMaxSection() + 1 ) ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java similarity index 66% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java index 6a9ce89a4..38dd8db86 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java @@ -7,18 +7,25 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockRegistryMirror; +import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; -import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.ExistingBlock; import net.momirealms.craftengine.core.world.World; import org.bukkit.Location; import org.bukkit.block.Block; +import org.jetbrains.annotations.NotNull; -public class BukkitBlockInWorld implements BlockInWorld { +import java.util.Optional; + +public class BukkitExistingBlock implements ExistingBlock { private final Block block; - public BukkitBlockInWorld(Block block) { + public BukkitExistingBlock(Block block) { this.block = block; } @@ -44,6 +51,22 @@ public class BukkitBlockInWorld implements BlockInWorld { return FastNMS.INSTANCE.method$FluidState$getType(fluidData) == MFluids.WATER; } + @Override + public @NotNull StatePropertyAccessor createStatePropertyAccessor() { + return FastNMS.INSTANCE.createStatePropertyAccessor(BlockStateUtils.getBlockState(this.block)); + } + + @Override + public boolean isCustom() { + return CraftEngineBlocks.isCustomBlock(this.block); + } + + @Override + public @NotNull BlockStateWrapper blockState() { + Object blockState = BlockStateUtils.getBlockState(this.block); + return BlockRegistryMirror.stateByRegistryId(BlockStateUtils.blockStateToId(blockState)); + } + @Override public int x() { return this.block.getX(); @@ -59,6 +82,16 @@ public class BukkitBlockInWorld implements BlockInWorld { return this.block.getZ(); } + @Override + public Key id() { + Object blockState = BlockStateUtils.getBlockState(this.block); + Optional optionalCustomBlockState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomBlockState.isPresent()) { + return optionalCustomBlockState.get().owner().value().id(); + } + return BlockStateUtils.getBlockOwnerIdFromState(blockState); + } + @Override public World world() { return new BukkitWorld(this.block.getWorld()); 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 af2d6c269..bfb04f654 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 @@ -50,8 +50,8 @@ public class BukkitWorld implements World { } @Override - public BlockInWorld getBlockAt(int x, int y, int z) { - return new BukkitBlockInWorld(platformWorld().getBlockAt(x, y, z)); + public ExistingBlock getBlockAt(int x, int y, int z) { + return new BukkitExistingBlock(platformWorld().getBlockAt(x, y, z)); } @Override @@ -116,7 +116,7 @@ public class BukkitWorld implements World { public void setBlockAt(int x, int y, int z, BlockStateWrapper blockState, int flags) { Object worldServer = serverWorld(); Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z); - FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.handle(), flags); + FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.literalObject(), flags); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 45d45bf9d..2d8671d53 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.world; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; @@ -8,7 +9,6 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.SectionPos; @@ -29,11 +29,9 @@ import org.bukkit.event.world.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; public class BukkitWorldManager implements WorldManager, Listener { @@ -42,18 +40,16 @@ public class BukkitWorldManager implements WorldManager, Listener { private final Map worlds; private CEWorld[] worldArray = new CEWorld[0]; private final ReentrantReadWriteLock worldMapLock = new ReentrantReadWriteLock(); - private SchedulerTask tickTask; // cache private UUID lastVisitedUUID; private CEWorld lastVisitedWorld; private StorageAdaptor storageAdaptor; - private boolean isTicking = false; private boolean initialized = false; public BukkitWorldManager(BukkitCraftEngine plugin) { instance = this; this.plugin = plugin; - this.worlds = new HashMap<>(); + this.worlds = new Object2ObjectOpenHashMap<>(32, 0.5f); this.storageAdaptor = new DefaultStorageAdaptor(); for (World world : Bukkit.getWorlds()) { this.worlds.put(world.getUID(), new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor)); @@ -101,20 +97,6 @@ public class BukkitWorldManager implements WorldManager, Listener { } public void delayedInit() { - // events and tasks - this.tickTask = this.plugin.scheduler().asyncRepeating(() -> { - try { - if (this.isTicking) { - return; - } - this.isTicking = true; - for (CEWorld world : this.worldArray) { - world.tick(); - } - } finally { - this.isTicking = false; - } - }, 50, 50, TimeUnit.MILLISECONDS); // load loaded chunks this.worldMapLock.writeLock().lock(); try { @@ -124,6 +106,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (Chunk chunk : world.getLoadedChunks()) { handleChunkLoad(ceWorld, chunk); } + ceWorld.setTicking(true); } catch (Exception e) { CraftEngine.instance().logger().warn("Error loading world: " + world.getName(), e); } @@ -142,11 +125,9 @@ public class BukkitWorldManager implements WorldManager, Listener { if (this.storageAdaptor instanceof Listener listener) { HandlerList.unregisterAll(listener); } - if (this.tickTask != null && !this.tickTask.cancelled()) { - this.tickTask.cancel(); - } for (World world : Bukkit.getWorlds()) { CEWorld ceWorld = getWorld(world.getUID()); + ceWorld.setTicking(false); for (Chunk chunk : world.getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } @@ -175,6 +156,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { handleChunkLoad(ceWorld, chunk); } + ceWorld.setTicking(true); } finally { this.worldMapLock.writeLock().unlock(); } @@ -190,6 +172,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) { handleChunkLoad(world, chunk); } + world.setTicking(true); } finally { this.worldMapLock.writeLock().unlock(); } @@ -229,6 +212,7 @@ public class BukkitWorldManager implements WorldManager, Listener { } finally { this.worldMapLock.writeLock().unlock(); } + ceWorld.setTicking(false); for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } @@ -311,7 +295,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); if (!customState.isEmpty() && customState.vanillaBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().handle(), false); + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().literalObject(), false); unsaved = true; } } @@ -398,7 +382,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); if (!customState.isEmpty() && customState.customBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false); + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().literalObject(), false); } } } diff --git a/common-files/src/main/resources/additional-real-blocks.yml b/common-files/src/main/resources/additional-real-blocks.yml index 1573d2fbf..58279f8a7 100644 --- a/common-files/src/main/resources/additional-real-blocks.yml +++ b/common-files/src/main/resources/additional-real-blocks.yml @@ -26,7 +26,7 @@ minecraft:cherry_sapling: 1 minecraft:anvil: 2 minecraft:chipped_anvil: 2 minecraft:damaged_anvil: 2 -minecraft:sugarcane: 14 +minecraft:sugar_cane: 14 minecraft:iron_trapdoor: 32 minecraft:acacia_trapdoor: 32 minecraft:oak_trapdoor: 32 diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 27ead33b2..5c39b74bf 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -29,7 +29,6 @@ resource-pack: seed: 0 # 0 = random seed fake-directory: false escape-unicode: false - break-json: false resource-location: enable: true random-namespace: @@ -41,7 +40,6 @@ resource-pack: anti-unzip: false random-atlas: images-per-canvas: 32 # 0 = disable - use-double: true # Sometimes, some vanilla files that have been overwritten might be mistakenly obfuscated. # Please add the ignored textures/models/sounds here. bypass-textures: @@ -190,16 +188,6 @@ block: extended-interaction-range: 0.5 furniture: - # Automatically remove outdated furniture entities when a chunk is loaded. - handle-invalid-furniture-on-chunk-load: - # Enable/disable the cleanup system - enable: false - # Removes the specified invalid furniture - remove: - - "xxx:invalid_furniture" - # Converts the invalid furniture to a valid one - convert: - "namespace:furniture_a": "namespace:furniture_b" # Hide technical entities used for storing furniture metadata. # NOTE: # - These are INVISIBLE entities used internally for tracking furniture states @@ -211,10 +199,14 @@ furniture: collision-entity-type: interaction emoji: - chat: true - book: true - anvil: true - sign: true + # Contexts where emoji parsing is enabled + contexts: + chat: true + book: true + anvil: true + sign: true + # Prevent lag or oversized packet when processing emoji-heavy content + max-emojis-per-parse: 16 image: # Block image tags using minecraft:default font in these interfaces @@ -366,15 +358,11 @@ gui: brewing: title: "" -performance: - # Maximum chain update depth when fixing client visuals - max-note-block-chain-update-limit: 48 - # Prevent lag or oversized packet when processing emoji-heavy content - max-emojis-per-parse: 16 - light-system: # Required for custom light-emitting blocks enable: true + # Async light update + async-update: true chunk-system: # With cache system, those frequently load/unload chunks would consume fewer resources on serialization @@ -389,7 +377,7 @@ chunk-system: # Settings for injection injection: # Requires a restart to apply. - # SECTION: Inject the LevelChunkSection + # SECTION: Inject the LevelChunkSection (Use this if you have installed both FastAsyncWorldEdit and Axiom) # PALETTE: Inject the PalettedContainer target: PALETTE # Enables faster injection method @@ -420,6 +408,16 @@ chunk-system: # server's CraftEngine internal data. Enabling this option will synchronize the data when the chunk is loaded. # (This option only slightly impacts performance, which has been fully optimized, so you don't need to worry too much.) sync-custom-blocks-on-chunk-load: false + # This system processes any invalid block when a chunk is loaded. + process-invalid-blocks: + enable: false + remove: [] + convert: {} + # This system processes any invalid furniture when a chunk is loaded. + process-invalid-furniture: + enable: false + remove: [] + convert: {} # Enables or disables debug mode debug: diff --git a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml index a47b29498..901dbbb00 100644 --- a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml @@ -643,6 +643,17 @@ recipes: result: id: default:palm_trapdoor count: 2 + default:palm_door: + type: shaped + pattern: + - AA + - AA + - AA + ingredients: + A: default:palm_planks + result: + id: default:palm_door + count: 3 default:palm_fence_gate: type: shaped pattern: @@ -683,4 +694,4 @@ recipes: A: default:palm_planks result: id: default:palm_stairs - count: 4 \ No newline at end of file + count: 4 diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index c15db1c61..efd506c7d 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -83,6 +83,8 @@ warning.config.number.fixed.invalid_value: "Issue found in file warning.config.number.expression.missing_expression: "Issue found in file - The config '' is missing the required 'expression' argument for 'expression' number." warning.config.number.uniform.missing_min: "Issue found in file - The config '' is missing the required 'min' argument for 'uniform' number." warning.config.number.uniform.missing_max: "Issue found in file - The config '' is missing the required 'max' argument for 'uniform' number." +warning.config.number.gaussian.missing_min: "Issue found in file - The config '' is missing the required 'min' argument for 'gaussian' number." +warning.config.number.gaussian.missing_max: "Issue found in file - The config '' is missing the required 'max' argument for 'gaussian' number." warning.config.condition.all_of.missing_terms: "Issue found in file - The config '' is missing the required 'terms' argument for 'all_of' condition." warning.config.condition.all_of.invalid_terms_type: "Issue found in file - The config '' has a misconfigured 'all_of' condition, 'terms' should be a map list, current type: ''." warning.config.condition.any_of.missing_terms: "Issue found in file - The config '' is missing the required 'terms' argument for 'any_of' condition." @@ -92,6 +94,8 @@ warning.config.condition.inverted.invalid_term_type: "Issue found in fil warning.config.condition.enchantment.missing_predicate: "Issue found in file - The config '' is missing the required 'predicate' argument for 'enchantment' condition." warning.config.condition.enchantment.invalid_predicate: "Issue found in file - The config '' is using an invalid enchantment 'predicate' argument ''." warning.config.condition.match_block_property.missing_properties: "Issue found in file - The config '' is missing the required 'properties' argument for 'match_block_property' condition." +warning.config.condition.match_block.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_block' condition." +warning.config.condition.match_entity.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_entity' condition." warning.config.condition.match_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_item' condition." warning.config.condition.table_bonus.missing_enchantment: "Issue found in file - The config '' is missing the required 'enchantment' argument for 'table_bonus' condition." warning.config.condition.table_bonus.missing_chances: "Issue found in file - The config '' is missing the required 'chances' argument for 'table_bonus' condition." @@ -295,17 +299,18 @@ warning.config.block.behavior.fence_gate.missing_facing: "Issue found in warning.config.block.behavior.fence_gate.missing_in_wall: "Issue found in file - The block '' is missing the required 'in_wall' argument for 'fence_gate_block' behavior." warning.config.block.behavior.fence_gate.missing_open: "Issue found in file - The block '' is missing the required 'powered' argument for 'fence_gate_block' behavior." warning.config.block.behavior.fence_gate.missing_powered: "Issue found in file - The block '' is missing the required 'open' argument for 'fence_gate_block' behavior." -warning.config.block.behavior.trapdoor.missing_type: "Issue found in file - The block '' is missing the required 'type' property for 'slab_block' behavior." +warning.config.block.behavior.slab.missing_type: "Issue found in file - The block '' is missing the required 'type' property for 'slab_block' behavior." warning.config.block.behavior.stairs.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'stairs_block' behavior." warning.config.block.behavior.stairs.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'stairs_block' behavior." warning.config.block.behavior.stairs.missing_shape: "Issue found in file - The block '' is missing the required 'shape' property for 'stairs_block' behavior." warning.config.block.behavior.pressure_plate.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'pressure_plate_block' behavior." warning.config.block.behavior.grass.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'grass_block' behavior." warning.config.block.behavior.double_high.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'double_block' behavior." +warning.config.block.behavior.change_over_time.missing_next_block: "Issue found in file - The block '' is missing the required 'next_block' property for 'change_over_time_block' behavior." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." +warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" warning.config.model.generation.invalid_gui_light: "Issue found in file - The config '' is using an invalid gui-light option '' in 'generation' section. Allowed gui light options: []" -warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.texture.invalid: "Issue found in file - The config '' has a texture '' with path '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.model.generation.parent.invalid: "Issue found in file - The config '' has a parent argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.emoji.missing_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." @@ -411,14 +416,14 @@ warning.config.selector.invalid_type: "Issue found in file - The warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''." warning.config.resource_pack.item_model.already_exist: "Failed to generate item model for '' because the file '' already exists." warning.config.resource_pack.model.generation.already_exist: "Failed to generate model because the model file '' already exists." +warning.config.resource_pack.generation.malformatted_json: "Json file '' is malformatted." warning.config.resource_pack.generation.missing_font_texture: "Font '' is missing required texture: ''" -warning.config.resource_pack.generation.texture_not_in_atlas: "Texture '' is not listed in the atlas. You need to add the texture path to the atlas or enable 'obfuscation' option in config.yml." warning.config.resource_pack.generation.missing_model_texture: "Model '' is missing texture ''" warning.config.resource_pack.generation.missing_item_model: "Item '' is missing model file: ''" warning.config.resource_pack.generation.missing_block_model: "Block state '' is missing model file: ''" warning.config.resource_pack.generation.missing_parent_model: "Model '' cannot find parent model: ''" -warning.config.resource_pack.generation.malformatted_json: "Json file '' is malformatted." warning.config.resource_pack.generation.missing_equipment_texture: "Equipment '' is missing texture ''" +warning.config.resource_pack.generation.texture_not_in_atlas: "Texture '' is not listed in the atlas. You need to add the texture path to the atlas or enable 'obfuscation' option in config.yml." warning.config.resource_pack.invalid_overlay_format: "Issue found in config.yml at 'resource-pack.overlay-format' - Invalid overlay format ''. Overlay format must contain the placeholder '{version}'." warning.config.equipment.duplicate: "Issue found in file - Duplicated equipment ''. Please check if there is the same configuration in other files." warning.config.equipment.missing_type: "Issue found in file - The equipment '' is missing the required 'type' argument." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 348ab6b22..239100483 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -83,6 +83,8 @@ warning.config.number.fixed.invalid_value: "在文件 发现问 warning.config.number.expression.missing_expression: "在文件 发现问题 - 配置项 '' 缺少 'expression' 数字类型所需的 'expression' 参数" warning.config.number.uniform.missing_min: "在文件 发现问题 - 配置项 '' 缺少 'uniform' 数字类型所需的 'min' 参数" warning.config.number.uniform.missing_max: "在文件 发现问题 - 配置项 '' 缺少 'uniform' 数字类型所需的 'max' 参数" +warning.config.number.gaussian.missing_min: "在文件 发现问题 - 配置项 '' 缺少 'gaussian' 数字类型所需的 'min' 参数" +warning.config.number.gaussian.missing_max: "在文件 发现问题 - 配置项 '' 缺少 'gaussian' 数字类型所需的 'max' 参数" warning.config.condition.all_of.missing_terms: "在文件 发现问题 - 配置项 '' 缺少 'all_of' 条件所需的 'terms' 参数" warning.config.condition.all_of.invalid_terms_type: "在文件 发现问题 - 配置项 '' 的 'all_of' 条件配置错误, 'terms' 应为映射列表, 当前类型: ''" warning.config.condition.any_of.missing_terms: "在文件 发现问题 - 配置项 '' 缺少 'any_of' 条件所需的 'terms' 参数" @@ -92,6 +94,8 @@ warning.config.condition.inverted.invalid_term_type: "在文件 warning.config.condition.enchantment.missing_predicate: "在文件 发现问题 - 配置项 '' 缺少 'enchantment' 条件所需的 'predicate' 参数" warning.config.condition.enchantment.invalid_predicate: "在文件 发现问题 - 配置项 '' 使用了无效的附魔 'predicate' 参数 ''" warning.config.condition.match_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'match_block_property' 条件所需的 'properties' 参数" +warning.config.condition.match_block.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_block' 条件所需的 'id' 参数" +warning.config.condition.match_entity.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_entity' 条件所需的 'id' 参数" warning.config.condition.match_item.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_item' 条件所需的 'id' 参数" warning.config.condition.table_bonus.missing_enchantment: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'enchantment' 参数" warning.config.condition.table_bonus.missing_chances: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'chances' 参数" @@ -302,6 +306,7 @@ warning.config.block.behavior.stairs.missing_shape: "在文件 warning.config.block.behavior.pressure_plate.missing_powered: "在文件 发现问题 - 方块 '' 的 'pressure_plate_block' 行为缺少必需的 'powered' 属性" warning.config.block.behavior.grass.missing_feature: "在文件 发现问题 - 方块 '' 的 'grass_block' 行为缺少必需的 'feature' 参数" warning.config.block.behavior.double_high.missing_half: "在文件 发现问题 - 方块 '' 的 'double_block' 行为缺少必需的 'half' 属性" +warning.config.block.behavior.change_over_time.missing_next_block: "在文件 发现问题 - 方块 '' 的 'change_over_time_block' 行为缺少必需的 'next-block' 配置项" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" @@ -411,14 +416,14 @@ warning.config.selector.invalid_type: "在文件 中发现问题 warning.config.selector.invalid_target: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器目标 ''" warning.config.resource_pack.item_model.already_exist: "无法为 '' 生成物品模型,因为文件 '' 已存在" warning.config.resource_pack.model.generation.already_exist: "无法生成模型,因为模型文件 '' 已存在" +warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误" warning.config.resource_pack.generation.missing_font_texture: "字体''缺少必要纹理: ''" warning.config.resource_pack.generation.missing_model_texture: "模型''缺少纹理''" -warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'obfuscation' 选项" warning.config.resource_pack.generation.missing_item_model: "物品''缺少模型文件: ''" warning.config.resource_pack.generation.missing_block_model: "方块状态''缺少模型文件: ''" warning.config.resource_pack.generation.missing_parent_model: "模型''找不到父级模型文件: ''" -warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误" warning.config.resource_pack.generation.missing_equipment_texture: "装备 '' 缺少纹理 ''" +warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'obfuscation' 选项" warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 ''. Overlay格式必须包含占位符 '{version}'" warning.config.equipment.duplicate: "在文件 发现问题 - 重复的装备配置 ''。请检查其他文件中是否存在相同配置" warning.config.equipment.missing_type: "在文件 发现问题 - 装备 '' 缺少必需的 'type' 参数" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index df535d1cd..0694d2128 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { implementation("net.momirealms:sparrow-nbt-codec:${rootProject.properties["sparrow_nbt_version"]}") implementation("net.momirealms:sparrow-nbt-legacy-codec:${rootProject.properties["sparrow_nbt_version"]}") // S3 - implementation("net.momirealms:craft-engine-s3:0.4") + implementation("net.momirealms:craft-engine-s3:0.5") // Util compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") // Adventure @@ -65,6 +65,8 @@ dependencies { compileOnly("com.mojang:brigadier:${rootProject.properties["mojang_brigadier_version"]}") // authlib compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") + // concurrentutil + compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}") } java { @@ -99,6 +101,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java b/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java index 93924daf9..b2e22857d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java +++ b/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java @@ -6,11 +6,11 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.Tag; import java.util.Map; import java.util.Optional; @@ -51,6 +51,16 @@ public class AdvancementDisplay { public void applyClientboundData(Player player) { this.icon = CraftEngine.instance().itemManager().s2c(this.icon, player); + if (Config.interceptAdvancement()) { + Map tokens1 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.title)); + if (!tokens1.isEmpty()) { + this.title = AdventureHelper.replaceText(this.title, tokens1, NetworkTextReplaceContext.of(player)); + } + Map tokens2 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.description)); + if (!tokens2.isEmpty()) { + this.description = AdventureHelper.replaceText(this.description, tokens2, NetworkTextReplaceContext.of(player)); + } + } } public void write(FriendlyByteBuf buf) { @@ -75,8 +85,8 @@ public class AdvancementDisplay { } public static AdvancementDisplay read(FriendlyByteBuf buf) { - Component title = readComponent(buf); - Component description = readComponent(buf); + Component title = buf.readComponent(); + Component description = buf.readComponent(); Item icon = CraftEngine.instance().itemManager().decode(buf); AdvancementType type = AdvancementType.byId(buf.readVarInt()); int flags = buf.readInt(); @@ -88,28 +98,4 @@ public class AdvancementDisplay { float y = buf.readFloat(); return new AdvancementDisplay(title, description, icon, background, type, showToast, hidden, x, y); } - - private static Component readComponent(FriendlyByteBuf buf) { - if (Config.interceptAdvancement()) { - if (VersionHelper.isOrAbove1_20_3()) { - Tag nbt = buf.readNbt(false); - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - Component component = AdventureHelper.nbtToComponent(nbt); - if (!tokens.isEmpty()) { - component = AdventureHelper.replaceText(component, tokens); - } - return component; - } else { - String json = buf.readUtf(); - Component component = AdventureHelper.jsonToComponent(json); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (!tokens.isEmpty()) { - component = AdventureHelper.replaceText(component, tokens); - } - return component; - } - } else { - return buf.readComponent(); - } - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index 8b1f9e59d..ce78d8bf1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -324,6 +324,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 结合variants JsonElement combinedVariant = GsonHelper.combine(variants); Map overrideMap = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()); + AbstractBlockManager.this.tempVanillaBlockStateModels.put(registryId, combinedVariant); JsonElement previous = overrideMap.get(propertyNBT); if (previous != null && !previous.equals(combinedVariant)) { throw new LocalizedResourceConfigException("warning.config.block.state.model.conflict", GsonHelper.get().toJson(combinedVariant), blockState, GsonHelper.get().toJson(previous)); @@ -397,8 +398,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } } else { // 其他情况则是完整的方块 - BlockStateWrapper packedBlockState = createPackedBlockState(blockState); - if (packedBlockState == null || !packedBlockState.isVanillaBlock()) { + BlockStateWrapper packedBlockState = createBlockState(blockState); + if (packedBlockState == null) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); } registryId = packedBlockState.registryId(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java index e524b86c9..9a965a5ec 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.block; import com.google.common.collect.ImmutableMap; import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.parser.BlockNbtParser; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; @@ -58,6 +59,8 @@ public abstract class AbstractCustomBlock implements CustomBlock { placements.add(Property.createStateForPlacement(propertyEntry.getKey(), propertyEntry.getValue())); } this.placementFunction = composite(placements); + EntityBlockBehavior entityBlockBehavior = this.behavior.getEntityBehavior(); + boolean isEntityBlock = entityBlockBehavior != null; for (Map.Entry entry : variantMapper.entrySet()) { String nbtString = entry.getKey(); CompoundTag tag = BlockNbtParser.deserialize(this, nbtString); @@ -77,15 +80,19 @@ public abstract class AbstractCustomBlock implements CustomBlock { // Late init states ImmutableBlockState state = possibleStates.getFirst(); state.setSettings(blockStateVariant.settings()); - state.setVanillaBlockState((BlockStateWrapper.VanillaBlockState) BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); - state.setCustomBlockState((BlockStateWrapper.CustomBlockState) BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); + state.setVanillaBlockState(BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); + state.setCustomBlockState(BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); } + // double check if there's any invalid state for (ImmutableBlockState state : this.variantProvider().states()) { state.setBehavior(this.behavior); if (state.settings() == null) { state.setSettings(settings); } + if (isEntityBlock) { + state.setBlockEntityType(entityBlockBehavior.blockEntityType()); + } } this.applyPlatformSettings(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index 4af326d66..5536e4322 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.block; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; @@ -10,6 +11,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.util.Optional; import java.util.concurrent.Callable; @@ -24,6 +26,14 @@ public abstract class BlockBehavior { return Optional.empty(); } + @Nullable + public EntityBlockBehavior getEntityBehavior() { + if (this instanceof EntityBlockBehavior behavior) { + return behavior; + } + return null; + } + // BlockState state, Rotation rotation public Object rotate(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return superMethod.call(); @@ -85,6 +95,21 @@ public abstract class BlockBehavior { return false; } + //BlockState state + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return false; + } + + //BlockState state, Level level, BlockPos pos + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return 0; + } + + // BlockState state, LevelReader world, BlockPos pos + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + return null; + } + // Level level, RandomSource random, BlockPos pos, BlockState state public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception { return false; @@ -186,4 +211,4 @@ public abstract class BlockBehavior { } public abstract CustomBlock block(); -} +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java index 6f5daecc9..c6b9cd591 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java @@ -8,6 +8,7 @@ public final class BlockKeys { public static final Key NOTE_BLOCK = Key.of("minecraft:note_block"); public static final Key TRIPWIRE = Key.of("minecraft:tripwire"); public static final Key CRAFTING_TABLE = Key.of("minecraft:crafting_table"); + public static final Key CARTOGRAPHY_TABLE = Key.of("minecraft:cartography_table"); public static final Key STONECUTTER = Key.of("minecraft:stonecutter"); public static final Key BELL = Key.of("minecraft:bell"); public static final Key SMITHING_TABLE = Key.of("minecraft:smithing_table"); @@ -176,8 +177,6 @@ public final class BlockKeys { public static final Key WAXED_OXIDIZED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_oxidized_copper_trapdoor"); public static final Key WAXED_WEATHERED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_weathered_copper_trapdoor"); - - public static final Key OAK_FENCE_GATE = Key.of("minecraft:oak_fence_gate"); public static final Key SPRUCE_FENCE_GATE = Key.of("minecraft:spruce_fence_gate"); public static final Key BIRCH_FENCE_GATE = Key.of("minecraft:birch_fence_gate"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java index 6b69f04ee..1f6568636 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java @@ -43,5 +43,5 @@ public interface BlockManager extends Manageable, ModelGenerator { ImmutableBlockState getImmutableBlockState(int stateId); @Nullable - BlockStateWrapper createPackedBlockState(String blockState); + BlockStateWrapper createBlockState(String blockState); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index 8abe2a871..e2e78a9a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -38,6 +38,9 @@ public class BlockSettings { LazyReference> correctTools = LazyReference.lazyReference(Set::of); String name; String supportShapeBlockState; + float friction = 0.6f; + float speedFactor = 1f; + float jumpFactor = 1f; private BlockSettings() {} @@ -101,6 +104,9 @@ public class BlockSettings { newSettings.supportShapeBlockState = settings.supportShapeBlockState; newSettings.propagatesSkylightDown = settings.propagatesSkylightDown; newSettings.useShapeForLightOcclusion = settings.useShapeForLightOcclusion; + newSettings.speedFactor = settings.speedFactor; + newSettings.jumpFactor = settings.jumpFactor; + newSettings.friction = settings.friction; return newSettings; } @@ -140,6 +146,18 @@ public class BlockSettings { return hardness; } + public float friction() { + return friction; + } + + public float jumpFactor() { + return jumpFactor; + } + + public float speedFactor() { + return speedFactor; + } + public Tristate canOcclude() { return canOcclude; } @@ -236,6 +254,21 @@ public class BlockSettings { return this; } + public BlockSettings friction(float friction) { + this.friction = friction; + return this; + } + + public BlockSettings speedFactor(float speedFactor) { + this.speedFactor = speedFactor; + return this; + } + + public BlockSettings jumpFactor(float jumpFactor) { + this.jumpFactor = jumpFactor; + return this; + } + public BlockSettings tags(Set tags) { this.tags = tags; return this; @@ -384,6 +417,18 @@ public class BlockSettings { float floatValue = ResourceConfigUtils.getAsFloat(value, "hardness"); return settings -> settings.hardness(floatValue); })); + registerFactory("friction", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "friction"); + return settings -> settings.friction(floatValue); + })); + registerFactory("speed-factor", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "speed-factor"); + return settings -> settings.speedFactor(floatValue); + })); + registerFactory("jump-factor", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "jump-factor"); + return settings -> settings.jumpFactor(floatValue); + })); registerFactory("resistance", (value -> { float floatValue = ResourceConfigUtils.getAsFloat(value, "resistance"); return settings -> settings.resistance(floatValue); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java index f019602f6..fb880292e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java @@ -2,71 +2,7 @@ package net.momirealms.craftengine.core.block; public interface BlockStateWrapper { - Object handle(); + Object literalObject(); int registryId(); - - boolean isVanillaBlock(); - - static BlockStateWrapper vanilla(Object handle, int registryId) { - return new VanillaBlockState(handle, registryId); - } - - static BlockStateWrapper custom(Object handle, int registryId) { - return new CustomBlockState(handle, registryId); - } - - static BlockStateWrapper create(Object handle, int registryId, boolean isVanillaBlock) { - if (isVanillaBlock) return new VanillaBlockState(handle, registryId); - else return new CustomBlockState(handle, registryId); - } - - abstract class AbstractBlockState implements BlockStateWrapper { - protected final Object handle; - protected final int registryId; - - public AbstractBlockState(Object handle, int registryId) { - this.handle = handle; - this.registryId = registryId; - } - - @Override - public Object handle() { - return this.handle; - } - - @Override - public int registryId() { - return this.registryId; - } - } - - class VanillaBlockState extends AbstractBlockState { - - public VanillaBlockState(Object handle, int registryId) { - super(handle, registryId); - } - - @Override - public boolean isVanillaBlock() { - return true; - } - } - - class CustomBlockState extends AbstractBlockState { - - public CustomBlockState(Object handle, int registryId) { - super(handle, registryId); - } - - @Override - public DelegatingBlockState handle() { - return (DelegatingBlockState) super.handle(); - } - - @Override - public boolean isVanillaBlock() { - return false; - } - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index c40700c12..13948d536 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -19,11 +19,13 @@ public interface CustomBlock { Key id(); - @Nullable LootTable lootTable(); + @Nullable + LootTable lootTable(); void execute(PlayerOptionalContext context, EventTrigger trigger); - @NotNull BlockStateVariantProvider variantProvider(); + @NotNull + BlockStateVariantProvider variantProvider(); List getPossibleStates(CompoundTag nbt); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index 5c27421bf..cfa5ebb2d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -1,6 +1,10 @@ package net.momirealms.craftengine.core.block; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; @@ -8,6 +12,7 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; @@ -19,11 +24,12 @@ import java.util.List; public final class ImmutableBlockState extends BlockStateHolder { private CompoundTag tag; - private BlockStateWrapper.CustomBlockState customBlockState; - private BlockStateWrapper.VanillaBlockState vanillaBlockState; + private BlockStateWrapper customBlockState; + private BlockStateWrapper vanillaBlockState; private BlockBehavior behavior; private Integer hashCode; private BlockSettings settings; + private BlockEntityType blockEntityType; ImmutableBlockState( Holder owner, @@ -48,6 +54,14 @@ public final class ImmutableBlockState extends BlockStateHolder { this.settings = settings; } + public BlockEntityType blockEntityType() { + return blockEntityType; + } + + public void setBlockEntityType(BlockEntityType blockEntityType) { + this.blockEntityType = blockEntityType; + } + public boolean isEmpty() { return this == EmptyBlock.STATE; } @@ -67,6 +81,10 @@ public final class ImmutableBlockState extends BlockStateHolder { return this.hashCode; } + public boolean hasBlockEntity() { + return this.blockEntityType != null; + } + public BlockStateWrapper customBlockState() { return this.customBlockState; } @@ -75,11 +93,11 @@ public final class ImmutableBlockState extends BlockStateHolder { return this.vanillaBlockState; } - public void setCustomBlockState(@NotNull BlockStateWrapper.CustomBlockState customBlockState) { + public void setCustomBlockState(@NotNull BlockStateWrapper customBlockState) { this.customBlockState = customBlockState; } - public void setVanillaBlockState(@NotNull BlockStateWrapper.VanillaBlockState vanillaBlockState) { + public void setVanillaBlockState(@NotNull BlockStateWrapper vanillaBlockState) { this.vanillaBlockState = vanillaBlockState; } @@ -132,4 +150,11 @@ public final class ImmutableBlockState extends BlockStateHolder { if (lootTable == null) return List.of(); return lootTable.getRandomItems(builder.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, this).build(), world, player); } + + @SuppressWarnings("unchecked") + public BlockEntityTicker createBlockEntityTicker(CEWorld world, BlockEntityType type) { + EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); + if (blockBehavior == null) return null; + return (BlockEntityTicker) blockBehavior.createBlockEntityTicker(world, this, type); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java new file mode 100644 index 000000000..2c27e91fd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java @@ -0,0 +1,19 @@ +package net.momirealms.craftengine.core.block.behavior; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; + +public interface EntityBlockBehavior { + + BlockEntityType blockEntityType(); + + BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state); + + default BlockEntityTicker createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java new file mode 100644 index 000000000..566a75f08 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java @@ -0,0 +1,107 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.sparrow.nbt.CompoundTag; + +public abstract class BlockEntity { + protected final BlockPos pos; + protected ImmutableBlockState blockState; + protected BlockEntityType type; + protected CEWorld world; + protected boolean valid; + + protected BlockEntity(BlockEntityType type, BlockPos pos, ImmutableBlockState blockState) { + this.pos = pos; + this.blockState = blockState; + this.type = type; + } + + public final CompoundTag saveAsTag() { + CompoundTag tag = new CompoundTag(); + this.saveId(tag); + this.savePos(tag); + this.saveCustomData(tag); + return tag; + } + + private void saveId(CompoundTag tag) { + tag.putString("id", this.type.id().asString()); + } + + public void setBlockState(ImmutableBlockState blockState) { + this.blockState = blockState; + } + + public ImmutableBlockState blockState() { + return blockState; + } + + public CEWorld world() { + return world; + } + + public void setWorld(CEWorld world) { + this.world = world; + } + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + private void savePos(CompoundTag tag) { + tag.putInt("x", this.pos.x()); + tag.putInt("y", this.pos.y()); + tag.putInt("z", this.pos.z()); + } + + protected void saveCustomData(CompoundTag tag) { + } + + public void loadCustomData(CompoundTag tag) { + } + + public void preRemove() { + } + + public static BlockPos readPos(CompoundTag tag) { + return new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); + } + + public BlockEntityType type() { + return type; + } + + public static BlockPos readPosAndVerify(CompoundTag tag, ChunkPos chunkPos) { + int x = tag.getInt("x", 0); + int y = tag.getInt("y", 0); + int z = tag.getInt("z", 0); + int sectionX = SectionPos.blockToSectionCoord(x); + int sectionZ = SectionPos.blockToSectionCoord(z); + if (sectionX != chunkPos.x || sectionZ != chunkPos.z) { + x = chunkPos.x * 16 + SectionPos.sectionRelative(x); + z = chunkPos.z * 16 + SectionPos.sectionRelative(z); + } + return new BlockPos(x, y, z); + } + + public BlockPos pos() { + return pos; + } + + public boolean isValidBlockState(ImmutableBlockState blockState) { + return this.type == blockState.blockEntityType(); + } + + public interface Factory { + + T create(BlockPos pos, ImmutableBlockState state); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java new file mode 100644 index 000000000..4291b6de5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.util.Key; + +public record BlockEntityType(Key id, BlockEntity.Factory factory) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java new file mode 100644 index 000000000..1f90b42c5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.block.entity; + +public class BlockEntityTypes { + +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java new file mode 100644 index 000000000..6b5f57399 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; + +public interface BlockEntityTicker { + + void tick(CEWorld world, BlockPos pos, ImmutableBlockState state, T blockEntity); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java new file mode 100644 index 000000000..e85353685 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public class DummyTickingBlockEntity implements TickingBlockEntity { + public static final DummyTickingBlockEntity INSTANCE = new DummyTickingBlockEntity(); + + @Override + public boolean isValid() { + return false; + } + + @Override + public void tick() { + } + + @Override + public BlockPos pos() { + return BlockPos.ZERO; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java new file mode 100644 index 000000000..74ba19c4d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public class ReplaceableTickingBlockEntity implements TickingBlockEntity { + private TickingBlockEntity target; + + public ReplaceableTickingBlockEntity(TickingBlockEntity target) { + this.target = target; + } + + public TickingBlockEntity target() { + return target; + } + + public void setTicker(TickingBlockEntity target) { + this.target = target; + } + + @Override + public BlockPos pos() { + return this.target.pos(); + } + + @Override + public void tick() { + this.target.tick(); + } + + @Override + public boolean isValid() { + return this.target.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java new file mode 100644 index 000000000..99599744d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public interface TickingBlockEntity { + + void tick(); + + boolean isValid(); + + BlockPos pos(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java new file mode 100644 index 000000000..ee323e5a9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; + +public class TickingBlockEntityImpl implements TickingBlockEntity { + private final T blockEntity; + private final BlockEntityTicker ticker; + private final CEChunk chunk; + + public TickingBlockEntityImpl(CEChunk chunk, T blockEntity, BlockEntityTicker ticker) { + this.blockEntity = blockEntity; + this.ticker = ticker; + this.chunk = chunk; + } + + @Override + public BlockPos pos() { + return this.blockEntity.pos(); + } + + @Override + public void tick() { + // 已无效 + if (!this.isValid()) return; + // 还没加载完全 + if (this.blockEntity.world() == null) return; + BlockPos pos = pos(); + ImmutableBlockState state = this.chunk.getBlockState(pos); + // 不是合法方块 + if (!this.blockEntity.isValidBlockState(state)) { + this.chunk.removeBlockEntity(pos); + Debugger.BLOCK_ENTITY.warn(() -> "Invalid block entity(" + this.blockEntity.getClass().getSimpleName() + ") with state " + state + " found at world " + this.chunk.world().name() + " " + pos, null); + return; + } + try { + this.ticker.tick(this.chunk.world(), pos, state, this.blockEntity); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to tick block entity(" + this.blockEntity.getClass().getSimpleName() + ") at world " + this.chunk.world().name() + " " + pos, t); + } + } + + @Override + public boolean isValid() { + return this.blockEntity.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java new file mode 100644 index 000000000..a69d4e24e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.block.state; + +import java.util.Collection; + +public interface StatePropertyAccessor { + + String getPropertyValueAsString(String property); + + Collection getPropertyNames(); + + boolean hasProperty(String property); + + T getPropertyValue(String property); +} 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 956a55571..6675659ba 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 @@ -10,8 +10,6 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.BlockPos; import org.jetbrains.annotations.NotNull; -import java.util.List; - public abstract class Player extends AbstractEntity implements NetWorkUser { private static final Key TYPE = Key.of("minecraft:player"); @@ -26,10 +24,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { @Override public abstract Object serverPlayer(); - public abstract void sendPackets(List packet, boolean immediately); - - public abstract void sendPackets(List packet, boolean immediately, Runnable sendListener); - public abstract float getDestroyProgress(Object blockState, BlockPos pos); public abstract void setClientSideCanBreakBlock(boolean canBreak); @@ -52,6 +46,14 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract boolean isSneaking(); + public abstract boolean isSwimming(); + + public abstract boolean isClimbing(); + + public abstract boolean isGliding(); + + public abstract boolean isFlying(); + public abstract GameMode gameMode(); public abstract void setGameMode(GameMode gameMode); @@ -104,12 +106,12 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void unloadCurrentResourcePack(); - public abstract void performCommand(String command); + public abstract void performCommand(String command, boolean asOp); + + public abstract void performCommandAsEvent(String command); public abstract double luck(); - public abstract boolean isFlying(); - @Override public Key type() { return TYPE; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index bb4c46dd2..cc5c6cdfb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; @@ -39,9 +40,9 @@ public abstract class AbstractFontManager implements FontManager { private final EmojiParser emojiParser; private OffsetFont offsetFont; - protected Trie imageTagTrie; + protected Trie networkTagTrie; protected Trie emojiKeywordTrie; - protected Map tagMapper; + protected Map networkTagMapper; protected Map emojiMapper; protected List emojiList; protected List allEmojiSuggestions; @@ -57,6 +58,7 @@ public abstract class AbstractFontManager implements FontManager { this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("image.offset-characters")) .map(OffsetFont::new) .orElse(null); + this.networkTagMapper = new HashMap<>(1024); } @Override @@ -65,6 +67,14 @@ public abstract class AbstractFontManager implements FontManager { this.images.clear(); this.illegalChars.clear(); this.emojis.clear(); + this.networkTagTrie = null; + this.emojiKeywordTrie = null; + if (this.networkTagMapper != null) { + this.networkTagMapper.clear(); + } + if (this.emojiMapper != null) { + this.emojiMapper.clear(); + } } @Override @@ -80,7 +90,10 @@ public abstract class AbstractFontManager implements FontManager { @Override public void delayedLoad() { Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); - this.buildImageTagTrie(); + this.registerImageTags(); + this.registerShiftTags(); + this.registerGlobalTags(); + this.buildNetworkTagTrie(); this.buildEmojiKeywordsTrie(); this.emojiList = new ArrayList<>(this.emojis.values()); this.allEmojiSuggestions = this.emojis.values().stream() @@ -88,15 +101,48 @@ public abstract class AbstractFontManager implements FontManager { .collect(Collectors.toList()); } + private void registerGlobalTags() { + for (Map.Entry entry : this.plugin.globalVariableManager().globalVariables().entrySet()) { + String globalTag = globalTag(entry.getKey()); + this.networkTagMapper.put(globalTag, ComponentProvider.miniMessageOrConstant(entry.getValue())); + this.networkTagMapper.put("\\" + globalTag, ComponentProvider.constant(Component.text(entry.getValue()))); + } + } + + private void registerShiftTags() { + for (int i = -256; i <= 256; i++) { + String shiftTag = ""; + this.networkTagMapper.put(shiftTag, ComponentProvider.constant(this.offsetFont.createOffset(i))); + this.networkTagMapper.put("\\" + shiftTag, ComponentProvider.constant(Component.text(shiftTag))); + } + } + + private void registerImageTags() { + for (BitmapImage image : this.images.values()) { + String id = image.id().toString(); + String simpleImageTag = imageTag(id); + this.networkTagMapper.put(simpleImageTag, ComponentProvider.constant(image.componentAt(0, 0))); + this.networkTagMapper.put("\\" + simpleImageTag, ComponentProvider.constant(Component.text(simpleImageTag))); + for (int i = 0; i < image.rows(); i++) { + for (int j = 0; j < image.columns(); j++) { + String imageArgs = id + ":" + i + ":" + j; + String imageTag = imageTag(imageArgs); + this.networkTagMapper.put(imageTag, ComponentProvider.constant(image.componentAt(i, j))); + this.networkTagMapper.put("\\" + imageTag, ComponentProvider.constant(Component.text(imageTag))); + } + } + } + } + @Override - public Map matchTags(String json) { - if (this.imageTagTrie == null) { + public Map matchTags(String json) { + if (this.networkTagTrie == null) { return Collections.emptyMap(); } - Map tags = new HashMap<>(); - for (Token token : this.imageTagTrie.tokenize(json)) { + Map tags = new HashMap<>(); + for (Token token : this.networkTagTrie.tokenize(json)) { if (token.isMatch()) { - tags.put(token.getFragment(), this.tagMapper.get(token.getFragment())); + tags.put(token.getFragment(), this.networkTagMapper.get(token.getFragment())); } } return tags; @@ -218,9 +264,9 @@ public abstract class AbstractFontManager implements FontManager { public IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement) { boolean hasIllegal = false; // replace illegal image usage - Map tokens = matchTags(raw); + Map tokens = matchTags(raw); if (!tokens.isEmpty()) { - for (Map.Entry entry : tokens.entrySet()) { + for (Map.Entry entry : tokens.entrySet()) { raw = raw.replace(entry.getKey(), String.valueOf(replacement)); hasIllegal = true; } @@ -264,30 +310,10 @@ public abstract class AbstractFontManager implements FontManager { .build(); } - private void buildImageTagTrie() { - this.tagMapper = new HashMap<>(1024); - for (BitmapImage image : this.images.values()) { - String id = image.id().toString(); - String simpleImageTag = imageTag(id); - this.tagMapper.put(simpleImageTag, image.componentAt(0, 0)); - this.tagMapper.put("\\" + simpleImageTag, Component.text(simpleImageTag)); - for (int i = 0; i < image.rows(); i++) { - for (int j = 0; j < image.columns(); j++) { - String imageArgs = id + ":" + i + ":" + j; - String imageTag = imageTag(imageArgs); - this.tagMapper.put(imageTag, image.componentAt(i, j)); - this.tagMapper.put("\\" + imageTag, Component.text(imageTag)); - } - } - } - for (int i = -256; i <= 256; i++) { - String shiftTag = ""; - this.tagMapper.put(shiftTag, this.offsetFont.createOffset(i)); - this.tagMapper.put("\\" + shiftTag, Component.text(shiftTag)); - } - this.imageTagTrie = Trie.builder() + private void buildNetworkTagTrie() { + this.networkTagTrie = Trie.builder() .ignoreOverlaps() - .addKeywords(this.tagMapper.keySet()) + .addKeywords(this.networkTagMapper.keySet()) .build(); } @@ -295,6 +321,10 @@ public abstract class AbstractFontManager implements FontManager { return ""; } + private static String globalTag(String text) { + return ""; + } + @Override public boolean isDefaultFontInUse() { return !this.illegalChars.isEmpty(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 307350eea..aca5c9dd6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; @@ -103,7 +104,7 @@ public interface FontManager extends Manageable { return createOffsets(offset, (raw, font) -> raw); } - Map matchTags(String json); + Map matchTags(String json); void refreshEmojiSuggestions(UUID uuid); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java index fc1ed038b..63298239f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java @@ -105,7 +105,11 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif @Override public Item apply(Item item, ItemBuildContext context) { - return item.attributeModifiers(this.modifiers.stream().map(it -> it.toAttributeModifier(context)).toList()); + List results = new ArrayList<>(this.modifiers.size()); + for (PreModifier modifier : this.modifiers) { + results.add(modifier.toAttributeModifier(item, context)); + } + return item.attributeModifiers(results); } @Override @@ -125,12 +129,12 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif public record PreModifier(String type, AttributeModifier.Slot slot, - Key id, + Optional id, NumberProvider amount, AttributeModifier.Operation operation, AttributeModifiersModifier.PreModifier.@Nullable PreDisplay display) { - public PreModifier(String type, AttributeModifier.Slot slot, Key id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) { + public PreModifier(String type, AttributeModifier.Slot slot, Optional id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) { this.amount = amount; this.type = type; this.slot = slot; @@ -139,8 +143,9 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif this.display = display; } - public AttributeModifier toAttributeModifier(ItemBuildContext context) { - return new AttributeModifier(type, slot, id, amount.getDouble(context), operation, display == null ? null : display.toDisplay(context)); + public AttributeModifier toAttributeModifier(Item item, ItemBuildContext context) { + return new AttributeModifier(this.type, this.slot, this.id.orElseGet(() -> Key.of("craftengine", UUID.randomUUID().toString())), + this.amount.getDouble(context), this.operation, this.display == null ? null : this.display.toDisplay(context)); } public record PreDisplay(AttributeModifier.Display.Type type, String value) { @@ -155,15 +160,11 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif @Override public ItemDataModifier create(Object arg) { - MutableInt mutableInt = new MutableInt(0); List attributeModifiers = ResourceConfigUtils.parseConfigAsList(arg, (map) -> { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type"); Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type)); AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH)); - Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> { - mutableInt.add(1); - return Key.of("craftengine", "modifier_" + mutableInt.intValue()); - }); + Optional id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of); NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount")); AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf( ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH) diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java index 2b6672de8..82eafdef4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java @@ -45,6 +45,7 @@ public final class ItemDataModifiers { public static final Key UNBREAKABLE = Key.of("craftengine:unbreakable"); public static final Key DYNAMIC_LORE = Key.of("craftengine:dynamic-lore"); public static final Key OVERWRITABLE_LORE = Key.of("craftengine:overwritable-lore"); + public static final Key MAX_DAMAGE = Key.of("craftengine:max-damage"); public static void register(Key key, ItemDataModifierFactory factory) { ((WritableRegistry>) BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY) @@ -83,6 +84,7 @@ public final class ItemDataModifiers { register(COMPONENTS, ComponentsModifier.FACTORY); register(REMOVE_COMPONENTS, RemoveComponentModifier.FACTORY); register(FOOD, FoodModifier.FACTORY); + register(MAX_DAMAGE, MaxDamageModifier.FACTORY); } else { register(CUSTOM_NAME, CustomNameModifier.FACTORY); register(ITEM_NAME, CustomNameModifier.FACTORY); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java new file mode 100644 index 000000000..842e2892b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java @@ -0,0 +1,44 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ComponentKeys; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; + +public class MaxDamageModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + private final NumberProvider argument; + + public MaxDamageModifier(NumberProvider argument) { + this.argument = argument; + } + + @Override + public Key type() { + return ItemDataModifiers.MAX_DAMAGE; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.maxDamage(argument.getInt(context)); + return item; + } + + @Override + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.MAX_DAMAGE; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + NumberProvider numberProvider = NumberProviders.fromObject(arg); + return new MaxDamageModifier<>(numberProvider); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java index 3087306b1..d6a3460f5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java @@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.item.recipe.result.PostProcessors; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -61,9 +62,10 @@ public abstract class AbstractRecipeSerializer> implement return recipeCategory; } + @NotNull @SuppressWarnings({"unchecked"}) protected CustomRecipeResult parseResult(Map arguments) { - Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); + Map resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("result"), "result"); if (resultMap == null) { throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); } @@ -81,6 +83,27 @@ public abstract class AbstractRecipeSerializer> implement ); } + @Nullable + @SuppressWarnings({"unchecked"}) + protected CustomRecipeResult parseVisualResult(Map arguments) { + Map resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("visual-result"), "visual-result"); + if (resultMap == null) { + return null; + } + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); + int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); + BuildableItem resultItem = (BuildableItem) CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)); + if (resultItem.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id); + } + List> processors = ResourceConfigUtils.parseConfigAsList(resultMap.get("post-processors"), PostProcessors::fromMap); + return new CustomRecipeResult<>( + resultItem, + count, + processors.isEmpty() ? null : processors.toArray(new PostProcessor[0]) + ); + } + @SuppressWarnings("unchecked") protected CustomRecipeResult parseResult(DatapackRecipeResult recipeResult) { Item result = (Item) CraftEngine.instance().itemManager().build(recipeResult); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java index 2adf21dc3..655f40ea4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java @@ -1,14 +1,20 @@ package net.momirealms.craftengine.core.item.recipe; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe { protected final CraftingRecipeCategory category; + private final CustomRecipeResult visualResult; - protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult result, String group, CraftingRecipeCategory category) { + protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult result, @Nullable CustomRecipeResult visualResult, String group, CraftingRecipeCategory category) { super(id, showNotification, result, group); this.category = category == null ? CraftingRecipeCategory.MISC : category; + this.visualResult = visualResult; } public CraftingRecipeCategory category() { @@ -19,4 +25,28 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe public RecipeType type() { return RecipeType.CRAFTING; } + + public CustomRecipeResult visualResult() { + return visualResult; + } + + public boolean hasVisualResult() { + return visualResult != null; + } + + public T assembleVisual(RecipeInput input, ItemBuildContext context) { + if (this.visualResult != null) { + return this.visualResult.buildItemStack(context); + } else { + throw new IllegalStateException("No visual result available"); + } + } + + public Item buildVisualOrActualResult(ItemBuildContext context) { + if (this.visualResult != null) { + return this.visualResult.buildItem(context); + } else { + return super.result.buildItem(context); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index 70faa1450..40af56a0f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -21,10 +21,11 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { public CustomShapedRecipe(Key id, boolean showNotification, CustomRecipeResult result, + CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, Pattern pattern) { - super(id, showNotification, result, group, category); + super(id, showNotification, result, visualResult, group, category); this.pattern = pattern; this.parsedPattern = pattern.parse(); } @@ -165,7 +166,9 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } return new CustomShapedRecipe(id, showNotification(arguments), - parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), + parseResult(arguments), + parseVisualResult(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), new Pattern<>(pattern.toArray(new String[0]), ingredients) ); } @@ -175,7 +178,10 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { Map> ingredients = Maps.transformValues(VANILLA_RECIPE_HELPER.shapedIngredientMap(json.getAsJsonObject("key")), this::toIngredient); return new CustomShapedRecipe<>(id, true, - parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), + null, + VANILLA_RECIPE_HELPER.readGroup(json), + VANILLA_RECIPE_HELPER.craftingCategory(json), new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients) ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index d8ca669ef..678c75543 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -20,10 +20,11 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { public CustomShapelessRecipe(Key id, boolean showNotification, CustomRecipeResult result, + CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, List> ingredients) { - super(id, showNotification, result, group, category); + super(id, showNotification, result, visualResult, group, category); this.ingredients = ingredients; this.placementInfo = PlacementInfo.create(ingredients); } @@ -84,7 +85,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { } return new CustomShapelessRecipe(id, showNotification(arguments), - parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), + parseResult(arguments), + parseVisualResult(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), ingredients ); } @@ -93,7 +96,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { public CustomShapelessRecipe readJson(Key id, JsonObject json) { return new CustomShapelessRecipe<>(id, true, - parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), + null, + VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList() ); } 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 bbb5aed75..9ebe583a7 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 @@ -214,7 +214,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip for (Key component : this.components) { Object componentObj = item1.getExactComponent(component); if (componentObj != null) { - item3.setComponent(component, componentObj); + item3.setExactComponent(component, componentObj); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java index 7e60308d1..9594a764e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java @@ -19,7 +19,7 @@ public class UniqueIdItem { @NotNull public UniqueKey id() { - return uniqueId; + return this.uniqueId; } @NotNull @@ -37,6 +37,6 @@ public class UniqueIdItem { @Override public String toString() { - return "UniqueIdItem[" + "uniqueId=" + uniqueId + ", item=" + rawItem.getItem() + ']'; + return "UniqueIdItem[" + "uniqueId=" + this.uniqueId + ", item=" + this.rawItem.getItem() + ']'; } } 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 c8c09139c..f68ae92f7 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 @@ -42,6 +42,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.scanner.ScannerException; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -125,6 +126,7 @@ public abstract class AbstractPackManager implements PackManager { loadInternalList("textures", "", VANILLA_TEXTURES::add); VANILLA_MODELS.add(Key.of("minecraft", "builtin/entity")); + VANILLA_MODELS.add(Key.of("minecraft", "item/player_head")); for (int i = 0; i < 256; i++) { VANILLA_TEXTURES.add(Key.of("minecraft", "font/unicode_page_" + String.format("%02x", i))); } @@ -542,6 +544,19 @@ public abstract class AbstractPackManager implements PackManager { } catch (IOException e) { AbstractPackManager.this.plugin.logger().severe("Error while reading config file: " + path, e); return FileVisitResult.CONTINUE; + } catch (ScannerException e) { + if (e.getMessage() != null && e.getMessage().contains("TAB") && e.getMessage().contains("indentation")) { + try { + String content = Files.readString(path); + content = content.replace("\t", " "); + Files.writeString(path, content); + } catch (Exception ex) { + AbstractPackManager.this.plugin.logger().severe("Failed to fix tab indentation in config file: " + path, ex); + } + } else { + AbstractPackManager.this.plugin.logger().severe("Error found while reading config file: " + path, e); + } + return FileVisitResult.CONTINUE; } catch (LocalizedException e) { e.setArgument(0, path.toString()); TranslationManager.instance().log(e.node(), e.arguments()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java index 275521957..22a0dfc37 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.host.impl; +import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.host.ResourcePackHost; @@ -99,9 +100,9 @@ public class LobFileHost implements ResourcePackHost { public String getSpaceUsageText() { if (this.accountInfo == null) return "Usage data not available"; return String.format("Storage: %d/%d MB (%.1f%% used)", - this.accountInfo.getSpaceUsed() / 1_000_000, - this.accountInfo.getSpaceQuota() / 1_000_000, - (this.accountInfo.getSpaceUsed() * 100.0) / this.accountInfo.getSpaceQuota() + this.accountInfo.account.usage.spaceUsed / 1_000_000, + this.accountInfo.account.limits.spaceQuota / 1_000_000, + (this.accountInfo.account.usage.spaceUsed * 100.0) / this.accountInfo.account.limits.spaceQuota ); } @@ -172,7 +173,7 @@ public class LobFileHost implements ResourcePackHost { .thenApply(response -> { if (response.statusCode() == 200) { AccountInfo info = GsonHelper.get().fromJson(response.body(), AccountInfo.class); - if (info.isSuccess()) { + if (info.success) { this.accountInfo = info; return info; } @@ -278,31 +279,33 @@ public class LobFileHost implements ResourcePackHost { } } - @SuppressWarnings({"all"}) - public static class AccountInfo { - private boolean success; - private Map account_info; - private Map account_limits; - private Map account_usage; + public record AccountInfo( + boolean success, + Account account + ) {} - public String getEmail() { - return (String) this.account_info.get("email"); - } + public record Account( + Info info, + Limits limits, + Usage usage + ) {} - public int getSpaceQuota() { - return this.account_limits.getOrDefault("space_quota", 0); - } + public record Info( + String email, + String level, + @SerializedName("api_key") String apiKey, + @SerializedName("time_created") String timeCreated + ) {} - public int getSpaceUsed() { - return this.account_usage.getOrDefault("space_used", 0); - } + public record Limits( + @SerializedName("space_quota") long spaceQuota, + @SerializedName("slots_quota") long slotsQuota, + @SerializedName("max_file_size") long maxFileSize, + @SerializedName("max_file_download_speed") long maxFileDownloadSpeed + ) {} - public int getSlotsUsed() { - return this.account_usage.getOrDefault("slots_used", 0); - } - - public boolean isSuccess() { - return this.success; - } - } + public record Usage( + @SerializedName("space_used") long spaceUsed, + @SerializedName("slots_used") long slotsUsed + ) {} } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java index adebeba89..9056ed3cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java @@ -5,4 +5,8 @@ public class ResourcePackGenerationException extends RuntimeException { public ResourcePackGenerationException(String message) { super(message); } + + public ResourcePackGenerationException(String message, Throwable cause) { + super(message, cause); + } } 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 a78856e6f..aa5eea243 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 @@ -51,7 +51,8 @@ public abstract class CraftEngine implements Plugin { protected PluginLogger logger; protected Config config; protected Platform platform; - protected ClassPathAppender classPathAppender; + protected ClassPathAppender sharedClassPathAppender; + protected ClassPathAppender privateClassPathAppender; protected DependencyManager dependencyManager; protected SchedulerAdapter scheduler; protected NetworkManager networkManager; @@ -338,8 +339,13 @@ public abstract class CraftEngine implements Plugin { } @Override - public ClassPathAppender classPathAppender() { - return classPathAppender; + public ClassPathAppender sharedClassPathAppender() { + return sharedClassPathAppender; + } + + @Override + public ClassPathAppender privateClassPathAppender() { + return privateClassPathAppender; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java index 3521c3914..1813c2bd8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java @@ -35,7 +35,9 @@ public interface Plugin { PluginLogger logger(); - ClassPathAppender classPathAppender(); + ClassPathAppender sharedClassPathAppender(); + + ClassPathAppender privateClassPathAppender(); File dataFolderFile(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 6c1fe4b11..f87c745e8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -96,11 +96,8 @@ public class Config { protected Path resource_pack$delivery$file_to_upload; protected Component resource_pack$send$prompt; - protected int performance$max_note_block_chain_update_limit; - protected int performance$max_tripwire_chain_update_limit; - protected int performance$max_emojis_per_parse; - protected boolean light_system$force_update_light; + protected boolean light_system$async_update; protected boolean light_system$enable; protected int chunk_system$compression_method; @@ -110,9 +107,11 @@ public class Config { protected boolean chunk_system$cache_system; protected boolean chunk_system$injection$use_fast_method; protected boolean chunk_system$injection$target; + protected boolean chunk_system$process_invalid_furniture$enable; + protected Map chunk_system$process_invalid_furniture$mapping; + protected boolean chunk_system$process_invalid_blocks$enable; + protected Map chunk_system$process_invalid_blocks$mapping; - protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable; - protected Map furniture$handle_invalid_furniture_on_chunk_load$mapping; protected boolean furniture$hide_base_entity; protected ColliderType furniture$collision_entity_type; @@ -122,6 +121,7 @@ public class Config { protected boolean block$predict_breaking; protected int block$predict_breaking_interval; protected double block$extended_interaction_range; + protected boolean block$chunk_relighter; protected boolean recipe$enable; protected boolean recipe$disable_vanilla_recipes$all; @@ -161,10 +161,11 @@ public class Config { protected Key equipment$sacrificed_vanilla_armor$humanoid; protected Key equipment$sacrificed_vanilla_armor$humanoid_leggings; - protected boolean emoji$chat; - protected boolean emoji$book; - protected boolean emoji$anvil; - protected boolean emoji$sign; + protected boolean emoji$contexts$chat; + protected boolean emoji$contexts$book; + protected boolean emoji$contexts$anvil; + protected boolean emoji$contexts$sign; + protected int emoji$max_emojis_per_parse; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -225,6 +226,8 @@ public class Config { .builder() .setVersioning(new BasicVersioning("config-version")) .addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.') .build()); } try { @@ -285,7 +288,7 @@ public class Config { resource_pack$protection$obfuscation$resource_location$random_path$source = config.getString("resource-pack.protection.obfuscation.resource-location.random-path.source", "obf"); resource_pack$protection$obfuscation$resource_location$random_path$anti_unzip = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-path.anti-unzip", false); resource_pack$protection$obfuscation$resource_location$random_atlas$images_per_canvas = config.getInt("resource-pack.protection.obfuscation.resource-location.random-atlas.images-per-canvas", 16); - resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", true); + resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", false); resource_pack$protection$obfuscation$resource_location$bypass_textures = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-textures"); resource_pack$protection$obfuscation$resource_location$bypass_models = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-models"); resource_pack$protection$obfuscation$resource_location$bypass_sounds = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-sounds"); @@ -310,13 +313,9 @@ public class Config { resource_pack$duplicated_files_handler = List.of(); } - // performance - performance$max_note_block_chain_update_limit = config.getInt("performance.max-note-block-chain-update-limit", 64); - performance$max_tripwire_chain_update_limit = config.getInt("performance.max-tripwire-chain-update-limit", 128); - performance$max_emojis_per_parse = config.getInt("performance.max-emojis-per-parse", 32); - // light light_system$force_update_light = config.getBoolean("light-system.force-update-light", false); + light_system$async_update = config.getBoolean("light-system.async-update", true); light_system$enable = config.getBoolean("light-system.enable", true); // chunk @@ -330,22 +329,37 @@ public class Config { chunk_system$injection$target = config.getEnum("chunk-system.injection.target", InjectionTarget.class, InjectionTarget.PALETTE) == InjectionTarget.PALETTE; } - // furniture - furniture$handle_invalid_furniture_on_chunk_load$enable = config.getBoolean("furniture.handle-invalid-furniture-on-chunk-load.enable", false); - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (String furniture : config.getStringList("furniture.handle-invalid-furniture-on-chunk-load.remove")) { - builder.put(furniture, ""); + chunk_system$process_invalid_furniture$enable = config.getBoolean("chunk-system.process-invalid-furniture.enable", false); + ImmutableMap.Builder furnitureBuilder = ImmutableMap.builder(); + for (String furniture : config.getStringList("chunk-system.process-invalid-furniture.remove")) { + furnitureBuilder.put(furniture, ""); } - if (config.contains("furniture.handle-invalid-furniture-on-chunk-load.convert")) { - Section section = config.getSection("furniture.handle-invalid-furniture-on-chunk-load.convert"); + if (config.contains("chunk-system.process-invalid-furniture.convert")) { + Section section = config.getSection("chunk-system.process-invalid-furniture.convert"); if (section != null) { for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { - builder.put(entry.getKey(), entry.getValue().toString()); + furnitureBuilder.put(entry.getKey(), entry.getValue().toString()); } } } + chunk_system$process_invalid_furniture$mapping = furnitureBuilder.build(); - furniture$handle_invalid_furniture_on_chunk_load$mapping = builder.build(); + chunk_system$process_invalid_blocks$enable = config.getBoolean("chunk-system.process-invalid-blocks.enable", false); + ImmutableMap.Builder blockBuilder = ImmutableMap.builder(); + for (String furniture : config.getStringList("chunk-system.process-invalid-blocks.remove")) { + blockBuilder.put(furniture, ""); + } + if (config.contains("chunk-system.process-invalid-blocks.convert")) { + Section section = config.getSection("chunk-system.process-invalid-blocks.convert"); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + blockBuilder.put(entry.getKey(), entry.getValue().toString()); + } + } + } + chunk_system$process_invalid_blocks$mapping = blockBuilder.build(); + + // furniture furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true); furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH)); @@ -375,6 +389,7 @@ public class Config { block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true); block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1); block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0); + block$chunk_relighter = config.getBoolean("block.chunk-relighter", true); // recipe recipe$enable = config.getBoolean("recipe.enable", true); @@ -405,10 +420,11 @@ public class Config { image$intercept_packets$advancement = config.getBoolean("image.intercept-packets.advancement", true); // emoji - emoji$chat = config.getBoolean("emoji.chat", true); - emoji$anvil = config.getBoolean("emoji.anvil", true); - emoji$book = config.getBoolean("emoji.book", true); - emoji$sign = config.getBoolean("emoji.sign", true); + emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true); + emoji$contexts$anvil = config.getBoolean("emoji.contexts.anvil", true); + emoji$contexts$book = config.getBoolean("emoji.contexts.book", true); + emoji$contexts$sign = config.getBoolean("emoji.contexts.sign", true); + emoji$max_emojis_per_parse = config.getInt("emoji.max-emojis-per-parse", 32); firstTime = false; } @@ -439,6 +455,10 @@ public class Config { return instance.debug$item; } + public static boolean debugBlockEntity() { + return false; + } + public static boolean debugFurniture() { return instance.debug$furniture; } @@ -464,24 +484,28 @@ public class Config { } public static int maxNoteBlockChainUpdate() { - return instance.performance$max_note_block_chain_update_limit; + return 64; } public static int maxEmojisPerParse() { - return instance.performance$max_emojis_per_parse; + return instance.emoji$max_emojis_per_parse; } public static boolean handleInvalidFurniture() { - return instance.furniture$handle_invalid_furniture_on_chunk_load$enable; + return instance.chunk_system$process_invalid_furniture$enable; + } + + public static boolean handleInvalidBlock() { + return instance.chunk_system$process_invalid_blocks$enable; } public static Map furnitureMappings() { - return instance.furniture$handle_invalid_furniture_on_chunk_load$mapping; + return instance.chunk_system$process_invalid_furniture$mapping; } -// public static boolean forceUpdateLight() { -// return instance.light_system$force_update_light; -// } + public static Map blockMappings() { + return instance.chunk_system$process_invalid_blocks$mapping; + } public static boolean enableLightSystem() { return instance.light_system$enable; @@ -772,19 +796,19 @@ public class Config { } public static boolean allowEmojiSign() { - return instance.emoji$sign; + return instance.emoji$contexts$sign; } public static boolean allowEmojiChat() { - return instance.emoji$chat; + return instance.emoji$contexts$chat; } public static boolean allowEmojiAnvil() { - return instance.emoji$anvil; + return instance.emoji$contexts$anvil; } public static boolean allowEmojiBook() { - return instance.emoji$book; + return instance.emoji$contexts$book; } public static ColliderType colliderType() { @@ -859,6 +883,14 @@ public class Config { return instance.item$update_triggers$drop; } + public static boolean enableChunkRelighter() { + return instance.block$chunk_relighter; + } + + public static boolean asyncLightUpdate() { + return instance.light_system$async_update; + } + public void setObf(boolean enable) { this.resource_pack$protection$obfuscation$enable = enable; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java index 5c29100a9..eb0b3ae25 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -25,6 +26,10 @@ public class GlobalVariableManager implements Manageable { this.globalVariables.clear(); } + public Map globalVariables() { + return Collections.unmodifiableMap(this.globalVariables); + } + public ConfigParser parser() { return this.parser; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java new file mode 100644 index 000000000..42f7c5984 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.plugin.context; + +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.text.minimessage.*; + +import java.util.Map; + +public final class NetworkTextReplaceContext extends AbstractChainParameterContext { + private final Player player; + + public NetworkTextReplaceContext(Player player) { + super(new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); + this.player = player; + } + + public static NetworkTextReplaceContext of(Player player) { + return new NetworkTextReplaceContext(player); + } + + public Player player() { + return player; + } + + @Override + public TagResolver[] tagResolvers() { + if (this.tagResolvers == null) { + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this), new I18NTag(this), + new NamedArgumentTag(this), new ExpressionTag(this), new GlobalVariableTag(this)}; + } + return this.tagResolvers; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java new file mode 100644 index 000000000..127070193 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java @@ -0,0 +1,23 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Map; + +public class AlwaysFalseCondition implements Condition { + + @Override + public Key type() { + return CommonConditions.ALWAYS_FALSE; + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + return new AlwaysFalseCondition<>(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java similarity index 77% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java index dda586d78..c36d53cb9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java @@ -6,11 +6,11 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Map; -public class EmptyCondition implements Condition { +public class AlwaysTrueCondition implements Condition { @Override public Key type() { - return CommonConditions.EMPTY; + return CommonConditions.ALWAYS_TRUE; } @Override @@ -22,7 +22,7 @@ public class EmptyCondition implements Condition { @Override public Condition create(Map arguments) { - return new EmptyCondition<>(); + return new AlwaysTrueCondition<>(); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java index a5521169c..156c17856 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java @@ -5,11 +5,14 @@ import net.momirealms.craftengine.core.util.Key; public final class CommonConditions { private CommonConditions() {} - public static final Key EMPTY = Key.of("craftengine:empty"); + public static final Key ALWAYS_TRUE = Key.of("craftengine:always_true"); + public static final Key ALWAYS_FALSE = Key.of("craftengine:always_false"); public static final Key ALL_OF = Key.of("craftengine:all_of"); public static final Key ANY_OF = Key.of("craftengine:any_of"); public static final Key INVERTED = Key.of("craftengine:inverted"); public static final Key MATCH_ITEM = Key.of("craftengine:match_item"); + public static final Key MATCH_ENTITY = Key.of("craftengine:match_entity"); + public static final Key MATCH_BLOCK = Key.of("craftengine:match_block"); public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property"); public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus"); public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java index 7ae158e1f..3dc4a67e2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java @@ -27,7 +27,7 @@ public class ExpressionCondition implements Condition @Override public boolean test(CTX ctx) { - String exp = expression.get(ctx); + String exp = this.expression.get(ctx); Expression expr = new Expression(exp); try { return expr.evaluate().getBooleanValue(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockCondition.java new file mode 100644 index 000000000..769e849fa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockCondition.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.ExistingBlock; + +import java.util.*; + +public class MatchBlockCondition implements Condition { + private final Set ids; + private final boolean regexMatch; + + public MatchBlockCondition(Collection ids, boolean regexMatch) { + this.ids = new HashSet<>(ids); + this.regexMatch = regexMatch; + } + + @Override + public Key type() { + return CommonConditions.MATCH_BLOCK; + } + + @Override + public boolean test(CTX ctx) { + Optional block = ctx.getOptionalParameter(DirectContextParameters.BLOCK); + return block.filter(blockInWorld -> MiscUtils.matchRegex(blockInWorld.id().asString(), this.ids, this.regexMatch)).isPresent(); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + List ids = MiscUtils.getAsStringList(arguments.get("id")); + if (ids.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.condition.match_block.missing_id"); + } + boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex"); + return new MatchBlockCondition<>(ids, regex); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java index b8a213d27..e5ce7b198 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java @@ -1,7 +1,9 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; @@ -9,11 +11,9 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.Pair; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.ExistingBlock; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; public class MatchBlockPropertyCondition implements Condition { private final List> properties; @@ -29,19 +29,49 @@ public class MatchBlockPropertyCondition implements Conditi @Override public boolean test(CTX ctx) { - return ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE).map(state -> { - CustomBlock block = state.owner().value(); + ImmutableBlockState customBlockState = null; + StatePropertyAccessor vanillaStatePropertyAccessor = null; + // 优先使用自定义状态,其主要应用于自定义方块掉落物 + Optional optionalCustomState = ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE); + if (optionalCustomState.isPresent()) { + customBlockState = optionalCustomState.get(); + } else { + // 其次再判断block,这个过程会更慢,因为每次获取都是全新的方块状态,适用于物品等事件 + Optional optionalExistingBlock = ctx.getOptionalParameter(DirectContextParameters.BLOCK); + if (optionalExistingBlock.isPresent()) { + ExistingBlock existingBlock = optionalExistingBlock.get(); + customBlockState = existingBlock.customBlockState(); + if (customBlockState == null) { + vanillaStatePropertyAccessor = existingBlock.createStatePropertyAccessor(); + } + } else { + // 都没有则条件不过 + return false; + } + } + if (customBlockState != null) { + CustomBlock block = customBlockState.owner().value(); for (Pair property : this.properties) { Property propertyIns = block.getProperty(property.left()); if (propertyIns == null) { return false; } - if (!state.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) { + if (!customBlockState.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) { return false; } } - return true; - }).orElse(false); + } else { + for (Pair property : this.properties) { + String value = vanillaStatePropertyAccessor.getPropertyValueAsString(property.left()); + if (value == null) { + return false; + } + if (!value.equals(property.right())) { + return false; + } + } + } + return true; } public static class FactoryImpl implements ConditionFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java new file mode 100644 index 000000000..c1a92cedc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.*; + +public class MatchEntityCondition implements Condition { + private final Set ids; + private final boolean regexMatch; + + public MatchEntityCondition(Collection ids, boolean regexMatch) { + this.ids = new HashSet<>(ids); + this.regexMatch = regexMatch; + } + + @Override + public Key type() { + return CommonConditions.MATCH_ENTITY; + } + + @Override + public boolean test(CTX ctx) { + Optional entity = ctx.getOptionalParameter(DirectContextParameters.ENTITY); + return entity.filter(value -> MiscUtils.matchRegex(value.type().asString(), this.ids, this.regexMatch)).isPresent(); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + List ids = MiscUtils.getAsStringList(arguments.get("id")); + if (ids.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.condition.match_entity.missing_id"); + } + boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex"); + return new MatchEntityCondition<>(ids, regex); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java index 45d14beac..f6c7f78a8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java @@ -28,19 +28,7 @@ public class MatchItemCondition implements Condition { @Override public boolean test(CTX ctx) { Optional> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); - if (item.isEmpty()) return false; - Key key = item.get().id(); - String itemId = key.toString(); - if (this.regexMatch) { - for (String regex : ids) { - if (itemId.matches(regex)) { - return true; - } - } - } else { - return this.ids.contains(itemId); - } - return false; + return item.filter(value -> MiscUtils.matchRegex(value.id().asString(), this.ids, this.regexMatch)).isPresent(); } public static class FactoryImpl implements ConditionFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java index e5fc50979..c5e0e3c71 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java @@ -17,6 +17,8 @@ public class EventConditions { static { register(CommonConditions.MATCH_ITEM, new MatchItemCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_ENTITY, new MatchEntityCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_BLOCK, new MatchBlockCondition.FactoryImpl<>()); register(CommonConditions.MATCH_BLOCK_PROPERTY, new MatchBlockPropertyCondition.FactoryImpl<>()); register(CommonConditions.TABLE_BONUS, new TableBonusCondition.FactoryImpl<>()); register(CommonConditions.SURVIVES_EXPLOSION, new SurvivesExplosionCondition.FactoryImpl<>()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java index c27b2177f..2447d64ff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java @@ -16,34 +16,37 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class CommandFunction extends AbstractConditionalFunction { private final List command; - private final boolean asPlayer; private final PlayerSelector selector; + private final boolean asPlayer; + private final boolean asEvent; + private final boolean asOp; - public CommandFunction(List> predicates, @Nullable PlayerSelector selector, List command, boolean asPlayer) { + public CommandFunction(List> predicates, @Nullable PlayerSelector selector, List command, + boolean asPlayer, boolean asEvent, boolean asOp) { super(predicates); - this.asPlayer = asPlayer; this.command = command; this.selector = selector; + this.asPlayer = asPlayer; + this.asEvent = asEvent; + this.asOp = asOp; } @Override public void runInternal(CTX ctx) { - if (this.asPlayer) { + if (this.asPlayer || this.asOp) { if (this.selector == null) { - ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { - for (TextProvider c : this.command) { - it.performCommand(c.get(ctx)); - } - }); + ctx.getOptionalParameter(DirectContextParameters.PLAYER) + .ifPresent(player -> executeCommands( + ctx, this.asEvent ? player::performCommandAsEvent : command1 -> player.performCommand(command1, this.asOp) + )); } else { for (Player viewer : this.selector.get(ctx)) { RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); - for (TextProvider c : this.command) { - viewer.performCommand(c.get(relationalContext)); - } + executeCommands(relationalContext, this.asEvent ? viewer::performCommandAsEvent : command1 -> viewer.performCommand(command1, this.asOp)); } } } else { @@ -54,6 +57,12 @@ public class CommandFunction extends AbstractConditionalFun } } + private void executeCommands(Context ctx, Consumer executor) { + for (TextProvider c : this.command) { + executor.accept(c.get(ctx)); + } + } + @Override public Key type() { return CommonFunctions.COMMAND; @@ -70,7 +79,9 @@ public class CommandFunction extends AbstractConditionalFun Object command = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "command", "commands"), "warning.config.function.command.missing_command"); List commands = MiscUtils.getAsStringList(command).stream().map(TextProviders::fromString).toList(); boolean asPlayer = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-player", false), "as-player"); - return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer); + boolean asEvent = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-event", false), "as-event"); + boolean asOp = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-op", false), "as-op"); + return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer, asEvent, asOp); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java index 2ab73db03..c97d47bd3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java @@ -33,7 +33,7 @@ public class ParticleFunction extends AbstractConditionalFu final String blockState = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("block-state"), "warning.config.function.particle.missing_block_state"); @Override public BlockStateWrapper get() { - return CraftEngine.instance().blockManager().createPackedBlockState(this.blockState); + return CraftEngine.instance().blockManager().createBlockState(this.blockState); } })), ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java index 2efaf8877..15167ef06 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java @@ -62,7 +62,7 @@ public class PlaceBlockFunction extends AbstractConditional NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider flags = Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())); - return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createPackedBlockState(state)), x, y, z, flags, getPredicates(arguments)); + return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)), x, y, z, flags, getPredicates(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java new file mode 100644 index 000000000..6ca708f2b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java @@ -0,0 +1,104 @@ +package net.momirealms.craftengine.core.plugin.context.number; + +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class GaussianNumberProvider implements NumberProvider { + public static final FactoryImpl FACTORY = new FactoryImpl(); + + private final double min; + private final double max; + private final double mean; + private final double stdDev; + private final int maxAttempts; + + public GaussianNumberProvider(double min, double max, double mean, double stdDev, int maxAttempts) { + this.min = min; + this.max = max; + this.mean = mean; + this.stdDev = stdDev; + this.maxAttempts = maxAttempts; + validateParameters(); + } + + private void validateParameters() { + if (this.min >= this.max) { + throw new IllegalArgumentException("min must be less than max"); + } + if (this.stdDev <= 0) { + throw new IllegalArgumentException("std-dev must be greater than 0"); + } + if (this.maxAttempts <= 0) { + throw new IllegalArgumentException("max-attempts must be greater than 0"); + } + } + + @Override + public float getFloat(Context context) { + return (float) getDouble(context); + } + + @Override + public double getDouble(Context context) { + Random random = ThreadLocalRandom.current(); + int attempts = 0; + while (attempts < maxAttempts) { + double value = random.nextGaussian() * stdDev + mean; + if (value >= min && value <= max) { + return value; + } + attempts++; + } + return MCUtils.clamp(this.mean, this.min, this.max); + } + + @Override + public Key type() { + return NumberProviders.GAUSSIAN; + } + + public double min() { + return min; + } + + public double max() { + return max; + } + + public int maxAttempts() { + return maxAttempts; + } + + public double mean() { + return mean; + } + + public double stdDev() { + return stdDev; + } + + public static class FactoryImpl implements NumberProviderFactory { + + @Override + public NumberProvider create(Map arguments) { + double min = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("min"), "warning.config.number.gaussian.missing_min"), "min"); + double max = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("max"), "warning.config.number.gaussian.missing_max"), "max"); + double mean = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("mean", (min + max) / 2.0), "mean"); + double stdDev = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("std-dev", (max - min) / 6.0), "std-dev"); + int maxAttempts = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-attempts", 128), "max-attempts"); + return new GaussianNumberProvider(min, max, mean, stdDev, maxAttempts); + } + } + + @Override + public String toString() { + return String.format("GaussianNumberProvider{min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f, maxAttempts=%d}", + min, max, mean, stdDev, maxAttempts); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java index 22b698c86..49ce77b1a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java @@ -17,11 +17,13 @@ public class NumberProviders { public static final Key CONSTANT = Key.of("craftengine:constant"); public static final Key UNIFORM = Key.of("craftengine:uniform"); public static final Key EXPRESSION = Key.of("craftengine:expression"); + public static final Key GAUSSIAN = Key.of("craftengine:gaussian"); static { register(FIXED, FixedNumberProvider.FACTORY); register(CONSTANT, FixedNumberProvider.FACTORY); register(UNIFORM, UniformNumberProvider.FACTORY); + register(GAUSSIAN, GaussianNumberProvider.FACTORY); register(EXPRESSION, ExpressionNumberProvider.FACTORY); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java index 1aac69f9c..dc1cff926 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java @@ -2,31 +2,31 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.world.ExistingBlock; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; -public class BlockParameterProvider implements ChainParameterProvider { - private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); +public class BlockParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(DirectContextParameters.X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, BlockInWorld::customBlock); - CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, BlockInWorld::customBlockState); - CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, BlockInWorld::world); - CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, BlockInWorld::position); + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, ExistingBlock::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, ExistingBlock::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, ExistingBlock::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, ExistingBlock::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, ExistingBlock::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, ExistingBlock::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, ExistingBlock::customBlock); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, ExistingBlock::customBlockState); + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, ExistingBlock::world); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, ExistingBlock::position); } @SuppressWarnings("unchecked") @Override - public Optional getOptionalParameter(ContextKey parameter, BlockInWorld block) { + public Optional getOptionalParameter(ContextKey parameter, ExistingBlock block) { return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(block)); } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java index cf98e3774..a3d0b5335 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java @@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextKey; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.world.ExistingBlock; import net.momirealms.craftengine.core.world.Position; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; @@ -49,7 +49,7 @@ public final class DirectContextParameters { public static final ContextKey> MAIN_HAND_ITEM = ContextKey.direct("main_hand_item"); public static final ContextKey> OFF_HAND_ITEM = ContextKey.direct("off_hand_item"); public static final ContextKey CUSTOM_BLOCK = ContextKey.direct("custom_block"); - public static final ContextKey BLOCK = ContextKey.direct("block"); + public static final ContextKey BLOCK = ContextKey.direct("block"); public static final ContextKey TIME = ContextKey.direct("time"); public static final ContextKey ID = ContextKey.direct("id"); public static final ContextKey CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data"); @@ -57,8 +57,11 @@ public final class DirectContextParameters { public static final ContextKey ANCHOR_TYPE = ContextKey.direct("anchor_type"); public static final ContextKey HAND = ContextKey.direct("hand"); public static final ContextKey EVENT = ContextKey.direct("event"); - public static final ContextKey IS_FLYING = ContextKey.direct("is_flying"); public static final ContextKey IS_SNEAKING = ContextKey.direct("is_sneaking"); + public static final ContextKey IS_SWIMMING = ContextKey.direct("is_swimming"); + public static final ContextKey IS_CLIMBING = ContextKey.direct("is_climbing"); + public static final ContextKey IS_GLIDING = ContextKey.direct("is_gliding"); + public static final ContextKey IS_FLYING = ContextKey.direct("is_flying"); public static final ContextKey IS_CUSTOM = ContextKey.direct("is_custom"); public static final ContextKey IS_BLOCK_ITEM = ContextKey.direct("is_block_item"); public static final ContextKey GAMEMODE = ContextKey.direct("gamemode"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java index 7ed66aff2..25c3df127 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java @@ -29,8 +29,11 @@ public class PlayerParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Player::uuid); CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); - CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying); CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SNEAKING, Player::isSneaking); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SWIMMING, Player::isSwimming); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_CLIMBING, Player::isClimbing); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_GLIDING, Player::isGliding); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying); CONTEXT_FUNCTIONS.put(DirectContextParameters.GAMEMODE, Player::gameMode); CONTEXT_FUNCTIONS.put(DirectContextParameters.MAIN_HAND_ITEM, p -> p.getItemInHand(InteractionHand.MAIN_HAND)); CONTEXT_FUNCTIONS.put(DirectContextParameters.OFF_HAND_ITEM, p -> p.getItemInHand(InteractionHand.OFF_HAND)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java index 2eb14be4f..5caddb618 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java @@ -143,25 +143,6 @@ public class Dependencies { Collections.emptyList() ); - public static final Dependency SLF4J_API = new Dependency( - "slf4j-api", - "org.slf4j", - "slf4j-api", - Collections.emptyList() - ); - - public static final Dependency SLF4J_SIMPLE = new Dependency( - "slf4j-simple", - "org.slf4j", - "slf4j-simple", - Collections.emptyList() - ) { - @Override - public String getVersion() { - return Dependencies.SLF4J_API.getVersion(); - } - }; - public static final Dependency COMMONS_LANG3 = new Dependency( "commons-lang3", "org{}apache{}commons", @@ -180,16 +161,7 @@ public class Dependencies { "commons-imaging", "org{}apache{}commons", "commons-imaging", - List.of(Relocation.of("commons", "org{}apache{}commons")), - (p) -> { - try (JarFile jarFile = new JarFile(p.toFile())) { - ZipEntry entry = jarFile.getEntry("net/momirealms/craftengine/libraries/commons/imaging/Imaging.class"); - return entry != null; - } catch (IOException e) { - CraftEngine.instance().logger().warn("Error reading jar file", e); - return false; - } - } + List.of(Relocation.of("commons", "org{}apache{}commons")) ); public static final Dependency BYTE_BUDDY = new Dependency( @@ -224,7 +196,8 @@ public class Dependencies { "option", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency ADVENTURE_API = new Dependency( @@ -233,7 +206,8 @@ public class Dependencies { "adventure-api", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency ADVENTURE_NBT = new Dependency( @@ -242,7 +216,8 @@ public class Dependencies { "adventure-nbt", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -256,7 +231,8 @@ public class Dependencies { "adventure-key", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -270,7 +246,8 @@ public class Dependencies { "examination-api", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency EXAMINATION_STRING = new Dependency( @@ -279,7 +256,8 @@ public class Dependencies { "examination-string", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -293,7 +271,8 @@ public class Dependencies { "adventure-text-minimessage", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -307,7 +286,8 @@ public class Dependencies { "adventure-text-serializer-commons", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -321,7 +301,8 @@ public class Dependencies { "adventure-text-serializer-gson", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -335,7 +316,8 @@ public class Dependencies { "adventure-text-serializer-json-legacy-impl", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -349,7 +331,8 @@ public class Dependencies { "adventure-text-serializer-legacy", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -363,7 +346,8 @@ public class Dependencies { "adventure-text-serializer-json", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -392,27 +376,6 @@ public class Dependencies { List.of(Relocation.of("evalex", "com{}ezylang{}evalex")) ); - public static final Dependency NETTY_HTTP = new Dependency( - "netty-codec-http", - "io{}netty", - "netty-codec-http", - Collections.emptyList() - ); - - public static final Dependency NETTY_HTTP2 = new Dependency( - "netty-codec-http2", - "io{}netty", - "netty-codec-http2", - Collections.emptyList() - ); - - public static final Dependency REACTIVE_STREAMS = new Dependency( - "reactive-streams", - "org{}reactivestreams", - "reactive-streams", - List.of(Relocation.of("reactivestreams", "org{}reactivestreams")) - ); - public static final Dependency JIMFS = new Dependency( "jimfs", "com{}google{}jimfs", @@ -420,6 +383,36 @@ public class Dependencies { List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs")) ); + public static final Dependency NETTY_HTTP = new Dependency( + "netty-codec-http", + "io{}netty", + "netty-codec-http", + List.of( + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") + ) + ); + + public static final Dependency NETTY_HTTP2 = new Dependency( + "netty-codec-http2", + "io{}netty", + "netty-codec-http2", + List.of(Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2")) + ); + + public static final Dependency REACTIVE_STREAMS = new Dependency( + "reactive-streams", + "org{}reactivestreams", + "reactive-streams", + List.of( + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") + ) + ); public static final Dependency AMAZON_AWSSDK_S3 = new Dependency( "amazon-sdk-s3", @@ -427,7 +420,11 @@ public class Dependencies { "s3", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ); @@ -437,7 +434,11 @@ public class Dependencies { "netty-nio-client", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -452,7 +453,11 @@ public class Dependencies { "sdk-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -467,7 +472,11 @@ public class Dependencies { "auth", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -482,7 +491,11 @@ public class Dependencies { "regions", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -497,7 +510,11 @@ public class Dependencies { "identity-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -512,7 +529,11 @@ public class Dependencies { "http-client-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -527,7 +548,11 @@ public class Dependencies { "protocol-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -542,7 +567,11 @@ public class Dependencies { "aws-xml-protocol", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -557,7 +586,11 @@ public class Dependencies { "json-utils", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -572,7 +605,11 @@ public class Dependencies { "aws-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -587,7 +624,11 @@ public class Dependencies { "utils", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -602,7 +643,11 @@ public class Dependencies { "annotations", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -617,7 +662,11 @@ public class Dependencies { "crt-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -632,7 +681,11 @@ public class Dependencies { "checksums", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -647,7 +700,11 @@ public class Dependencies { "eventstream", List.of( Relocation.of("eventstream", "software{}amazon{}eventstream"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ); @@ -657,7 +714,11 @@ public class Dependencies { "profiles", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -672,7 +733,11 @@ public class Dependencies { "retries", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -687,7 +752,11 @@ public class Dependencies { "endpoints-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -702,7 +771,11 @@ public class Dependencies { "arns", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -717,7 +790,11 @@ public class Dependencies { "aws-query-protocol", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -732,7 +809,11 @@ public class Dependencies { "http-auth-aws", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -747,7 +828,11 @@ public class Dependencies { "http-auth-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -762,7 +847,11 @@ public class Dependencies { "http-auth", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -777,7 +866,11 @@ public class Dependencies { "http-auth-aws-eventstream", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -792,7 +885,11 @@ public class Dependencies { "checksums-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -807,7 +904,11 @@ public class Dependencies { "retries-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -822,7 +923,11 @@ public class Dependencies { "metrics-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -837,7 +942,11 @@ public class Dependencies { "third-party-jackson-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java index 893716bac..ef1713a5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java @@ -15,21 +15,36 @@ public class Dependency { private final String rawArtifactId; private final List relocations; private final Predicate verifier; + private final boolean shared; public Dependency(String id, String groupId, String artifactId, List relocations) { + this(id, groupId, artifactId, relocations, false); + } + + public Dependency(String id, String groupId, String artifactId, List relocations, boolean shared) { this.id = id; this.groupId = groupId; this.rawArtifactId = artifactId; this.relocations = relocations; this.verifier = (p) -> true; + this.shared = shared; } public Dependency(String id, String groupId, String artifactId, List relocations, Predicate verifier) { + this(id, groupId, artifactId, relocations, verifier, false); + } + + public Dependency(String id, String groupId, String artifactId, List relocations, Predicate verifier, boolean shared) { this.id = id; this.groupId = groupId; this.rawArtifactId = artifactId; this.relocations = relocations; this.verifier = verifier; + this.shared = shared; + } + + public boolean shared() { + return shared; } public boolean verify(Path remapped) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java index 886f830d2..5e8eeec28 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java @@ -22,7 +22,8 @@ import java.util.stream.Stream; public class DependencyManagerImpl implements DependencyManager { private final DependencyRegistry registry; private final Path cacheDirectory; - private final ClassPathAppender classPathAppender; + private final ClassPathAppender sharedClassPathAppender; + private final ClassPathAppender privateClassPathAppender; private final Map loaded = Collections.synchronizedMap(new HashMap<>()); private final Map, IsolatedClassLoader> loaders = new HashMap<>(); private final RelocationHandler relocationHandler; @@ -33,7 +34,8 @@ public class DependencyManagerImpl implements DependencyManager { this.plugin = plugin; this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); - this.classPathAppender = plugin.classPathAppender(); + this.sharedClassPathAppender = plugin.sharedClassPathAppender(); + this.privateClassPathAppender = plugin.privateClassPathAppender(); this.loadingExecutor = plugin.scheduler().async(); this.relocationHandler = new RelocationHandler(this); } @@ -108,8 +110,14 @@ public class DependencyManagerImpl implements DependencyManager { this.loaded.put(dependency, file); - if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) { - this.classPathAppender.addJarToClasspath(file); + if (dependency.shared()) { + if (this.sharedClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) { + this.sharedClassPathAppender.addJarToClasspath(file); + } + } else { + if (this.privateClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) { + this.privateClassPathAppender.addJarToClasspath(file); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java index 2833d3f3d..10a29eda1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java @@ -6,7 +6,7 @@ public final class Relocation { private static final String RELOCATION_PREFIX = "net.momirealms.craftengine.libraries."; public static Relocation of(String id, String pattern) { - return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id); + return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id.replace("{}", ".")); } private final String pattern; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java index 836731d7e..a87b62c10 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java @@ -92,7 +92,7 @@ public class I18NData { } private static String stateToRealBlockId(ImmutableBlockState state) { - String id = state.customBlockState().handle().toString(); + String id = state.customBlockState().literalObject().toString(); int first = -1, last = -1; for (int i = 0; i < id.length(); i++) { char c = id.charAt(i); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java index 4ca696c84..9ac5d509d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java @@ -9,8 +9,9 @@ public enum Debugger { COMMON(Config::debugCommon), PACKET(Config::debugPacket), FURNITURE(Config::debugFurniture), - RESOURCE_PACK(Config::debugFurniture), - ITEM(Config::debugItem); + RESOURCE_PACK(Config::debugResourcePack), + ITEM(Config::debugItem), + BLOCK_ENTITY(Config::debugBlockEntity); private final Supplier condition; @@ -26,7 +27,11 @@ public enum Debugger { public void warn(Supplier message, Throwable e) { if (this.condition.get()) { - CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + if (e != null) { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + } else { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get()); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java index 15e0aa919..57fb85de9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.plugin.network; import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.entity.player.Player; public interface EntityPacketHandler { @@ -8,7 +9,7 @@ public interface EntityPacketHandler { return false; } - default void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + default void handleSetEntityData(Player user, ByteBufPacketEvent event) { } default void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java new file mode 100644 index 000000000..9776a4fee --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.plugin.network; + +import io.netty.buffer.ByteBuf; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkDecoder; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkMemberEncoder; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.ResourceKey; + +public interface ModPacket { + + ResourceKey> type(); + + default void handle(NetWorkUser user) { + } + + static NetworkCodec codec(NetworkMemberEncoder networkMemberEncoder, NetworkDecoder networkDecoder) { + return NetworkCodec.ofMember(networkMemberEncoder, networkDecoder); + } + +} 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 453b8e055..eaf72bf24 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 @@ -4,10 +4,13 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.plugin.Plugin; +import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -38,6 +41,10 @@ public interface NetWorkUser { void sendPacket(Object packet, boolean immediately, Runnable sendListener); + void sendPackets(List packet, boolean immediately); + + void sendPackets(List packet, boolean immediately, Runnable sendListener); + void sendCustomPayload(Key channel, byte[] data); void kick(Component message); @@ -71,4 +78,18 @@ public interface NetWorkUser { void setShouldProcessFinishConfiguration(boolean shouldProcess); boolean shouldProcessFinishConfiguration(); + + boolean isChunkTracked(long chunkPos); + + ChunkStatus getTrackedChunk(long chunkPos); + + void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus); + + void clearTrackedChunks(); + + void removeTrackedChunk(long chunkPos); + + IntIdentityList clientBlockList(); + + void setClientBlockList(IntIdentityList integers); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java new file mode 100644 index 000000000..356ee14f7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java @@ -0,0 +1,32 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +import java.util.function.Function; + +public interface NetworkCodec extends NetworkEncoder, NetworkDecoder { + + default NetworkCodec map(Function factory, Function getter) { + return new NetworkCodec<>() { + @Override + public V decode(B in) { + return factory.apply(NetworkCodec.this.decode(in)); + } + + @Override + public void encode(B out, V value) { + NetworkCodec.this.encode(out, getter.apply(value)); + } + }; + } + + static NetworkCodec ofMember(final NetworkMemberEncoder networkMemberEncoder, final NetworkDecoder networkDecoder) { + return new NetworkCodec<>() { + public V decode(B in) { + return networkDecoder.decode(in); + } + + public void encode(B out, V value) { + networkMemberEncoder.encode(value, out); + } + }; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java similarity index 87% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java index 4642578bc..0589a2df5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; +package net.momirealms.craftengine.core.plugin.network.codec; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; @@ -21,9 +21,9 @@ import java.util.OptionalInt; /** * 随便写了点方便后面重构和客户端通讯 */ -public interface NetWorkCodecs { +public interface NetworkCodecs { - NetWorkCodec BOOLEAN = new NetWorkCodec<>() { + NetworkCodec BOOLEAN = new NetworkCodec<>() { @Override public Boolean decode(ByteBuf in) { return in.readBoolean(); @@ -35,7 +35,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec BYTE = new NetWorkCodec<>() { + NetworkCodec BYTE = new NetworkCodec<>() { @Override public Byte decode(ByteBuf in) { return in.readByte(); @@ -47,9 +47,9 @@ public interface NetWorkCodecs { } }; - NetWorkCodec ROTATION_BYTE = BYTE.map(MCUtils::unpackDegrees, MCUtils::packDegrees); + NetworkCodec ROTATION_BYTE = BYTE.map(MCUtils::unpackDegrees, MCUtils::packDegrees); - NetWorkCodec SHORT = new NetWorkCodec<>() { + NetworkCodec SHORT = new NetworkCodec<>() { @Override public Short decode(ByteBuf in) { return in.readShort(); @@ -61,7 +61,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec UNSIGNED_SHORT = new NetWorkCodec<>() { + NetworkCodec UNSIGNED_SHORT = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return in.readUnsignedShort(); @@ -73,7 +73,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec INTEGER = new NetWorkCodec<>() { + NetworkCodec INTEGER = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return in.readInt(); @@ -85,7 +85,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VAR_INTEGER = new NetWorkCodec<>() { + NetworkCodec VAR_INTEGER = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { int result = 0; @@ -111,12 +111,12 @@ public interface NetWorkCodecs { } }; - NetWorkCodec OPTIONAL_VAR_INTEGER = VAR_INTEGER.map( + NetworkCodec OPTIONAL_VAR_INTEGER = VAR_INTEGER.map( integer -> integer == 0 ? OptionalInt.empty() : OptionalInt.of(integer - 1), optionalInt -> optionalInt.isPresent() ? optionalInt.getAsInt() + 1 : 0 ); - NetWorkCodec LONG = new NetWorkCodec<>() { + NetworkCodec LONG = new NetworkCodec<>() { @Override public Long decode(ByteBuf in) { return in.readLong(); @@ -128,7 +128,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VAR_LONG = new NetWorkCodec<>() { + NetworkCodec VAR_LONG = new NetworkCodec<>() { @Override public Long decode(ByteBuf in) { long result = 0L; @@ -154,7 +154,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec FLOAT = new NetWorkCodec<>() { + NetworkCodec FLOAT = new NetworkCodec<>() { @Override public Float decode(ByteBuf in) { return in.readFloat(); @@ -166,7 +166,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec DOUBLE = new NetWorkCodec<>() { + NetworkCodec DOUBLE = new NetworkCodec<>() { @Override public Double decode(ByteBuf in) { return in.readDouble(); @@ -178,7 +178,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec BYTE_ARRAY = new NetWorkCodec<>() { + NetworkCodec BYTE_ARRAY = new NetworkCodec<>() { @Override public byte[] decode(ByteBuf in) { int maxSize = in.readableBytes(); @@ -199,7 +199,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec LONG_ARRAY = new NetWorkCodec<>() { + NetworkCodec LONG_ARRAY = new NetworkCodec<>() { @Override public long[] decode(ByteBuf in) { int arrayLength = VAR_INTEGER.decode(in); @@ -224,7 +224,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec STRING_UTF8 = new NetWorkCodec<>() { + NetworkCodec STRING_UTF8 = new NetworkCodec<>() { private static final int MAX_STRING_LENGTH = 32767; @Override @@ -273,7 +273,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec TAG = new NetWorkCodec<>() { + NetworkCodec TAG = new NetworkCodec<>() { @Override public Tag decode(ByteBuf in) { int initialIndex = in.readerIndex(); @@ -304,7 +304,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec COMPOUND_TAG = TAG.map(tag -> { + NetworkCodec COMPOUND_TAG = TAG.map(tag -> { if (tag instanceof CompoundTag compoundTag) { return compoundTag; } else { @@ -312,7 +312,7 @@ public interface NetWorkCodecs { } }, tag -> tag); - NetWorkCodec> OPTIONAL_COMPOUND_TAG = new NetWorkCodec<>() { + NetworkCodec> OPTIONAL_COMPOUND_TAG = new NetworkCodec<>() { @Override public Optional decode(ByteBuf in) { int initialIndex = in.readerIndex(); @@ -347,7 +347,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VECTOR3F = new NetWorkCodec<>() { + NetworkCodec VECTOR3F = new NetworkCodec<>() { @Override public Vector3f decode(ByteBuf in) { return new Vector3f(in.readFloat(), in.readFloat(), in.readFloat()); @@ -361,7 +361,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec QUATERNIONF = new NetWorkCodec<>() { + NetworkCodec QUATERNIONF = new NetworkCodec<>() { @Override public Quaternionf decode(ByteBuf in) { return new Quaternionf(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); @@ -376,9 +376,9 @@ public interface NetWorkCodecs { } }; - NetWorkCodec CONTAINER_ID = VAR_INTEGER; + NetworkCodec CONTAINER_ID = VAR_INTEGER; - NetWorkCodec RGB_COLOR = new NetWorkCodec<>() { + NetworkCodec RGB_COLOR = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return 255 << 24 | in.readByte() & 0xFF << 16 | in.readByte() & 0xFF << 8 | in.readByte() & 0xFF; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java new file mode 100644 index 000000000..b6e1bd5a8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +public interface NetworkDecoder { + T decode(I in); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java new file mode 100644 index 000000000..0c79a9164 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +public interface NetworkEncoder { + void encode(O out, T value); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java new file mode 100644 index 000000000..0b96c1b7a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +@FunctionalInterface +public interface NetworkMemberEncoder { + void encode(T object, O object2); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/component/ComponentProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/component/ComponentProvider.java new file mode 100644 index 000000000..87b021160 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/component/ComponentProvider.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.plugin.text.component; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.AdventureHelper; + +import java.util.function.Function; + +import static net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine.CUSTOM_RESOLVERS; + +public sealed interface ComponentProvider extends Function + permits ComponentProvider.Constant, ComponentProvider.MiniMessage { + + static ComponentProvider constant(Component component) { + return new Constant(component); + } + + static ComponentProvider miniMessageOrConstant(String line) { + if (line.equals(AdventureHelper.customMiniMessage().stripTags(line, CUSTOM_RESOLVERS))) { + return constant(AdventureHelper.miniMessage().deserialize(line)); + } else { + return new MiniMessage(line); + } + } + + non-sealed class Constant implements ComponentProvider { + private final Component value; + + public Constant(final Component value) { + this.value = value; + } + + @Override + public Component apply(Context context) { + return this.value; + } + } + + non-sealed class MiniMessage implements ComponentProvider { + private final String value; + + public MiniMessage(final String value) { + this.value = value; + } + + @Override + public Component apply(Context context) { + return AdventureHelper.miniMessage().deserialize(this.value, context.tagResolvers()); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java index efefaa5ba..c095c0f32 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java @@ -9,12 +9,15 @@ import java.util.*; public abstract class AbstractMappedRegistry implements WritableRegistry { protected final ResourceKey> key; - protected final Map> byResourceLocation = new HashMap<>(512); - protected final Map, Holder.Reference> byResourceKey = new HashMap<>(512); - protected final List> byId = new ArrayList<>(512); + protected final Map> byResourceLocation; + protected final Map, Holder.Reference> byResourceKey; + protected final List> byId; - protected AbstractMappedRegistry(ResourceKey> key) { + protected AbstractMappedRegistry(ResourceKey> key, int expectedSize) { this.key = key; + this.byResourceLocation = new HashMap<>(expectedSize); + this.byResourceKey = new HashMap<>(expectedSize); + this.byId = new ArrayList<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index 806b20c22..e5db08ae8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.registry; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -40,53 +41,58 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.ResourceKey; public class BuiltInRegistries { - public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK); - public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY); - public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY); - public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY); - public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY); - public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY); - public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY); - public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY); - public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY); - public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); - public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY); - public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER); - public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY); - public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER); - public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY); - public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER); - public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY); - public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER); - public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY); - public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); - public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); - public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); - public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); - public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); - public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); - public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); - public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); - public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY); - public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); - public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY); - public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY); - public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY); - public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY); - public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE); - public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE); - public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE); - public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); - public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE); + public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512); + public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY, 64); + public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY, 64); + public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY, 64); + public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY, 16); + public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY, 32); + public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY, 32); + public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY, 16); + public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY, 16); + public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY, 16); + public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY, 16); + public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER, 16); + public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY, 16); + public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER, 16); + public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY, 16); + public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER, 16); + public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY, 16); + public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER, 16); + public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY, 16); + public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER, 16); + public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY, 16); + public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY, 16); + public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16); + public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16); + public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16); + public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16); + public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16); + public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128); + public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128); + public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY, 16); + public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY, 8); + public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE, 16); + public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE, 16); + public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE, 16); + public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16); + public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16); + public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); + public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 128); - private static Registry createConstantBoundRegistry(ResourceKey> key) { - return new ConstantBoundRegistry<>(key); + private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { + return new ConstantBoundRegistry<>(key, expectedSize); } - private static Registry createDynamicBoundRegistry(ResourceKey> key) { - return new DynamicBoundRegistry<>(key); + private static Registry createDynamicBoundRegistry(ResourceKey> key, int expectedSize) { + return new DynamicBoundRegistry<>(key, expectedSize); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java index cf3da4675..1c24d56fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java @@ -12,10 +12,11 @@ import java.util.Objects; public class ConstantBoundRegistry extends AbstractMappedRegistry { protected final Reference2IntMap toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); - protected final Map> byValue = new IdentityHashMap<>(512); + protected final Map> byValue; - public ConstantBoundRegistry(ResourceKey> key) { - super(key); + public ConstantBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); + this.byValue = new IdentityHashMap<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java index 47808d13f..fef205d22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java @@ -8,8 +8,8 @@ import java.util.Objects; public class DynamicBoundRegistry extends AbstractMappedRegistry { - public DynamicBoundRegistry(ResourceKey> key) { - super(key); + public DynamicBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index d06a53394..374ff31f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.registry; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -40,6 +41,9 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -83,4 +87,6 @@ public class Registries { public static final ResourceKey> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type")); public static final ResourceKey>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); public static final ResourceKey>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type")); + public static final ResourceKey>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type")); + public static final ResourceKey>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java index e48a6c429..a3174292d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java @@ -12,7 +12,8 @@ public enum SoundSource { NEUTRAL("neutral"), PLAYER("player"), AMBIENT("ambient"), - VOICE("voice"); + VOICE("voice"), + UI("ui"); private final String id; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index e988d1f68..a8ad7ed18 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -13,6 +13,8 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.json.JSONOptions; import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.adventure.NBTComponentSerializer; import net.momirealms.sparrow.nbt.adventure.NBTSerializerOptions; @@ -356,12 +358,15 @@ public class AdventureHelper { return AdventureHelper.plainTextContent(resultComponent); } - public static Component replaceText(Component text, Map replacements) { + public static Component replaceText(Component text, Map replacements, Context context) { String patternString = replacements.keySet().stream() .map(Pattern::quote) .collect(Collectors.joining("|")); return text.replaceText(builder -> builder.match(Pattern.compile(patternString)) - .replacement((result, b) -> replacements.get(result.group()))); + .replacement((result, b) -> + Optional.ofNullable(replacements.get(result.group())).orElseThrow(() -> new IllegalStateException("Could not find tag '" + result.group() + "'")).apply(context) + ) + ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index 54195dff8..ed0c90d9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -4,10 +4,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; public class MiscUtils { @@ -153,4 +150,17 @@ public class MiscUtils { return o; } } + + public static boolean matchRegex(String id, Set ids, boolean regexMatch) { + if (regexMatch) { + for (String regex : ids) { + if (id.matches(regex)) { + return true; + } + } + } else { + return ids.contains(id); + } + return false; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java b/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java new file mode 100644 index 000000000..dfe5b977a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.util; + +public class MutableBoolean { + private boolean value; + + public MutableBoolean(boolean value) { + this.value = value; + } + + public boolean booleanValue() { + return value; + } + + public void set(boolean value) { + this.value = value; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java index 4640a4140..8d2871260 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java @@ -29,7 +29,7 @@ public class SectionPosUtils { return nearby; } - public static Map toMap(Set sections, int minLightSection, int maxLightSection) { + public static Map toMap(Collection sections, int minLightSection, int maxLightSection) { int nBits = maxLightSection - minLightSection; Map nearby = new Long2ObjectOpenHashMap<>(Math.max(8, sections.size() / 2), 0.5f); for (SectionPos section : sections) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java index 686b5e95d..0bc751b85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.MCUtils; public class BlockPos extends Vec3i { + public static final BlockPos ZERO = new BlockPos(0, 0, 0); public BlockPos(int x, int y, int z) { super(x, y, z); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index 2a636bd69..9d68a0e1c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -1,52 +1,78 @@ package net.momirealms.craftengine.core.world; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; public abstract class CEWorld { public static final String REGION_DIRECTORY = "craftengine"; protected final World world; - protected final Map loadedChunkMap; + protected final ConcurrentLong2ReferenceChainedHashTable loadedChunkMap; protected final WorldDataStorage worldDataStorage; - protected final ReentrantReadWriteLock loadedChunkMapLock = new ReentrantReadWriteLock(); protected final WorldHeight worldHeightAccessor; protected final Set updatedSectionSet = ConcurrentHashMap.newKeySet(128); - - private CEChunk lastChunk; - private long lastChunkPos; + protected final List tickingBlockEntities = new ArrayList<>(); + protected final List pendingTickingBlockEntities = new ArrayList<>(); + protected boolean isTickingBlockEntities = false; + protected SchedulerTask syncTickTask; + protected SchedulerTask asyncTickTask; public CEWorld(World world, StorageAdaptor adaptor) { this.world = world; - this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f); this.worldDataStorage = adaptor.adapt(world); this.worldHeightAccessor = world.worldHeight(); - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } public CEWorld(World world, WorldDataStorage dataStorage) { this.world = world; - this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f); this.worldDataStorage = dataStorage; this.worldHeightAccessor = world.worldHeight(); - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; + } + + public void setTicking(boolean ticking) { + if (ticking) { + if (this.syncTickTask == null || this.syncTickTask.cancelled()) { + this.syncTickTask = CraftEngine.instance().scheduler().sync().runRepeating(this::syncTick, 1, 1); + } + if (this.asyncTickTask == null || this.asyncTickTask.cancelled()) { + this.asyncTickTask = CraftEngine.instance().scheduler().sync().runAsyncRepeating(this::asyncTick, 1, 1); + } + } else { + if (this.syncTickTask != null && !this.syncTickTask.cancelled()) { + this.syncTickTask.cancel(); + } + if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) { + this.asyncTickTask.cancel(); + } + } + } + + public String name() { + return this.world.name(); + } + + public UUID uuid() { + return this.world.uuid(); } public void save() { - this.loadedChunkMapLock.readLock().lock(); try { - for (Map.Entry entry : this.loadedChunkMap.entrySet()) { + for (ConcurrentLong2ReferenceChainedHashTable.TableEntry entry : this.loadedChunkMap.entrySet()) { CEChunk chunk = entry.getValue(); if (chunk.dirty()) { worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk); @@ -55,8 +81,6 @@ public abstract class CEWorld { } } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to save world chunks", e); - } finally { - this.loadedChunkMapLock.readLock().unlock(); } } @@ -65,44 +89,20 @@ public abstract class CEWorld { } public boolean isChunkLoaded(final long chunkPos) { - this.loadedChunkMapLock.readLock().lock(); - try { - return loadedChunkMap.containsKey(chunkPos); - } finally { - this.loadedChunkMapLock.readLock().unlock(); - } + return loadedChunkMap.containsKey(chunkPos); } public void addLoadedChunk(CEChunk chunk) { - this.loadedChunkMapLock.writeLock().lock(); - try { - this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk); - } finally { - this.loadedChunkMapLock.writeLock().unlock(); - } + this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk); } public void removeLoadedChunk(CEChunk chunk) { - this.loadedChunkMapLock.writeLock().lock(); - try { - this.loadedChunkMap.remove(chunk.chunkPos().longKey()); - if (this.lastChunk == chunk) { - this.lastChunk = null; - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; - } - } finally { - this.loadedChunkMapLock.writeLock().unlock(); - } + this.loadedChunkMap.remove(chunk.chunkPos().longKey()); } @Nullable public CEChunk getChunkAtIfLoaded(long chunkPos) { - this.loadedChunkMapLock.readLock().lock(); - try { - return getChunkAtIfLoadedMainThread(chunkPos); - } finally { - this.loadedChunkMapLock.readLock().unlock(); - } + return this.loadedChunkMap.get(chunkPos); } @Nullable @@ -111,27 +111,6 @@ public abstract class CEWorld { } @Nullable - public CEChunk getChunkAtIfLoadedMainThread(long chunkPos) { - if (chunkPos == this.lastChunkPos) { - return this.lastChunk; - } - CEChunk chunk = this.loadedChunkMap.get(chunkPos); - if (chunk != null) { - this.lastChunk = chunk; - this.lastChunkPos = chunkPos; - } - return chunk; - } - - @Nullable - public CEChunk getChunkAtIfLoadedMainThread(int x, int z) { - return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z)); - } - - public WorldHeight worldHeight() { - return worldHeightAccessor; - } - public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) { CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4); if (chunk == null) { @@ -140,6 +119,7 @@ public abstract class CEWorld { return chunk.getBlockState(x, y, z); } + @Nullable public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) { CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); if (chunk == null) { @@ -149,7 +129,7 @@ public abstract class CEWorld { } public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) { - if (worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { return false; } CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); @@ -160,6 +140,18 @@ public abstract class CEWorld { return true; } + @Nullable + public BlockEntity getBlockEntityAtIfLoaded(BlockPos blockPos) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + return null; + } + CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); + if (chunk == null) { + return null; + } + return chunk.getBlockEntity(blockPos, true); + } + public WorldDataStorage worldDataStorage() { return worldDataStorage; } @@ -172,5 +164,48 @@ public abstract class CEWorld { this.updatedSectionSet.addAll(pos); } - public abstract void tick(); + public WorldHeight worldHeight() { + return this.worldHeightAccessor; + } + + public void syncTick() { + this.tickBlockEntities(); + if (!Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public void asyncTick() { + if (Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public abstract void updateLight(); + + public void addBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingBlockEntities) { + this.pendingTickingBlockEntities.add(ticker); + } else { + this.tickingBlockEntities.add(ticker); + } + } + + protected void tickBlockEntities() { + this.isTickingBlockEntities = true; + if (!this.pendingTickingBlockEntities.isEmpty()) { + this.tickingBlockEntities.addAll(this.pendingTickingBlockEntities); + this.pendingTickingBlockEntities.clear(); + } + ReferenceOpenHashSet toRemove = new ReferenceOpenHashSet<>(); + for (TickingBlockEntity blockEntity : this.tickingBlockEntities) { + if (blockEntity.isValid()) { + blockEntity.tick(); + } else { + toRemove.add(blockEntity); + } + } + this.tickingBlockEntities.removeAll(toRemove); + this.isTickingBlockEntities = false; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java index a053afd79..32eb4788f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java @@ -65,10 +65,23 @@ public class ChunkPos { return longKey; } + public ChunkPos[] adjacentChunkPos() { + return adjacentChunkPos(this); + } + public static long asLong(int chunkX, int chunkZ) { return (long) chunkX & 4294967295L | ((long) chunkZ & 4294967295L) << 32; } + public static ChunkPos[] adjacentChunkPos(ChunkPos chunkPos) { + return new ChunkPos[] { + new ChunkPos(chunkPos.x, chunkPos.z - 1), + new ChunkPos(chunkPos.x, chunkPos.z + 1), + new ChunkPos(chunkPos.x + 1, chunkPos.z), + new ChunkPos(chunkPos.x - 1, chunkPos.z) + }; + } + @Override public final boolean equals(Object o) { if (o == null) return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java similarity index 64% rename from core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java rename to core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java index 5dac8cf13..947bc664d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java @@ -1,11 +1,15 @@ package net.momirealms.craftengine.core.world; +import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public interface BlockInWorld { +public interface ExistingBlock { default boolean canBeReplaced(BlockPlaceContext blockPlaceContext) { return false; @@ -18,15 +22,25 @@ public interface BlockInWorld { @Nullable CustomBlock customBlock(); + boolean isCustom(); + @Nullable ImmutableBlockState customBlockState(); + @NotNull + BlockStateWrapper blockState(); + + @NotNull + StatePropertyAccessor createStatePropertyAccessor(); + default WorldPosition position() { return new WorldPosition(world(), x(), y(), z()); } World world(); + Key id(); + int x(); int y(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java index 3897374da..fac06da92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java @@ -13,4 +13,8 @@ public class SectionPos extends Vec3i { public static SectionPos of(BlockPos pos) { return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4); } + + public static int sectionRelative(int rel) { + return rel & 15; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java index 7d49c6aaa..9413aff03 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java @@ -47,12 +47,12 @@ public class Vec3i implements Comparable { @Override public boolean equals(Object object) { - return this == object || object instanceof Vec3i vec3i && this.x() == vec3i.x() && this.y() == vec3i.y() && this.z() == vec3i.z(); + return this == object || object instanceof Vec3i vec3i && this.x == vec3i.x && this.y == vec3i.y && this.z == vec3i.z; } @Override public int hashCode() { - return (this.y() + this.z() * 31) * 31 + this.x(); + return (this.y + this.z * 31) * 31 + this.x; } @Override 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 fb4923698..a7d960454 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 @@ -21,9 +21,9 @@ public interface World { WorldHeight worldHeight(); - BlockInWorld getBlockAt(int x, int y, int z); + ExistingBlock getBlockAt(int x, int y, int z); - default BlockInWorld getBlockAt(final BlockPos pos) { + default ExistingBlock getBlockAt(final BlockPos pos) { return getBlockAt(pos.x(), pos.y(), pos.z()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index fb00cc0dc..cce78f674 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -1,37 +1,42 @@ package net.momirealms.craftengine.core.world.chunk; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.tick.*; +import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; +import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; +import java.util.*; public class CEChunk { - private boolean loaded; - private final CEWorld world; - private final ChunkPos chunkPos; - private final CESection[] sections; - private final WorldHeight worldHeightAccessor; - private final Map blockEntities; - private boolean dirty; + public final CEWorld world; + public final ChunkPos chunkPos; + public final CESection[] sections; + public final WorldHeight worldHeightAccessor; + public final Map blockEntities; + private volatile boolean dirty; + private volatile boolean loaded; + protected final Map tickingBlockEntitiesByPos = new HashMap<>(); public CEChunk(CEWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(16, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map blockEntities) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, ListTag blockEntitiesTag) { this.world = world; this.chunkPos = chunkPos; - this.blockEntities = blockEntities; + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 16), 0.5f); this.worldHeightAccessor = world.worldHeight(); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; @@ -44,10 +49,104 @@ public class CEChunk { } } this.fillEmptySection(); + List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); + for (BlockEntity blockEntity : blockEntities) { + this.setBlockEntity(blockEntity); + } } - public Map blockEntities() { - return this.blockEntities; + public void addBlockEntity(BlockEntity blockEntity) { + this.setBlockEntity(blockEntity); + this.replaceOrCreateTickingBlockEntity(blockEntity); + } + + public void removeBlockEntity(BlockPos blockPos) { + BlockEntity removedBlockEntity = this.blockEntities.remove(blockPos); + if (removedBlockEntity != null) { + removedBlockEntity.setValid(false); + } + } + + public void clearAllBlockEntities() { + this.blockEntities.values().forEach(e -> e.setValid(false)); + this.blockEntities.clear(); + this.tickingBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingBlockEntitiesByPos.clear(); + } + + public void replaceOrCreateTickingBlockEntity(T blockEntity) { + ImmutableBlockState blockState = blockEntity.blockState(); + BlockEntityTicker ticker = blockState.createBlockEntityTicker(this.world, blockEntity.type()); + if (ticker != null) { + this.tickingBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, ticker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeBlockEntityTicker(blockEntity.pos()); + } + } + + private void removeBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity blockEntity = this.tickingBlockEntitiesByPos.remove(pos); + if (blockEntity != null) { + blockEntity.setTicker(DummyTickingBlockEntity.INSTANCE); + } + } + + public void setBlockEntity(BlockEntity blockEntity) { + BlockPos pos = blockEntity.pos(); + ImmutableBlockState blockState = this.getBlockState(pos); + if (!blockState.hasBlockEntity()) { + Debugger.BLOCK_ENTITY.debug(() -> "Failed to add invalid block entity " + blockEntity.saveAsTag() + " at " + pos); + return; + } + // 设置方块实体所在世界 + blockEntity.setWorld(this.world); + blockEntity.setValid(true); + BlockEntity previous = this.blockEntities.put(pos, blockEntity); + // 标记旧的方块实体无效 + if (previous != null && previous != blockEntity) { + previous.setValid(false); + } + } + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos, boolean create) { + BlockEntity blockEntity = this.blockEntities.get(pos); + if (blockEntity == null) { + if (create) { + blockEntity = createBlockEntity(pos); + if (blockEntity != null) { + this.addBlockEntity(blockEntity); + } + } + } else { + if (!blockEntity.isValid()) { + this.blockEntities.remove(pos); + return null; + } + } + return blockEntity; + } + + private BlockEntity createBlockEntity(BlockPos pos) { + ImmutableBlockState blockState = this.getBlockState(pos); + if (!blockState.hasBlockEntity()) { + return null; + } + return Objects.requireNonNull(blockState.behavior().getEntityBehavior()).createBlockEntity(pos, blockState); + } + + public Map blockEntities() { + return Collections.unmodifiableMap(this.blockEntities); } public boolean dirty() { @@ -69,9 +168,9 @@ public class CEChunk { } private void fillEmptySection() { - for (int i = 0; i < sections.length; ++i) { - if (sections[i] == null) { - sections[i] = new CESection(world.worldHeight().getSectionYFromSectionIndex(i), + for (int i = 0; i < this.sections.length; ++i) { + if (this.sections[i] == null) { + this.sections[i] = new CESection(this.world.worldHeight().getSectionYFromSectionIndex(i), new PalettedContainer<>(null, EmptyBlock.STATE, PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE)); } } @@ -93,17 +192,17 @@ public class CEChunk { } } - @Nullable + @NotNull public ImmutableBlockState getBlockState(BlockPos pos) { return getBlockState(pos.x(), pos.y(), pos.z()); } - @Nullable + @NotNull public ImmutableBlockState getBlockState(int x, int y, int z) { int index = sectionIndex(SectionPos.blockToSectionCoord(y)); CESection section = this.sections[index]; if (section == null) { - return null; + return EmptyBlock.STATE; } return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15); } @@ -128,21 +227,21 @@ public class CEChunk { @NotNull public CEWorld world() { - return world; + return this.world; } @NotNull public ChunkPos chunkPos() { - return chunkPos; + return this.chunkPos; } @NotNull public CESection[] sections() { - return sections; + return this.sections; } public boolean isLoaded() { - return loaded; + return this.loaded; } public void load() { @@ -154,6 +253,7 @@ public class CEChunk { public void unload() { if (!this.loaded) return; this.world.removeLoadedChunk(this); + this.clearAllBlockEntities(); this.loaded = false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java index a7f9553ab..344509b5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java @@ -9,8 +9,8 @@ public class CESection { public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = SECTION_WIDTH * SECTION_WIDTH * SECTION_HEIGHT; - private final int sectionY; - private final PalettedContainer statesContainer; + public final int sectionY; + public final PalettedContainer statesContainer; public CESection(int sectionY, PalettedContainer statesContainer) { this.sectionY = sectionY; @@ -53,6 +53,6 @@ public class CESection { } public int sectionY() { - return sectionY; + return this.sectionY; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java new file mode 100644 index 000000000..744068aa6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.world.chunk; + +public class ChunkStatus { + + public ChunkStatus() { + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java index 8f8be31d9..0e1d59602 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -1,48 +1,49 @@ package net.momirealms.craftengine.core.world.chunk.serialization; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; -import org.jetbrains.annotations.ApiStatus; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public final class DefaultBlockEntitySerializer { - @ApiStatus.Experimental - public static ListTag serialize(Map tiles) { + public static ListTag serialize(Map tiles) { ListTag result = new ListTag(); - Map nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); - for (Map.Entry entry : tiles.entrySet()) { - int pos = entry.getKey(); - CompoundTag tag = entry.getValue().nbt(); - int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos}); - int[] newPoses = new int[previous.length + 1]; - System.arraycopy(previous, 0, newPoses, 0, previous.length); - newPoses[previous.length] = pos; - nbtToPosMap.put(tag, newPoses); - } - for (Map.Entry entry : nbtToPosMap.entrySet()) { - CompoundTag blockEntityTag = new CompoundTag(); - blockEntityTag.put("data", entry.getKey()); - blockEntityTag.putIntArray("pos", entry.getValue()); - result.add(blockEntityTag); - } - return result; - } - - @ApiStatus.Experimental - public static Map deserialize(ListTag tag) { - Map result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f); - for (int i = 0; i < tag.size(); i++) { - CompoundTag blockEntityTag = tag.getCompound(i); - CompoundTag data = blockEntityTag.getCompound("data"); - int[] pos = blockEntityTag.getIntArray("pos"); - for (int j = 0; j < pos.length; j++) { - result.put(j, new BlockEntityState(data)); + for (Map.Entry entry : tiles.entrySet()) { + BlockEntity entity = entry.getValue(); + if (entity.isValid()) { + result.add(entity.saveAsTag()); } } return result; } + + public static List deserialize(CEChunk chunk, ListTag tag) { + List blockEntities = new ArrayList<>(tag.size()); + for (int i = 0; i < tag.size(); i++) { + CompoundTag data = tag.getCompound(i); + Key id = Key.of(data.getString("id")); + BlockEntityType type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id); + if (type == null) { + Debugger.BLOCK_ENTITY.debug(() -> "Unknown block entity type: " + id); + } else { + BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); + ImmutableBlockState blockState = chunk.getBlockState(pos); + BlockEntity blockEntity = type.factory().create(pos, blockState); + blockEntity.loadCustomData(data); + blockEntities.add(blockEntity); + } + } + return blockEntities; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 1a44e0efc..fafceb99c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -47,6 +47,6 @@ public final class DefaultChunkSerializer { } } ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); - return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities)); + return new CEChunk(world, pos, sectionArray, blockEntities); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java index e8947d094..36d420260 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.InactiveCustomBlock; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; @@ -58,7 +59,20 @@ public final class DefaultSectionSerializer { CompoundTag palette = (CompoundTag) tag; String id = palette.getString("id"); CompoundTag data = palette.getCompound("properties"); - Key key = Key.of(id); + Key key; + if (Config.handleInvalidBlock()) { + String converted = Config.blockMappings().get(id); + if (converted == null) { + key = Key.of(id); + } else if (converted.isEmpty()) { + paletteEntries.add(EmptyBlock.STATE); + continue; + } else { + key = Key.of(converted); + } + } else { + key = Key.of(id); + } Holder owner = BuiltInRegistries.BLOCK.get(key).orElseGet(() -> { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder( ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), key)); diff --git a/gradle.properties b/gradle.properties index ac5eb779a..09b0c867e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.62.1 -config_version=44 -lang_version=24 +project_version=0.0.62.12 +config_version=45 +lang_version=25 project_group=net.momirealms latest_supported_version=1.21.8 @@ -40,23 +40,24 @@ commons_io_version=2.18.0 commons_imaging_version=1.0.0-alpha6 commons_lang3_version=3.17.0 sparrow_nbt_version=0.9.4 -sparrow_util_version=0.50.9 +sparrow_util_version=0.51 fastutil_version=8.5.15 -netty_version=4.1.121.Final +netty_version=4.1.124.Final joml_version=1.10.8 datafixerupper_version=8.0.16 mojang_brigadier_version=1.0.18 byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 -anti_grief_version=0.19 -nms_helper_version=1.0.58 +anti_grief_version=0.20 +nms_helper_version=1.0.74 evalex_version=3.5.0 reactive_streams_version=1.0.4 -amazon_awssdk_version=2.31.23 +amazon_awssdk_version=2.33.1 amazon_awssdk_eventstream_version=1.0.1 jimfs_version=1.3.0 authlib_version=6.0.58 +concurrent_util_version=0.0.3 # Proxy settings #systemProp.socks.proxyHost=127.0.0.1 diff --git a/libs/boosted-yaml-1.3.7.jar b/libs/boosted-yaml-1.3.7.jar index ea489cb34..b6481f3fa 100644 Binary files a/libs/boosted-yaml-1.3.7.jar and b/libs/boosted-yaml-1.3.7.jar differ diff --git a/libs/concurrentutil-0.0.3.jar b/libs/concurrentutil-0.0.3.jar new file mode 100644 index 000000000..27b20749b Binary files /dev/null and b/libs/concurrentutil-0.0.3.jar differ diff --git a/libs/worldguard-bukkit-7.0.14-dist.jar b/libs/worldguard-bukkit-7.0.14-dist.jar new file mode 100644 index 000000000..69d590e5e Binary files /dev/null and b/libs/worldguard-bukkit-7.0.14-dist.jar differ