diff --git a/leaves-server/minecraft-patches/features/0034-Item-overstack-util.patch b/leaves-server/minecraft-patches/features/0034-Item-overstack-util.patch index c66ecf71..806b7bb8 100644 --- a/leaves-server/minecraft-patches/features/0034-Item-overstack-util.patch +++ b/leaves-server/minecraft-patches/features/0034-Item-overstack-util.patch @@ -227,7 +227,7 @@ index 02d2efef2dc0f0e12eac0c71fa290af706f7694d..99f109e2653eff10c011f380694bd77a default SlotAccess getChestVehicleSlot(final int index) { diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 042d227fcfd31037fd7b64ba5872bb2e7f59d6c5..49abae7db7d7a9ee967ea24b7503e1e9489d4005 100644 +index 042d227fcfd31037fd7b64ba5872bb2e7f59d6c5..8b27afcbe3013344158424da112d05edcda0c7e9 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -271,6 +271,13 @@ public abstract class AbstractContainerMenu { @@ -322,7 +322,7 @@ index 042d227fcfd31037fd7b64ba5872bb2e7f59d6c5..49abae7db7d7a9ee967ea24b7503e1e9 ItemStack item = container.getItem(i); if (!item.isEmpty()) { - f += (float)item.getCount() / container.getMaxStackSize(item); -+ f += (float)item.getCount() / container.getMaxStackLeaves(item); // Leaves - item over-stack util ++ f += Math.clamp((float) item.getCount() / container.getMaxStackSize(item), 0f, 1f); // Leaves - item over-stack util } } diff --git a/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch b/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch index 272a3a53..7cb7d9b0 100644 --- a/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch +++ b/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch @@ -4,6 +4,21 @@ Date: Wed, 5 Feb 2025 23:11:32 +0800 Subject: [PATCH] Servux Protocol +diff --git a/net/minecraft/server/ServerTickRateManager.java b/net/minecraft/server/ServerTickRateManager.java +index 40338efd1c0e56d869d03f1d0687e7ff0fcbf11a..c0504614b9239a69f2a6a49d964a97647469c1e4 100644 +--- a/net/minecraft/server/ServerTickRateManager.java ++++ b/net/minecraft/server/ServerTickRateManager.java +@@ -128,4 +128,10 @@ public class ServerTickRateManager extends TickRateManager { + player.connection.send(ClientboundTickingStatePacket.from(this)); + player.connection.send(ClientboundTickingStepPacket.from(this)); + } ++ ++ // Leaves start - servux ++ public long getRemainingSprintTicks() { ++ return remainingSprintTicks; ++ } ++ // Leaves end - servux + } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 5072dc7ac71e1640b2aad35c3c3560e0860ece94..f78c4f3f2047564730050f16693c00aa2c29a4e0 100644 --- a/net/minecraft/server/level/ServerLevel.java diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java b/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java index fdea2ec2..27b1de4e 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java @@ -27,6 +27,7 @@ import org.leavesmc.leaves.protocol.CarpetServerProtocol.CarpetRule; import org.leavesmc.leaves.protocol.CarpetServerProtocol.CarpetRules; import org.leavesmc.leaves.protocol.bladeren.BladerenProtocol.LeavesFeature; import org.leavesmc.leaves.protocol.bladeren.BladerenProtocol.LeavesFeatureSet; +import org.leavesmc.leaves.protocol.servux.logger.DataLogger; import org.leavesmc.leaves.region.RegionFileFormat; import org.leavesmc.leaves.util.MathUtils; @@ -865,6 +866,15 @@ public final class LeavesConfig { @GlobalConfig("hud-metadata-protocol") public boolean hudMetadataProtocol = false; + @GlobalConfig("hud-logger-protocol") + public boolean hudLoggerProtocol = false; + + @GlobalConfig("hud-enabled-loggers") + public List hudEnabledLoggers = List.of(DataLogger.Type.TPS, DataLogger.Type.MOB_CAPS); + + @GlobalConfig("hud-update-interval") + public int hudUpdateInterval = 1; + @GlobalConfig("hud-metadata-protocol-share-seed") public boolean hudMetadataShareSeed = true; diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/LitematicaEasyPlaceProtocol.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/LitematicaEasyPlaceProtocol.java index ce7ef9ad..16da03c9 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/LitematicaEasyPlaceProtocol.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/LitematicaEasyPlaceProtocol.java @@ -1,6 +1,5 @@ package org.leavesmc.leaves.protocol; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -10,6 +9,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.EnumProperty; @@ -23,7 +23,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; +import java.util.function.BiFunction; public class LitematicaEasyPlaceProtocol { @@ -51,9 +51,8 @@ public class LitematicaEasyPlaceProtocol { BlockStateProperties.ROTATION_16 ); - public static final ImmutableMap, ?> BLACKLISTED_PROPERTIES = ImmutableMap.of( - BlockStateProperties.WATERLOGGED, false, - BlockStateProperties.POWERED, false + public static final ImmutableSet> DYNAMIC_PROPERTIES = ImmutableSet.of( + (state, original) -> state.setValue(BlockStateProperties.WATERLOGGED, original.is(Blocks.WATER)) ); public static BlockState applyPlacementProtocol(BlockState state, BlockPlaceContext context) { @@ -70,6 +69,14 @@ public class LitematicaEasyPlaceProtocol { } EnumProperty property = CarpetAlternativeBlockPlacement.getFirstDirectionProperty(state); + BlockState original = context.getWorld().getBlockStateIfLoaded(context.pos); + if (original == null) { + return oldState; + } + + for (var func : DYNAMIC_PROPERTIES) { + state = func.apply(state, original); + } if (property != null && property != BlockStateProperties.VERTICAL_DIRECTION) { state = applyDirectionProperty(state, context, property, protocolValue); @@ -97,7 +104,7 @@ public class LitematicaEasyPlaceProtocol { if (property != null && property.equals(p)) { continue; } - if (!WHITELISTED_PROPERTIES.contains(p) || BLACKLISTED_PROPERTIES.containsKey(p)) { + if (!WHITELISTED_PROPERTIES.contains(p)) { continue; } @@ -129,12 +136,6 @@ public class LitematicaEasyPlaceProtocol { LeavesLogger.LOGGER.warning("Exception trying to apply placement protocol value", e); } - for (Map.Entry, ?> p : BLACKLISTED_PROPERTIES.entrySet()) { - if (state.hasProperty(p.getKey())) { - state = state.setValue((Property) p.getKey(), (T) p.getValue()); - } - } - if (state.canSurvive(context.getWorld(), context.getPos())) { return state; } else { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java index 1cac9120..1e974b1b 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java @@ -14,6 +14,7 @@ public abstract class AbstractInvokerHolder { protected final T handler; protected final Class returnType; protected final Class[] parameterTypes; + protected final boolean isStatic; protected AbstractInvokerHolder(LeavesProtocol owner, Method invoker, T handler, @Nullable Class returnType, @NotNull Class... parameterTypes) { this.owner = owner; @@ -21,6 +22,7 @@ public abstract class AbstractInvokerHolder { this.handler = handler; this.returnType = returnType; this.parameterTypes = parameterTypes; + this.isStatic = Modifier.isStatic(invoker.getModifiers()); validateMethodSignature(); } @@ -58,7 +60,7 @@ public abstract class AbstractInvokerHolder { return null; } try { - if (Modifier.isStatic(invoker.getModifiers())) { + if (isStatic) { return invoker.invoke(null, args); } else { return invoker.invoke(owner, args); diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxEntityDataProtocol.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxEntityDataProtocol.java index f28d021e..79f48001 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxEntityDataProtocol.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxEntityDataProtocol.java @@ -39,6 +39,11 @@ public class ServuxEntityDataProtocol implements LeavesProtocol { sendMetadata(player); } + @ProtocolHandler.PlayerLeave + public static void onPlayerLeave(ServerPlayer player) { + readingSessionKeys.remove(player.getUUID()); + } + @ProtocolHandler.PayloadReceiver(payload = EntityDataPayload.class) public static void onPacketReceive(ServerPlayer player, EntityDataPayload payload) { switch (payload.packetType) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxHudDataProtocol.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxHudDataProtocol.java index 69022ef4..404de7f2 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxHudDataProtocol.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxHudDataProtocol.java @@ -8,6 +8,7 @@ import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -22,7 +23,9 @@ import org.leavesmc.leaves.protocol.core.LeavesCustomPayload; import org.leavesmc.leaves.protocol.core.LeavesProtocol; import org.leavesmc.leaves.protocol.core.ProtocolHandler; import org.leavesmc.leaves.protocol.core.ProtocolUtils; +import org.leavesmc.leaves.protocol.servux.logger.DataLogger; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -32,13 +35,36 @@ import java.util.Map; @LeavesProtocol.Register(namespace = "servux") public class ServuxHudDataProtocol implements LeavesProtocol { - public static final int PROTOCOL_VERSION = 1; + public static final int PROTOCOL_VERSION = 2; private static final List players = new ArrayList<>(); private static final int updateInterval = 80; + private static final HashMap> loggerPlayers = new HashMap<>(); + private static final HashMap> LOGGERS = new HashMap<>(); + private static final HashMap DATA = new HashMap<>(); + public static boolean refreshSpawnMetadata = false; + @ProtocolHandler.Init + private static void initializeLoggers() { + if (!LOGGERS.isEmpty()) { + return; + } + for (DataLogger.Type type : DataLogger.Type.VALUES) { + DataLogger entry = type.init(); + if (entry != null) { + LOGGERS.put(type, entry); + } + } + } + + @ProtocolHandler.PlayerLeave + private static void onPlayerLeave(ServerPlayer player) { + players.remove(player); + loggerPlayers.remove(player); + } + @ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class) public static void onPacketReceive(ServerPlayer player, HudDataPayload payload) { switch (payload.packetType) { @@ -50,13 +76,20 @@ public class ServuxHudDataProtocol implements LeavesProtocol { metadata.putString("id", HudDataPayload.CHANNEL.toString()); metadata.putInt("version", PROTOCOL_VERSION); metadata.putString("servux", ServuxProtocol.SERVUX_STRING); + if (LeavesConfig.protocol.servux.hudLoggerProtocol) { + CompoundTag nbt = new CompoundTag(); + for (DataLogger.Type type : DataLogger.Type.VALUES) { + nbt.putBoolean(type.getSerializedName(), isLoggerTypeEnabled(type)); + } + metadata.put("Loggers", nbt); + } putWorldData(metadata); sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_METADATA, metadata)); } - case PACKET_C2S_SPAWN_DATA_REQUEST -> refreshSpawnMetadata(player); case PACKET_C2S_RECIPE_MANAGER_REQUEST -> refreshRecipeManager(player); + case PACKET_C2S_DATA_LOGGER_REQUEST -> refreshLoggers(player, payload.nbt); } } @@ -134,6 +167,91 @@ public class ServuxHudDataProtocol implements LeavesProtocol { } } + public static void refreshLoggers(ServerPlayer player, @Nonnull CompoundTag nbt) { + if (!player.getBukkitEntity().hasPermission("servux.provider.hud_data.logger")) { + player.sendSystemMessage(Component.translatable("servux.hud_data.error.insufficient_for_loggers", "any")); + return; + } + + if (nbt.isEmpty()) { + return; + } + + List list = new ArrayList<>(); + + for (String key : nbt.keySet()) { + DataLogger.Type type = DataLogger.Type.fromStringStatic(key); + + if (type != null && nbt.getBooleanOr(key, false)) { + list.add(type); + } + } + if (!list.isEmpty()) { + loggerPlayers.put(player, list); + } else { + loggerPlayers.remove(player); + } + } + + private static boolean isLoggerTypeEnabled(DataLogger.Type type) { + return LeavesConfig.protocol.servux.hudEnabledLoggers.contains(type); + } + + @ProtocolHandler.Ticker + public void protocolTick() { + for (ServerPlayer player : players) { + if (refreshSpawnMetadata) { + refreshSpawnMetadata(player); + } + refreshWeatherData(player); + } + refreshSpawnMetadata = false; + } + + @ProtocolHandler.Ticker(tickerId = "logger") + public void loggerTick() { + if (!LeavesConfig.protocol.servux.hudLoggerProtocol) { + return; + } + + MinecraftServer server = MinecraftServer.getServer(); + + if (server.getTickCount() % LeavesConfig.protocol.servux.hudUpdateInterval == 0) { + DATA.clear(); + LOGGERS.forEach((type, logger) -> { + if (!isLoggerTypeEnabled(type)) { + return; + } + DATA.put(type, logger.getResult(server)); + }); + } + + for (ServerPlayer player : loggerPlayers.keySet()) { + List list = loggerPlayers.get(player); + if (list.isEmpty()) { + return; + } + + CompoundTag nbt = new CompoundTag(); + for (DataLogger.Type type : list) { + if (DATA.containsKey(type)) { + nbt.put(type.getSerializedName(), DATA.get(type)); + } + } + sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_DATA_LOGGER_TICK, nbt)); + } + } + + @Override + public boolean isActive() { + return LeavesConfig.protocol.servux.hudMetadataProtocol; + } + + @Override + public int tickerInterval(String tickerID) { + return tickerID.equals("logger") ? 1 : updateInterval; + } + public static void sendPacket(ServerPlayer player, HudDataPayload payload) { if (payload.packetType == HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_START) { FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer()); @@ -148,27 +266,6 @@ public class ServuxHudDataProtocol implements LeavesProtocol { sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_DATA, buf)); } - @ProtocolHandler.Ticker - public void onTick() { - for (ServerPlayer player : players) { - if (refreshSpawnMetadata) { - refreshSpawnMetadata(player); - } - refreshWeatherData(player); - } - refreshSpawnMetadata = false; - } - - @Override - public boolean isActive() { - return LeavesConfig.protocol.servux.hudMetadataProtocol; - } - - @Override - public int tickerInterval(String tickerID) { - return updateInterval; - } - public enum HudDataPayloadType { PACKET_S2C_METADATA(1), PACKET_C2S_METADATA_REQUEST(2), @@ -176,6 +273,8 @@ public class ServuxHudDataProtocol implements LeavesProtocol { PACKET_C2S_SPAWN_DATA_REQUEST(4), PACKET_S2C_WEATHER_TICK(5), PACKET_C2S_RECIPE_MANAGER_REQUEST(6), + PACKET_S2C_DATA_LOGGER_TICK(7), + PACKET_C2S_DATA_LOGGER_REQUEST(8), // For Packet Splitter (Oversize Packets, S2C) PACKET_S2C_NBT_RESPONSE_START(10), PACKET_S2C_NBT_RESPONSE_DATA(11); @@ -208,7 +307,8 @@ public class ServuxHudDataProtocol implements LeavesProtocol { switch (payload.packetType()) { case PACKET_S2C_NBT_RESPONSE_DATA -> buf.writeBytes(payload.buffer().copy()); case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST, - PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK, PACKET_C2S_RECIPE_MANAGER_REQUEST -> buf.writeNbt(payload.nbt()); + PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK, PACKET_C2S_RECIPE_MANAGER_REQUEST, + PACKET_C2S_DATA_LOGGER_REQUEST, PACKET_S2C_DATA_LOGGER_TICK -> buf.writeNbt(payload.nbt()); } }, buf -> { @@ -221,7 +321,7 @@ public class ServuxHudDataProtocol implements LeavesProtocol { return new HudDataPayload(type, new FriendlyByteBuf(buf.readBytes(buf.readableBytes()))); } case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST, PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK, - PACKET_C2S_RECIPE_MANAGER_REQUEST -> { + PACKET_C2S_RECIPE_MANAGER_REQUEST, PACKET_C2S_DATA_LOGGER_REQUEST, PACKET_S2C_DATA_LOGGER_TICK -> { return new HudDataPayload(type, buf.readNbt()); } } @@ -236,6 +336,5 @@ public class ServuxHudDataProtocol implements LeavesProtocol { public HudDataPayload(HudDataPayloadType type, FriendlyByteBuf buffer) { this(type, new CompoundTag(), buffer); } - } } \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxStructuresProtocol.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxStructuresProtocol.java index fd08f36e..6b4421f9 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxStructuresProtocol.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/ServuxStructuresProtocol.java @@ -64,6 +64,7 @@ public class ServuxStructuresProtocol implements LeavesProtocol { @ProtocolHandler.PlayerLeave public static void onPlayerLoggedOut(@NotNull ServerPlayer player) { players.remove(player.getId()); + timeouts.remove(player.getUUID()); } @ProtocolHandler.Ticker @@ -77,11 +78,6 @@ public class ServuxStructuresProtocol implements LeavesProtocol { } } - @Override - public int tickerInterval(String tickerID) { - return updateInterval; - } - public static void onStartedWatchingChunk(ServerPlayer player, LevelChunk chunk) { MinecraftServer server = player.getServer(); if (players.containsKey(player.getId()) && server != null) { @@ -318,6 +314,11 @@ public class ServuxStructuresProtocol implements LeavesProtocol { sendPacket(player, new StructuresPayload(StructuresPayloadType.PACKET_S2C_STRUCTURE_DATA, buf)); } + @Override + public int tickerInterval(String tickerID) { + return updateInterval; + } + @Override public boolean isActive() { return LeavesConfig.protocol.servux.structureProtocol; diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/DataLogger.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/DataLogger.java new file mode 100644 index 00000000..4a53abcd --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/DataLogger.java @@ -0,0 +1,153 @@ +package org.leavesmc.leaves.protocol.servux.logger; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerTickRateManager; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.NaturalSpawner; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.protocol.servux.logger.data.MobCapData; +import org.leavesmc.leaves.protocol.servux.logger.data.TPSData; + +import java.util.ArrayList; +import java.util.function.Function; + +public abstract class DataLogger { + + private final Type type; + + public DataLogger(Type type) { + this.type = type; + } + + public Type getType() { + return this.type; + } + + public abstract T getResult(MinecraftServer server); + + public enum Type implements StringRepresentable { + TPS("tps", Tps::new, Tps.CODEC), + MOB_CAPS("mob_caps", MobCaps::new, MobCaps.CODEC); + + public static final StringRepresentable.EnumCodec CODEC = StringRepresentable.fromEnum(Type::values); + public static final ImmutableList VALUES = ImmutableList.copyOf(values()); + + private final String name; + private final Function> factory; + private final Codec codec; + + Type(String name, Function> factory, Codec codec) { + this.name = name; + this.factory = factory; + this.codec = codec; + } + + public static @Nullable Type fromStringStatic(String name) { + for (Type type : VALUES) { + if (type.name.equalsIgnoreCase(name)) { + return type; + } + } + + return null; + } + + public Function> getFactory() { + return factory; + } + + public @Nullable DataLogger init() { + return this.factory.apply(this); + } + + public Codec codec() { + return this.codec; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + } + + public static class Tps extends DataLogger { + + public static final Codec CODEC = CompoundTag.CODEC; + + public Tps(Type type) { + super(type); + } + + @Override + public CompoundTag getResult(MinecraftServer server) { + try { + return (CompoundTag) TPSData.CODEC.encodeStart(server.registryAccess().createSerializationContext(NbtOps.INSTANCE), this.build(server)).getOrThrow(); + } catch (Exception e) { + return new CompoundTag(); + } + } + + private TPSData build(MinecraftServer server) { + ServerTickRateManager tickManager = server.tickRateManager(); + boolean frozen = tickManager.isFrozen(); + boolean sprinting = tickManager.isSprinting(); + final double mspt = server.tickTimes5s.getAverage(); + double tps = 1000.0D / Math.max(sprinting ? 0.0 : tickManager.millisecondsPerTick(), mspt); + + if (frozen) { + tps = 0.0d; + } + + return new TPSData( + mspt, tps, + tickManager.getRemainingSprintTicks(), + frozen, sprinting, + tickManager.isSteppingForward() + ); + } + } + + public static class MobCaps extends DataLogger { + + public static final Codec CODEC = CompoundTag.CODEC; + + public MobCaps(Type type) { + super(type); + } + + @Override + public CompoundTag getResult(MinecraftServer server) { + CompoundTag nbt = new CompoundTag(); + + for (ServerLevel world : server.getAllLevels()) { + NaturalSpawner.SpawnState info = world.getChunkSource().getLastSpawnState(); + if (info == null) { + continue; + } + + int chunks = info.getSpawnableChunkCount(); + Object2IntMap counts = info.getMobCategoryCounts(); + MobCapData mobCapData = new MobCapData(new ArrayList<>(), world.getGameTime()); + for (MobCategory category : MobCategory.values()) { + mobCapData.data().add(new MobCapData.Cap(counts.getOrDefault(category, 0), NaturalSpawner.globalLimitForCategory(world, category, chunks))); + } + + try { + nbt.put(world.dimension().location().toString(), MobCapData.CODEC.encodeStart(world.registryAccess().createSerializationContext(NbtOps.INSTANCE), mobCapData).getPartialOrThrow()); + } catch (Exception ignored) { + } + } + + return nbt; + } + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/MobCapData.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/MobCapData.java new file mode 100644 index 00000000..60921024 --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/MobCapData.java @@ -0,0 +1,33 @@ +package org.leavesmc.leaves.protocol.servux.logger.data; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.PrimitiveCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.entity.MobCategory; + +import java.util.List; + +public record MobCapData(List data, long worldTick) { + public static final int CAP_COUNT = MobCategory.values().length; + + private static MobCapData of(int count, List data, long worldTick) { + return new MobCapData(data, worldTick); + } + + public static final Codec CODEC = RecordCodecBuilder.create( + (inst) -> inst.group( + PrimitiveCodec.INT.fieldOf("cap_count").forGetter($ -> CAP_COUNT), + Codec.list(Cap.CODEC).fieldOf("cap_data").forGetter(MobCapData::data), + PrimitiveCodec.LONG.fieldOf("WorldTick").forGetter(MobCapData::worldTick) + ).apply(inst, MobCapData::of) + ); + + public record Cap(int current, int cap) { + public static Codec CODEC = RecordCodecBuilder.create( + (inst) -> inst.group( + PrimitiveCodec.INT.fieldOf("current").forGetter(Cap::current), + PrimitiveCodec.INT.fieldOf("cap").forGetter(Cap::cap) + ).apply(inst, Cap::new) + ); + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/TPSData.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/TPSData.java new file mode 100644 index 00000000..b2b2bdb5 --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/servux/logger/data/TPSData.java @@ -0,0 +1,18 @@ +package org.leavesmc.leaves.protocol.servux.logger.data; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.PrimitiveCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record TPSData(double mspt, double tps, long sprintTicks, boolean frozen, boolean sprinting, boolean stepping) { + public static Codec CODEC = RecordCodecBuilder.create( + (inst) -> inst.group( + PrimitiveCodec.DOUBLE.fieldOf("mspt").forGetter(TPSData::mspt), + PrimitiveCodec.DOUBLE.fieldOf("tps").forGetter(TPSData::tps), + PrimitiveCodec.LONG.fieldOf("sprintTicks").forGetter(TPSData::sprintTicks), + PrimitiveCodec.BOOL.fieldOf("frozen").forGetter(TPSData::frozen), + PrimitiveCodec.BOOL.fieldOf("sprinting").forGetter(TPSData::sprinting), + PrimitiveCodec.BOOL.fieldOf("stepping").forGetter(TPSData::stepping) + ).apply(inst, TPSData::new) + ); +} \ No newline at end of file