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 656ba4791..a341e41fe 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 @@ -10,7 +10,8 @@ import net.momirealms.craftengine.bukkit.compatibility.leveler.*; import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils; -import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicMobsListener; +import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener; +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.skript.SkriptHook; @@ -39,6 +40,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager { private final Map levelerProviders; private boolean hasPlaceholderAPI; private boolean hasViaVersion; + private MythicSkillHelper skillExecute; public BukkitCompatibilityManager(BukkitCraftEngine plugin) { this.plugin = plugin; @@ -131,11 +133,16 @@ public class BukkitCompatibilityManager implements CompatibilityManager { } if (this.isPluginEnabled("MythicMobs")) { BukkitItemManager.instance().registerExternalItemSource(new MythicMobsSource()); - new MythicMobsListener(this.plugin); + new MythicItemDropListener(this.plugin); logHook("MythicMobs"); } } + @Override + public void executeMMSkill(String skill, float power, Player player) { + MythicSkillHelper.execute(skill, power, player); + } + @Override public void registerLevelerProvider(String plugin, LevelerProvider provider) { this.levelerProviders.put(plugin, provider); diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java similarity index 93% rename from bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java index 9c582cf1d..8b151df79 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java @@ -21,12 +21,12 @@ import org.bukkit.inventory.ItemStack; import java.lang.reflect.Constructor; -public class CraftEngineItemDrop extends ItemDrop implements IItemDrop { +public class MythicItemDrop extends ItemDrop implements IItemDrop { private final CustomItem customItem; private static final Constructor constructor$BukkitItemStack = ReflectionUtils.getConstructor(BukkitItemStack.class, ItemStack.class); private static final boolean useReflection = constructor$BukkitItemStack != null; - public CraftEngineItemDrop(String line, MythicLineConfig config, CustomItem customItem) { + public MythicItemDrop(String line, MythicLineConfig config, CustomItem customItem) { super(line, config); this.customItem = customItem; } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java similarity index 83% rename from bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java index 22101993b..96cacc0b7 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java @@ -8,10 +8,10 @@ import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -public class MythicMobsListener implements Listener { +public class MythicItemDropListener implements Listener { private final BukkitCraftEngine plugin; - public MythicMobsListener(BukkitCraftEngine plugin) { + public MythicItemDropListener(BukkitCraftEngine plugin) { this.plugin = plugin; Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin()); } @@ -24,7 +24,7 @@ public class MythicMobsListener implements Listener { this.plugin.itemManager().getCustomItem(itemId).ifPresent(customItem -> { String line = event.getContainer().getConfigLine(); MythicLineConfig config = event.getConfig(); - event.register(new CraftEngineItemDrop(line, config, customItem)); + event.register(new MythicItemDrop(line, config, customItem)); }); } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java new file mode 100644 index 000000000..8ac5a7356 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.bukkit.compatibility.mythicmobs; + +import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.core.utils.MythicUtil; +import net.momirealms.craftengine.core.entity.player.Player; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.List; + +public final class MythicSkillHelper { + + public static void execute(String skill, float power, Player player) { + org.bukkit.entity.Player casterPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + Location location = casterPlayer.getLocation(); + LivingEntity target = MythicUtil.getTargetedEntity(casterPlayer); + List targets = new ArrayList<>(); + List locations = null; + if (target != null) { + targets.add(target); + locations = List.of(target.getLocation()); + } + MythicBukkit.inst().getAPIHelper().castSkill(casterPlayer, skill, casterPlayer, location, targets, locations, power); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index b03240f25..04669495f 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -1,9 +1,12 @@ package net.momirealms.craftengine.bukkit.compatibility.worldedit; +import com.fastasyncworldedit.bukkit.FaweBukkitWorld; import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; +import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.ProcessorTraverser; import com.sk89q.worldedit.EditSession; @@ -24,6 +27,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.EmptyBlock; @@ -89,12 +93,14 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { if (levelChunk != null) { Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); CESection[] ceSections = ceChunk.sections(); - for (int i = 0; i < ceSections.length; i++) { - CESection ceSection = ceSections[i]; - Object section = sections[i]; - int finalI = i; - WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), - (injected) -> sections[finalI] = injected); + synchronized (sections) { + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + int finalI = i; + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + (injected) -> sections[finalI] = injected); + } } } } @@ -174,18 +180,18 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Override public @Nullable Operation commit() { - Operation operation = super.commit(); saveAllChunks(); + Operation operation = super.commit(); List chunks = new ArrayList<>(this.brokenChunks); this.brokenChunks.clear(); - Object worldServer = this.ceWorld.world().serverWorld(); - Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); - for (ChunkPos chunk : chunks) { - CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); - // only inject loaded chunks - if (loaded == null) continue; - injectLevelChunk(chunkSource, loaded); - } + Object worldServer = this.ceWorld.world().serverWorld(); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); + for (ChunkPos chunk : chunks) { + CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); + // only inject loaded chunks + if (loaded == null) continue; + injectLevelChunk(chunkSource, loaded); + } return operation; } @@ -214,11 +220,12 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { try { CEChunk ceChunk = Optional.ofNullable(this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ)) .orElse(this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ))); + CESection ceSection = ceChunk.sectionById(SectionPos.blockToSectionCoord(blockY)); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(newStateId); if (immutableBlockState == null) { - ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE); + ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, EmptyBlock.STATE); } else { - ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); + ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, immutableBlockState); } this.chunksToSave.add(ceChunk); } catch (IOException e) { 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 a8d5c862d..161171c20 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,23 +1,12 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.incendo.cloud.Command; -import org.incendo.cloud.bukkit.parser.location.LocationParser; -import org.incendo.cloud.parser.standard.IntegerParser; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; public class TestCommand extends BukkitCommandFeature { @@ -29,35 +18,9 @@ public class TestCommand extends BukkitCommandFeature { public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder .senderType(Player.class) - .required("location", LocationParser.locationParser()) - .required("remove", IntegerParser.integerParser()) .handler(context -> { Player player = context.sender(); - int removeEntityId = context.get("remove"); - if (removeEntityId >= 0) { - try { - Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{removeEntityId}); - plugin().adapt(player).sendPacket(packet, true); - player.sendMessage("发送成功"); - } catch (ReflectiveOperationException e) { - player.sendMessage("发送失败"); - } - return; - } - Location location = context.get("location"); - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List packets = new ArrayList<>(); - List cachedShulkerValues = new ArrayList<>(); - HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, cachedShulkerValues); // NO AI - // HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedShulkerValues); // Invisible - HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(true, cachedShulkerValues); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), location.x(), location.y(), location.z(), 0, location.getYaw(), - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(cachedShulkerValues))); - plugin().adapt(player).sendPackets(packets, true); - player.sendMessage("发送成功 id: " + entityId); + player.sendMessage("客户端模组状态: " + BukkitNetworkManager.instance().getUser(player).clientModEnabled()); }); } 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 21685624a..4a2ba84c7 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 @@ -29,6 +29,7 @@ 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.UnknownPayload; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; @@ -105,7 +106,7 @@ public class PacketConsumers { byte yHeadRot = buf.readByte(); int data = buf.readVarInt(); // Falling blocks - int remapped = remap(data); + int remapped = user.clientModEnabled() ? remapMOD(data) : remap(data); if (remapped != data) { int xa = buf.readShort(); int ya = buf.readShort(); @@ -428,7 +429,7 @@ public class PacketConsumers { if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) { return; } - int state = remap(before); + int state = user.clientModEnabled() ? remapMOD(before) : remap(before); if (state == before) { return; } @@ -450,7 +451,7 @@ public class PacketConsumers { BlockPos blockPos = buf.readBlockPos(); int state = buf.readInt(); boolean global = buf.readBoolean(); - int newState = remap(state); + int newState = user.clientModEnabled() ? remapMOD(state) : remap(state); if (newState == state) { return; } @@ -1006,7 +1007,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1046,7 +1047,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1086,7 +1087,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1884,34 +1885,38 @@ public class PacketConsumers { public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { try { - if (!VersionHelper.isOrAbove1_20_5()) return; + if (!VersionHelper.isOrAbove1_20_2()) return; Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet); + Payload clientPayload; if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { - Payload discardedPayload = DiscardedPayload.from(payload); - if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) + clientPayload = DiscardedPayload.from(payload); + } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$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; - FriendlyByteBuf buf = discardedPayload.toBuffer(); - NetWorkDataTypes dataType = NetWorkDataTypes.readType(buf); - if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { - int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf); - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (clientBlockRegistrySize != serverBlockRegistrySize) { - 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 (!VersionHelper.isOrAbove1_20_2()) return; - if (dataType.as(Boolean.class).decode(buf)) { - FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); - dataType.writeType(bufPayload); - dataType.as(Boolean.class).encode(bufPayload, true); - user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array()); - } + } + 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()); } } } catch (Throwable e) { 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 index 5655e1028..37da1618d 100644 --- 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 @@ -1,64 +1,28 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload; +import io.netty.buffer.ByteBuf; -import net.momirealms.craftengine.core.util.FriendlyByteBuf; +public enum NetWorkDataTypes { + CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER), + CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN); -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Function; + private final NetWorkCodec codec; -public class NetWorkDataTypes { - private static final Map> id2NetWorkDataTypes = new HashMap<>(); - - public static final NetWorkDataTypes CLIENT_CUSTOM_BLOCK = - new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt); - - public static final NetWorkDataTypes CANCEL_BLOCK_UPDATE = - new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean); - - static { - register(CLIENT_CUSTOM_BLOCK); - register(CANCEL_BLOCK_UPDATE); + NetWorkDataTypes(NetWorkCodec codec) { + this.codec = codec; } - private static void register(NetWorkDataTypes type) { - id2NetWorkDataTypes.put(type.id, type); + public NetWorkCodec codec() { + return codec; } - private final int id; - private final Function decoder; - private final BiConsumer encoder; - - public NetWorkDataTypes(int id, Function decoder, BiConsumer encoder) { - this.id = id; - this.decoder = decoder; - this.encoder = encoder; + @SuppressWarnings("unchecked") + public V decode(ByteBuf buf) { + return (V) codec.decode(buf); } - public T decode(FriendlyByteBuf buf) { - return decoder.apply(buf); - } - - public void encode(FriendlyByteBuf buf, T data) { - encoder.accept(buf, data); - } - - public int id() { - return id; - } - - public void writeType(FriendlyByteBuf buf) { - buf.writeVarInt(id); - } - - public static NetWorkDataTypes readType(FriendlyByteBuf buf) { - int id = buf.readVarInt(); - return id2NetWorkDataTypes.get(id); - } - - @SuppressWarnings({"unchecked", "unused"}) - public NetWorkDataTypes as(Class clazz) { - return (NetWorkDataTypes) this; + @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/UnknownPayload.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java new file mode 100644 index 000000000..4143eea27 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.Key; + +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); + Key channel = KeyUtils.resourceLocationToKey(id); + return new UnknownPayload(channel, data); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to create UnknownPayload", e); + return null; + } + } + + @Override + public FriendlyByteBuf toBuffer() { + return new FriendlyByteBuf(rawPayload); + } +} 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 b8e41241c..77076e777 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 @@ -3873,4 +3873,27 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to initialize HashOps", e); } } + + public static final Class clazz$SnowLayerBlock = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.BlockSnow", + "world.level.block.SnowLayerBlock" + ) + ); + + public static final Field field$SnowLayerBlock$LAYERS = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$SnowLayerBlock, clazz$IntegerProperty, 0 + ) + ); + + public static final Object instance$SnowLayerBlock$LAYERS; + + static { + try { + instance$SnowLayerBlock$LAYERS = field$SnowLayerBlock$LAYERS.get(null); + } catch (IllegalAccessException e) { + throw new ReflectionInitException("Failed to initialize SnowLayerBlock$LAYERS", e); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java index 2cf9d1613..34d2f0e65 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java @@ -17,6 +17,7 @@ public final class MBlocks { public static final Object SHORT_GRASS$defaultState; public static final Object SHULKER_BOX; public static final Object COMPOSTER; + public static final Object SNOW; private static Object getById(String id) { Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); @@ -35,5 +36,6 @@ public final class MBlocks { SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS); SHULKER_BOX = getById("shulker_box"); COMPOSTER = getById("composter"); + SNOW = getById("snow"); } } 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 6c1d427a9..6c11c869e 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 @@ -1248,7 +1248,9 @@ public final class NetworkReflections { ); public static final Constructor constructor$ClientboundCustomPayloadPacket = requireNonNull( - ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0) + VersionHelper.isOrAbove1_20_2() + ? ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, clazz$CustomPacketPayload) + : ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$FriendlyByteBuf) ); // 1.20.2+ @@ -1621,4 +1623,27 @@ public final class NetworkReflections { throw new ReflectionInitException("Failed to initialize HashedStack$STREAM_CODEC", e); } } + + // 1.20.2~1.20.4 + public static final Class clazz$UnknownPayload = MiscUtils.requireNonNullIf( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload") + ), + VersionHelper.isOrAbove1_20_2() && !VersionHelper.isOrAbove1_20_5() + ); + + // 1.20.2~1.20.4 + public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$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) + .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) + .orElse(null); } 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 74430caf7..5e78643a6 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 @@ -306,13 +306,20 @@ public class BukkitServerPlayer extends Player { public void sendCustomPayload(Key channel, byte[] data) { try { Object channelKey = KeyUtils.toResourceLocation(channel); - Object dataPayload; - if (DiscardedPayload.useNewMethod) { - dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data); + Object responsePacket; + if (VersionHelper.isOrAbove1_20_2()) { + Object dataPayload; + if (NetworkReflections.clazz$UnknownPayload != null) { + dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + } else if (DiscardedPayload.useNewMethod) { + dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data); + } else { + dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + } + responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload); } else { - dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelKey, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data))); } - Object responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload); this.sendPacket(responsePacket, true); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to send custom payload to " + name(), e); 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 e575d2f20..00c8dfc57 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 @@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; +import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -142,4 +143,10 @@ public class BlockStateUtils { Object blockOwner = getBlockOwner(state); return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0; } + + 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); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java index 170c50dd1..6a9ce89a4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java @@ -2,7 +2,10 @@ package net.momirealms.craftengine.bukkit.world; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +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.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -21,11 +24,15 @@ public class BukkitBlockInWorld implements BlockInWorld { @Override public boolean canBeReplaced(BlockPlaceContext context) { - ImmutableBlockState customState = CraftEngineBlocks.getCustomBlockState(this.block); + Object state = BlockStateUtils.getBlockState(this.block); + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); if (customState != null && !customState.isEmpty()) { return customState.behavior().canBeReplaced(context, customState); } - return this.block.isReplaceable(); + if (BlockStateUtils.getBlockOwner(state) == MBlocks.SNOW) { + return (int) FastNMS.INSTANCE.method$StateHolder$getValue(state, CoreReflections.instance$SnowLayerBlock$LAYERS) == 1; + } + return BlockStateUtils.isReplaceable(state); } @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 61aa6dabb..bd04de0e4 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 @@ -88,6 +88,11 @@ public class BukkitWorldManager implements WorldManager, Listener { } } + @Override + public CEWorld[] getWorlds() { + return this.worldArray; + } + private void resetWorldArray() { this.worldArray = this.worlds.values().toArray(new CEWorld[0]); } @@ -218,7 +223,6 @@ public class BukkitWorldManager implements WorldManager, Listener { this.lastVisitedUUID = null; } this.resetWorldArray(); - } finally { this.worldMapLock.writeLock().unlock(); } @@ -328,74 +332,76 @@ public class BukkitWorldManager implements WorldManager, Listener { Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ()); Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); - for (int i = 0; i < ceSections.length; i++) { - CESection ceSection = ceSections[i]; - Object section = sections[i]; - if (Config.syncCustomBlocks()) { - Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section); - Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer); - Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data); - boolean requiresSync = false; - if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) { - Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette); - if (BlockStateUtils.isCustomBlock(onlyBlockState)) { + synchronized (sections) { + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + if (Config.syncCustomBlocks()) { + Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section); + Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer); + Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data); + boolean requiresSync = false; + if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) { + Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette); + if (BlockStateUtils.isCustomBlock(onlyBlockState)) { + requiresSync = true; + } + } else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) { + Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette); + for (Object blockState : blockStates) { + if (blockState != null) { + if (BlockStateUtils.isCustomBlock(blockState)) { + requiresSync = true; + break; + } + } + } + } else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) { + Object biMap = CoreReflections.field$HashMapPalette$values.get(palette); + Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap); + for (Object blockState : blockStates) { + if (blockState != null) { + if (BlockStateUtils.isCustomBlock(blockState)) { + requiresSync = true; + break; + } + } + } + } else { requiresSync = true; } - } else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) { - Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette); - for (Object blockState : blockStates) { - if (blockState != null) { - if (BlockStateUtils.isCustomBlock(blockState)) { - requiresSync = true; - break; - } - } - } - } else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) { - Object biMap = CoreReflections.field$HashMapPalette$values.get(palette); - Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap); - for (Object blockState : blockStates) { - if (blockState != null) { - if (BlockStateUtils.isCustomBlock(blockState)) { - requiresSync = true; - break; - } - } - } - } else { - requiresSync = true; - } - if (requiresSync) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < 16; y++) { - Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z); - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState); - if (optionalCustomState.isPresent()) { - ceSection.setBlockState(x, y, z, optionalCustomState.get()); + if (requiresSync) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < 16; y++) { + Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState); + if (optionalCustomState.isPresent()) { + ceSection.setBlockState(x, y, z, optionalCustomState.get()); + } } } } } } - } - if (Config.restoreCustomBlocks()) { - if (!ceSection.statesContainer().isEmpty()) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < 16; y++) { - ImmutableBlockState customState = ceSection.getBlockState(x, y, z); - if (!customState.isEmpty() && customState.customBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false); + if (Config.restoreCustomBlocks()) { + if (!ceSection.statesContainer().isEmpty()) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < 16; y++) { + ImmutableBlockState customState = ceSection.getBlockState(x, y, z); + if (!customState.isEmpty() && customState.customBlockState() != null) { + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false); + } } } } } } + int finalI = i; + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + (injected) -> sections[finalI] = injected); } - int finalI = i; - WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), - (injected) -> sections[finalI] = injected); } if (Config.enableRecipeSystem()) { @SuppressWarnings("unchecked") diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 5acc916c1..9222673b8 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -84,8 +84,6 @@ resource-pack: ip: "localhost" port: 8163 protocol: "http" - # The optional URL must be complete and include a trailing slash / at the end. - #url: "http://localhost:8163/" deny-non-minecraft-request: true one-time-token: true rate-limit: @@ -380,9 +378,9 @@ chunk-system: # Settings for injection injection: # Requires a restart to apply - # SECTION: Inject the LevelChunkSection (Faster & Experimental) since 0.0.53 + # SECTION: Inject the LevelChunkSection # PALETTE: Inject the PalettedContainer - target: PALETTE + target: SECTION # Enables faster injection method # Note: May not work with certain server forks that alter chunk class structure (In most cases it won't conflict) use-fast-method: false diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 56a24d8f5..375cfd1f0 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -393,6 +393,7 @@ warning.config.function.potion_effect.missing_potion_effect: "Issue foun warning.config.function.set_cooldown.missing_time: "Issue found in file - The config '' is missing the required 'time' argument for 'set_cooldown' function." warning.config.function.set_cooldown.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'set_cooldown' function." warning.config.function.remove_cooldown.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'remove_cooldown' function." +warning.config.function.mythic_mobs_skill.missing_skill: "Issue found in file - The config '' is missing the required 'skill' argument for 'mythic_mobs_skill' function." warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector." warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''." warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 11b6758f2..91bfbf70c 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -391,6 +391,7 @@ warning.config.function.potion_effect.missing_potion_effect: "在文件 warning.config.function.set_cooldown.missing_time: "在文件 中发现问题 - 配置项 '' 缺少 'set_cooldown' 函数必需的 'time' 参数" warning.config.function.set_cooldown.missing_id: "在文件 中发现问题 - 配置项 '' 缺少 'set_cooldown' 函数必需的 'id' 参数" warning.config.function.remove_cooldown.missing_id: "在文件 中发现问题 - 配置项 '' 缺少 'remove_cooldown' 函数必需的 'id' 参数" +warning.config.function.mythic_mobs_skill.missing_skill: "在文件 中发现问题 - 配置项 '' 缺少 'mythic_mobs_skill' 函数必需的 'skill' 参数" warning.config.selector.missing_type: "在文件 中发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器类型 ''" warning.config.selector.invalid_target: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器目标 ''" diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index 5636e7ae9..a54c56afa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.*; import java.util.Map; -public class Properties { +public final class Properties { public static final Key BOOLEAN = Key.of("craftengine:boolean"); public static final Key INT = Key.of("craftengine:int"); public static final Key STRING = Key.of("craftengine:string"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 6b75dc648..6865d2cf7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -9,11 +9,14 @@ import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.equipment.*; import net.momirealms.craftengine.core.item.modifier.*; +import net.momirealms.craftengine.core.item.modifier.lore.DynamicLoreModifier; +import net.momirealms.craftengine.core.item.modifier.lore.LoreModifier; import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; +import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.pack.model.*; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; @@ -534,15 +537,12 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl return new CustomNameModifier<>(name); }, "custom-name", "item-name", "display-name"); } + registerDataType(LoreModifier::createLoreModifier, "lore", "display-lore", "description"); registerDataType((obj) -> { - List lore = MiscUtils.getAsStringList(obj); - return new LoreModifier<>(lore); - }, "lore", "display-lore", "description"); - registerDataType((obj) -> { - Map> dynamicLore = new LinkedHashMap<>(); + Map> dynamicLore = new LinkedHashMap<>(); if (obj instanceof Map map) { for (Map.Entry entry : map.entrySet()) { - dynamicLore.put(entry.getKey().toString(), MiscUtils.getAsStringList(entry.getValue())); + dynamicLore.put(entry.getKey().toString(), LoreModifier.createLoreModifier(entry.getValue())); } } return new DynamicLoreModifier<>(dynamicLore); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java index d403ba310..365582553 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java @@ -16,7 +16,6 @@ import net.momirealms.sparrow.nbt.Tag; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; public abstract class ItemFactory, I> { protected final CraftEngine plugin; @@ -112,7 +111,7 @@ public abstract class ItemFactory, I> { protected void loreComponent(W item, List component) { if (component != null && !component.isEmpty()) { - loreJson(item, component.stream().map(AdventureHelper::componentToJson).collect(Collectors.toList())); + loreJson(item, component.stream().map(AdventureHelper::componentToJson).toList()); } else { loreJson(item, null); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java deleted file mode 100644 index 6c5c88a59..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java +++ /dev/null @@ -1,65 +0,0 @@ -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.NetworkItemHandler; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; - -import java.util.ArrayList; -import java.util.List; - -public class LoreModifier implements ItemDataModifier { - private final List argument; - - public LoreModifier(List argument) { - if (Config.addNonItalicTag()) { - List processed = new ArrayList<>(argument.size()); - for (String arg : argument) { - if (arg.startsWith("")) { - processed.add(arg); - } else { - processed.add("" + arg); - } - } - this.argument = processed; - } else { - this.argument = argument; - } - } - - @Override - public String name() { - return "lore"; - } - - @Override - public Item apply(Item item, ItemBuildContext context) { - item.loreComponent(this.argument.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); - return item; - } - - @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.LORE); - if (previous != null) { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "Lore"); - if (previous != null) { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } - return item; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java rename to core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java index 65392a1b8..5f36228d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java @@ -1,31 +1,29 @@ -package net.momirealms.craftengine.core.item.modifier; +package net.momirealms.craftengine.core.item.modifier.lore; 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.NetworkItemHandler; -import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; -import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Optional; -public class DynamicLoreModifier implements ItemDataModifier { +public final class DynamicLoreModifier implements ItemDataModifier { public static final String CONTEXT_TAG_KEY = "craftengine:display_context"; - private final Map> displayContexts; - private final String defaultContext; + private final Map> displayContexts; + private final LoreModifier defaultModifier; - public DynamicLoreModifier(Map> displayContexts) { - this.defaultContext = displayContexts.keySet().iterator().next(); + public DynamicLoreModifier(Map> displayContexts) { this.displayContexts = displayContexts; + this.defaultModifier = displayContexts.values().iterator().next(); } - public Map> displayContexts() { - return Collections.unmodifiableMap(this.displayContexts); + public Map> displayContexts() { + return displayContexts; } @Override @@ -35,13 +33,12 @@ public class DynamicLoreModifier implements ItemDataModifier { @Override public Item apply(Item item, ItemBuildContext context) { - String displayContext = Optional.ofNullable(item.getJavaTag(CONTEXT_TAG_KEY)).orElse(this.defaultContext).toString(); - List lore = this.displayContexts.get(displayContext); + String displayContext = Optional.ofNullable(item.getJavaTag(CONTEXT_TAG_KEY)).orElse(this.defaultModifier).toString(); + LoreModifier lore = this.displayContexts.get(displayContext); if (lore == null) { - lore = this.displayContexts.get(this.defaultContext); + lore = this.defaultModifier; } - item.loreComponent(lore.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); - return item; + return lore.apply(item, context); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java new file mode 100644 index 000000000..0b6b8ce34 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.TriFunction; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +// todo 可以考虑未来添加条件系统 +public record LoreModification(Operation operation, boolean split, String[] content) { + + public Stream apply(Stream lore, ItemBuildContext context) { + return this.operation.function.apply(lore, context, this); + } + + public Stream parseAsStream(ItemBuildContext context) { + Stream parsed = Arrays.stream(this.content).map(string -> AdventureHelper.miniMessage().deserialize(string, context.tagResolvers())); + return this.split ? parsed.map(AdventureHelper::splitLines).flatMap(List::stream) : parsed; + } + + public List parseAsList(ItemBuildContext context) { + return this.parseAsStream(context).collect(Collectors.toList()); + } + + public enum Operation { + APPEND((s, c, modification) -> Stream.concat(s, modification.parseAsStream(c))), + PREPEND((s, c, modification) -> Stream.concat(modification.parseAsStream(c), s)); + + private final TriFunction, ItemBuildContext, LoreModification, Stream> function; + + Operation(TriFunction, ItemBuildContext, LoreModification, Stream> function) { + this.function = function; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java new file mode 100644 index 000000000..7e2c4df28 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import org.jetbrains.annotations.NotNull; + +public record LoreModificationHolder(LoreModification modification, int priority) implements Comparable { + + @Override + public int compareTo(@NotNull LoreModificationHolder o) { + return Integer.compare(priority, o.priority); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java new file mode 100644 index 000000000..8e998115d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java @@ -0,0 +1,126 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import net.kyori.adventure.text.Component; +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.NetworkItemHandler; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; + +import java.util.*; +import java.util.stream.Stream; + +public sealed interface LoreModifier extends ItemDataModifier + permits LoreModifier.EmptyLoreModifier, LoreModifier.CompositeLoreModifier, LoreModifier.DoubleLoreModifier, LoreModifier.SingleLoreModifier { + + @Override + default String name() { + return "lore"; + } + + @Override + default Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { + if (VersionHelper.isOrAbove1_20_5()) { + Tag previous = item.getSparrowNBTComponent(ComponentKeys.LORE); + if (previous != null) networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + else networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } else { + Tag previous = item.getTag("display", "Lore"); + if (previous != null) networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + else networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + return item; + } + + static LoreModifier createLoreModifier(Object arg) { + List rawLoreData = MiscUtils.getAsList(arg, Object.class); + String[] rawLore = new String[rawLoreData.size()]; + label_all_string_check: { + for (int i = 0; i < rawLore.length; i++) { + Object o = rawLoreData.get(i); + if (o instanceof Map) { + break label_all_string_check; + } else { + rawLore[i] = o.toString(); + } + } + return new SingleLoreModifier<>(new LoreModification(LoreModification.Operation.APPEND, false, rawLore)); + } + + List modifications = new ArrayList<>(rawLoreData.size() + 1); + int lastPriority = 0; + for (Object o : rawLoreData) { + if (o instanceof Map complexLore) { + String[] content = MiscUtils.getAsStringArray(complexLore.get("content")); + LoreModification.Operation operation = ResourceConfigUtils.getAsEnum(Optional.ofNullable(complexLore.get("operation")).map(String::valueOf).orElse(null), LoreModification.Operation.class, LoreModification.Operation.APPEND); + lastPriority = Optional.ofNullable(complexLore.get("priority")).map(it -> ResourceConfigUtils.getAsInt(it, "priority")).orElse(lastPriority); + boolean split = ResourceConfigUtils.getAsBoolean(complexLore.get("split-lines"), "split-lines"); + modifications.add(new LoreModificationHolder(new LoreModification(operation, split, content), lastPriority)); + } + } + modifications.sort(LoreModificationHolder::compareTo); + return switch (modifications.size()) { + case 0 -> new EmptyLoreModifier<>(); + case 1 -> new SingleLoreModifier<>(modifications.get(0).modification()); + case 2 -> new DoubleLoreModifier<>(modifications.get(0).modification(), modifications.get(1).modification()); + default -> new CompositeLoreModifier<>(modifications.stream().map(LoreModificationHolder::modification).toArray(LoreModification[]::new)); + }; + } + + non-sealed class EmptyLoreModifier implements LoreModifier { + + @Override + public Item apply(Item item, ItemBuildContext context) { + return item; + } + } + + non-sealed class SingleLoreModifier implements LoreModifier { + private final LoreModification modification; + + public SingleLoreModifier(LoreModification modification) { + this.modification = modification; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(this.modification.parseAsList(context)); + return item; + } + } + + non-sealed class DoubleLoreModifier implements LoreModifier { + private final LoreModification modification1; + private final LoreModification modification2; + + public DoubleLoreModifier(LoreModification m1, LoreModification m2) { + this.modification1 = m1; + this.modification2 = m2; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(this.modification2.apply(this.modification1.apply(Stream.empty(), context), context).toList()); + return item; + } + } + + non-sealed class CompositeLoreModifier implements LoreModifier { + private final LoreModification[] modifications; + + public CompositeLoreModifier(LoreModification... modifications) { + this.modifications = modifications; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(Arrays.stream(this.modifications).reduce(Stream.empty(), (stream, modification) -> modification.apply(stream, context), Stream::concat).toList()); + return item; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java index 4e580df18..1d6c48e33 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java @@ -20,7 +20,7 @@ public final class ResourceLocation { } public static boolean isValidNamespace(String namespace) { - for(int i = 0; i < namespace.length(); ++i) { + for (int i = 0; i < namespace.length(); ++i) { if (!validNamespaceChar(namespace.charAt(i))) { return false; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java index 83b15f39b..f38d30c1f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java @@ -34,4 +34,6 @@ public interface CompatibilityManager { String parse(Player player1, Player player2, String text); int getPlayerProtocolVersion(UUID uuid); + + void executeMMSkill(String skill, float power, Player player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java index 1541a39d2..c61dba386 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java @@ -38,6 +38,7 @@ public class EventFunctions { register(CommonFunctions.LEVELER_EXP, new LevelerExpFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.SET_COOLDOWN, new SetCooldownFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REMOVE_COOLDOWN, new RemoveCooldownFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.MYTHIC_MOBS_SKILL, new MythicMobsSkillFunction.FactoryImpl<>(EventConditions::fromMap)); } public static void register(Key key, FunctionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index 8c0bc6435..055e3260a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java @@ -27,4 +27,5 @@ public final class CommonFunctions { public static final Key DROP_LOOT = Key.of("craftengine:drop_loot"); public static final Key SWING_HAND = Key.of("craftengine:swing_hand"); public static final Key LEVELER_EXP = Key.of("craftengine:leveler_exp"); + public static final Key MYTHIC_MOBS_SKILL = Key.of("craftengine:mythic_mobs_skill"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java new file mode 100644 index 000000000..031ed08da --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; + +public class MythicMobsSkillFunction extends AbstractConditionalFunction { + private final String skill; + private final float power; + + public MythicMobsSkillFunction(String skill, float power, List> predicates) { + super(predicates); + this.skill = skill; + this.power = power; + } + + @Override + protected void runInternal(CTX ctx) { + ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { + CraftEngine.instance().compatibilityManager().executeMMSkill(this.skill, this.power, it); + }); + } + + @Override + public Key type() { + return CommonFunctions.MYTHIC_MOBS_SKILL; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map args) { + String skill = ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("skill"), "warning.config.function.mythic_mobs_skill.missing_skill"); + float power = ResourceConfigUtils.getAsFloat(args.getOrDefault("power", 1.0), "power"); + return new MythicMobsSkillFunction<>(skill, power, getPredicates(args)); + } + } +} 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 3ed1f6048..e988d1f68 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 @@ -5,7 +5,9 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentIteratorType; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -15,7 +17,7 @@ import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.adventure.NBTComponentSerializer; import net.momirealms.sparrow.nbt.adventure.NBTSerializerOptions; -import java.util.Map; +import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -29,6 +31,21 @@ public class AdventureHelper { private final MiniMessage miniMessageCustom; private final GsonComponentSerializer gsonComponentSerializer; private final NBTComponentSerializer nbtComponentSerializer; + private static final TextReplacementConfig REPLACE_LF = TextReplacementConfig.builder().matchLiteral("\n").replacement(Component.newline()).build(); + /** + * This iterator slices a component into individual parts that + *
    + *
  • Can be used individually without style loss
  • + *
  • Can be concatenated to form the original component, given that children are dropped
  • + *
+ * Any {@link net.kyori.adventure.text.ComponentIteratorFlag}s are ignored. + */ + private static final ComponentIteratorType SLICER = (component, deque, flags) -> { + final List children = component.children(); + for (int i = children.size() - 1; i >= 0; i--) { + deque.addFirst(children.get(i).applyFallbackStyle(component.style())); + } + }; private AdventureHelper() { this.miniMessage = MiniMessage.builder().build(); @@ -209,6 +226,24 @@ public class AdventureHelper { return getNBT().deserialize(tag); } + public static List splitLines(Component component) { + List result = new ArrayList<>(1); + Component line = Component.empty(); + for (Iterator it = component.replaceText(REPLACE_LF).iterator(SLICER); it.hasNext(); ) { + Component child = it.next().children(Collections.emptyList()); + if (child instanceof TextComponent text && text.content().equals(Component.newline().content())) { + result.add(line.compact()); + line = Component.empty(); + } else { + line = line.append(child); + } + } + if (Component.IS_NOT_EMPTY.test(line)) { + result.add(line.compact()); + } + return result; + } + /** * Checks if a character is a legacy color code. * 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 f3b1bf95c..54195dff8 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 @@ -51,6 +51,20 @@ public class MiscUtils { return list; } + public static String[] getAsStringArray(Object o) { + if (o instanceof List list) { + String[] array = new String[list.size()]; + for (int i = 0; i < array.length; i++) { + array[i] = list.get(i).toString(); + } + return array; + } else if (o != null) { + return new String[]{o.toString()}; + } else { + return new String[0]; + } + } + @SuppressWarnings("unchecked") public static List getAsList(Object o, Class clazz) { if (o instanceof List list) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index b6a40aa27..dfdaa4317 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -5,10 +5,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -20,6 +17,17 @@ public final class ResourceConfigUtils { return raw != null ? function.apply(raw) : defaultValue; } + public static > E getAsEnum(Object o, Class clazz, E defaultValue) { + if (o == null) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + return defaultValue; + } + } + public static T requireNonNullOrThrow(T obj, String node) { if (obj == null) throw new LocalizedResourceConfigException(node); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java index 5c83b592b..d6a746cbd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java @@ -13,6 +13,8 @@ public interface WorldManager extends Manageable { CEWorld getWorld(UUID uuid); + CEWorld[] getWorlds(); + void loadWorld(World world); void loadWorld(CEWorld world); 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 45b0a52b8..fb00cc0dc 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 @@ -113,7 +113,7 @@ public class CEChunk { return this.sections[index]; } - @Nullable + @NotNull public CESection sectionById(int sectionId) { return this.sections[sectionIndex(sectionId)]; } diff --git a/gradle.properties b/gradle.properties index 649a773b0..2c126627e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.60.3 -config_version=42 +project_version=0.0.60.5 +config_version=43 lang_version=22 project_group=net.momirealms latest_supported_version=1.21.8 @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.18 -nms_helper_version=1.0.36 +nms_helper_version=1.0.37 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23