diff --git a/gradle.properties b/gradle.properties index 86ded629..2c9e0c6a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://fabricmc.net/use # or better: https://modmuss50.me/fabric.html - minecraft_version=1.14.2 Pre-Release 1 - yarn_mappings=1.14.2 Pre-Release 1+build.1 + minecraft_version=1.14.2 Pre-Release 2 + yarn_mappings=1.14.2 Pre-Release 2+build.2 loader_version=0.4.7+build.147 # Mod Properties diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index fb3f625d..be0958d0 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.FileNotFoundException; //import carpet.helpers.SpawnChunks; +import carpet.fakes.MinecraftServer_motdInterface; import carpet.utils.Messenger; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.dedicated.DedicatedServer; @@ -152,7 +153,8 @@ public class CarpetSettings .choices("100","0 10 50 100").setNotStrict(), rule("customMOTD", "creative","Sets a different motd message on client trying to connect to the server") .extraInfo("use '_' to use the startup setting from server.properties") - .choices("_","_").setNotStrict(), + .choices("_","_").setNotStrict().validate((s) -> + ((MinecraftServer_motdInterface)CarpetServer.minecraft_server).checkMOTD()), rule("rotatorBlock", "experimental", "Cactus in dispensers rotates blocks.") .extraInfo("Cactus in a dispenser gives the dispenser the ability to rotate the blocks " + "that are in front of it anti-clockwise if possible."), diff --git a/src/main/java/carpet/commands/TickCommand.java b/src/main/java/carpet/commands/TickCommand.java index dbe9759d..5f2dcf8d 100644 --- a/src/main/java/carpet/commands/TickCommand.java +++ b/src/main/java/carpet/commands/TickCommand.java @@ -130,13 +130,13 @@ public class TickCommand private static int healthReport(ServerCommandSource source, int ticks) { - CarpetProfiler.prepare_tick_report(ticks); + CarpetProfiler.prepare_tick_report(source.getMinecraftServer(), ticks); return 1; } private static int healthEntities(ServerCommandSource source, int ticks) { - CarpetProfiler.prepare_entity_report(ticks); + CarpetProfiler.prepare_entity_report(source.getMinecraftServer(), ticks); return 1; } diff --git a/src/main/java/carpet/fakes/MinecraftServer_motdInterface.java b/src/main/java/carpet/fakes/MinecraftServer_motdInterface.java new file mode 100644 index 00000000..6cb05284 --- /dev/null +++ b/src/main/java/carpet/fakes/MinecraftServer_motdInterface.java @@ -0,0 +1,6 @@ +package carpet.fakes; + +public interface MinecraftServer_motdInterface +{ + String checkMOTD(); +} diff --git a/src/main/java/carpet/mixins/MinecraftServer_motdMixin.java b/src/main/java/carpet/mixins/MinecraftServer_motdMixin.java new file mode 100644 index 00000000..c177d465 --- /dev/null +++ b/src/main/java/carpet/mixins/MinecraftServer_motdMixin.java @@ -0,0 +1,41 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.MinecraftServer_motdInterface; +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(MinecraftServer.class) +public abstract class MinecraftServer_motdMixin implements MinecraftServer_motdInterface +{ + @Shadow private String motd; + + @Shadow public abstract void setMotd(String string_1); + + @Redirect(method = "run", at = @At( + value = "FIELD", + target = "Lnet/minecraft/server/MinecraftServer;motd:Ljava/lang/String;") + ) + private String setCustomMotd(MinecraftServer server) + { + return checkMOTD(); + } + + @Override + public String checkMOTD() + { + if ("_".equals(CarpetSettings.getString("customMOTD"))) + { + setMotd(motd); + return motd; + } + else + { + setMotd(CarpetSettings.getString("customMOTD")); + return CarpetSettings.getString("customMOTD"); + } + } +} diff --git a/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java b/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java index 77b0fdb2..ab54cb4c 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.helpers.TickSpeed; +import carpet.utils.CarpetProfiler; import net.minecraft.server.MinecraftServer; import net.minecraft.util.SystemUtil; import net.minecraft.util.profiler.DisableableProfiler; @@ -42,6 +43,8 @@ public abstract class MinecraftServer_tickspeedMixin @Shadow private volatile boolean loading; + CarpetProfiler.ProfilerToken currentSection; + // Cancel a while statement @Redirect(method = "run", at = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;running:Z")) private boolean cancelRunLoop(MinecraftServer server) @@ -60,6 +63,10 @@ public abstract class MinecraftServer_tickspeedMixin { //long long_1 = SystemUtil.getMeasuringTimeMs() - this.timeReference; //CM deciding on tick speed + if (CarpetProfiler.tick_health_requested != 0L) + { + CarpetProfiler.start_tick_profiling(); + } long mspt = 0L; long long_1 = 0L; if (TickSpeed.time_warp_start_time != 0 && TickSpeed.continueWarp()) @@ -102,4 +109,64 @@ public abstract class MinecraftServer_tickspeedMixin } } + + @Inject(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/PlayerManager;saveAllPlayerData()V", + shift = At.Shift.BEFORE + )) + private void startAutosave(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + currentSection = CarpetProfiler.start_section(null, "Autosave", CarpetProfiler.TYPE.GENERAL); + } + + @Inject(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/MinecraftServer;save(ZZZ)Z", + shift = At.Shift.AFTER + )) + private void finishAutosave(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + CarpetProfiler.end_current_section(currentSection); + } + + @Inject(method = "tickWorlds", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/MinecraftServer;getNetworkIo()Lnet/minecraft/server/ServerNetworkIo;", + shift = At.Shift.BEFORE + )) + private void startNetwork(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + currentSection = CarpetProfiler.start_section(null, "Network", CarpetProfiler.TYPE.GENERAL); + } + + @Inject(method = "tickWorlds", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/PlayerManager;updatePlayerLatency()V", + shift = At.Shift.AFTER + )) + private void finishNetwork(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + CarpetProfiler.end_current_section(currentSection); + } + + @Inject(method = "method_16208", at = @At("HEAD")) + private void startAsync(CallbackInfo ci) + { + currentSection = CarpetProfiler.start_section(null, "Async Tasks", CarpetProfiler.TYPE.GENERAL); + } + @Inject(method = "method_16208", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/MinecraftServer;waitFor(Ljava/util/function/BooleanSupplier;)V", + shift = At.Shift.BEFORE + )) + private void stopAsync(CallbackInfo ci) + { + if (CarpetProfiler.tick_health_requested != 0L) + { + CarpetProfiler.end_tick_profiling((MinecraftServer) (Object)this, currentSection); + } + } + + } diff --git a/src/main/java/carpet/mixins/ServerChunkManager_tickMixin.java b/src/main/java/carpet/mixins/ServerChunkManager_tickMixin.java new file mode 100644 index 00000000..9d38df9e --- /dev/null +++ b/src/main/java/carpet/mixins/ServerChunkManager_tickMixin.java @@ -0,0 +1,80 @@ +package carpet.mixins; + +import carpet.helpers.TickSpeed; +import carpet.utils.CarpetProfiler; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.level.LevelGeneratorType; +import net.minecraft.world.level.LevelProperties; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.BooleanSupplier; + +@Mixin(ServerChunkManager.class) +public abstract class ServerChunkManager_tickMixin +{ + @Shadow @Final private ServerWorld world; + CarpetProfiler.ProfilerToken currentSection; + + @Inject(method = "tick(Ljava/util/function/BooleanSupplier;)V", at = @At( + value = "CONSTANT", + args = "stringValue=purge" + )) + private void startTicketSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + currentSection = CarpetProfiler.start_section(world, "Ticket Manager", CarpetProfiler.TYPE.GENERAL); + } + + @Inject(method = "tick(Ljava/util/function/BooleanSupplier;)V", at = @At( + value = "CONSTANT", + args = "stringValue=chunks" + )) + private void stopTicketStartSpawningSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + currentSection = CarpetProfiler.start_section(world, "Spawning and Random Ticks", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick(Ljava/util/function/BooleanSupplier;)V", at = @At( + value = "CONSTANT", + args = "stringValue=unload" + )) + private void stopSpawningStartUnloadSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + currentSection = CarpetProfiler.start_section(world, "Unloading", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick(Ljava/util/function/BooleanSupplier;)V", at = @At("TAIL")) + private void stopUnloadSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + } + } + + //// Tick freeze + @Redirect(method = "tickChunks", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/LevelProperties;getGeneratorType()Lnet/minecraft/world/level/LevelGeneratorType;" + )) + private LevelGeneratorType skipChunkTicking(LevelProperties levelProperties) + { + if (!TickSpeed.process_entities) return LevelGeneratorType.DEBUG_ALL_BLOCK_STATES; + return levelProperties.getGeneratorType(); + } + +} diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandlerMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..cb07ae50 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandlerMixin.java @@ -0,0 +1,57 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.helpers.TickSpeed; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.packet.PlayerInputC2SPacket; +import net.minecraft.server.network.packet.PlayerMoveC2SPacket; +import net.minecraft.server.network.packet.VehicleMoveC2SPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerPlayNetworkHandler.class) +public class ServerPlayNetworkHandlerMixin +{ + @Shadow public ServerPlayerEntity player; + + @Shadow private double lastTickX; + + @Shadow private double lastTickY; + + @Inject(method = "onPlayerInput", at = @At(value = "RETURN")) + private void checkMoves(PlayerInputC2SPacket p, CallbackInfo ci) + { + if (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking()) + { + TickSpeed.reset_player_active_timeout(); + } + } + + @Inject(method = "onPlayerMove", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSleeping()Z", + shift = At.Shift.BEFORE + )) + private void checkMove(PlayerMoveC2SPacket p, CallbackInfo ci) + { + if (p.getX(player.x) - lastTickX > 0.0001D || p.getY(player.y) - lastTickY > 0.0001D || p.getY(player.y) - lastTickY > 0.0001D) + { + TickSpeed.reset_player_active_timeout(); + } + } + + @Redirect(method = "onPlayerMove", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;isInTeleportationState()Z")) + private boolean relaxMoveRestrictions(ServerPlayerEntity serverPlayerEntity) + { + return serverPlayerEntity.isInTeleportationState() || CarpetSettings.getBool("antiCheatSpeed"); + } + +} diff --git a/src/main/java/carpet/mixins/ServerWorld_tickMixin.java b/src/main/java/carpet/mixins/ServerWorld_tickMixin.java new file mode 100644 index 00000000..73c96434 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerWorld_tickMixin.java @@ -0,0 +1,193 @@ +package carpet.mixins; + +import carpet.helpers.TickSpeed; +import carpet.utils.CarpetProfiler; +import net.minecraft.entity.raid.RaidManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.village.ZombieSiegeManager; +import net.minecraft.world.WanderingTraderManager; +import net.minecraft.world.World; +import net.minecraft.world.border.WorldBorder; +import net.minecraft.world.chunk.ChunkManager; +import net.minecraft.world.dimension.Dimension; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.level.LevelGeneratorType; +import net.minecraft.world.level.LevelProperties; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.BiFunction; +import java.util.function.BooleanSupplier; + +@Mixin(ServerWorld.class) +public abstract class ServerWorld_tickMixin extends World +{ + @Shadow protected abstract void sendBlockActions(); + + CarpetProfiler.ProfilerToken currentSection; + + protected ServerWorld_tickMixin(LevelProperties levelProperties_1, DimensionType dimensionType_1, BiFunction biFunction_1, Profiler profiler_1, boolean boolean_1) + { + super(levelProperties_1, dimensionType_1, biFunction_1, profiler_1, boolean_1); + } + + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=world border" + )) + private void startWeatherSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + currentSection = CarpetProfiler.start_section((World)(Object)this, "Environment", CarpetProfiler.TYPE.GENERAL); + } + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=world border" + )) + private void stopWeatherStartChunkSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + // we go deeper here + } + } + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=tickPending" + )) + private void stopChunkStartBlockSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + // out of chunk + currentSection = CarpetProfiler.start_section((World) (Object) this, "Blocks", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=village" + )) + private void stopBlockStartVillageSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + currentSection = CarpetProfiler.start_section((World) (Object) this, "Village", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=tickPending" + )) + private void stopVillageStartBlockAgainSection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + currentSection = CarpetProfiler.start_section((World) (Object) this, "Blocks", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=entities" + )) + private void stopBlockAgainStartEntitySection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + if (currentSection != null) + { + CarpetProfiler.end_current_section(currentSection); + currentSection = CarpetProfiler.start_section((World) (Object) this, "Entities", CarpetProfiler.TYPE.GENERAL); + } + } + + @Inject(method = "tick", at = @At("TAIL")) + private void endEntitySection(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + CarpetProfiler.end_current_section(currentSection); + } + + //// freeze + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/border/WorldBorder;tick()V" + )) + private void tickWorldBorder(WorldBorder worldBorder) + { + if (TickSpeed.process_entities) worldBorder.tick(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/dimension/Dimension;hasSkyLight()Z" + )) + private boolean tickWorldBorder(Dimension dimension) + { + return TickSpeed.process_entities && dimension.hasSkyLight(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/world/ServerWorld;tickTime()V" + )) + private void tickTimeConditionally(ServerWorld serverWorld) + { + if (TickSpeed.process_entities) this.tickTime(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/LevelProperties;getGeneratorType()Lnet/minecraft/world/level/LevelGeneratorType;" + )) + private LevelGeneratorType tickPendingBlocks(LevelProperties levelProperties) + { + if (TickSpeed.process_entities) return levelProperties.getGeneratorType(); + return LevelGeneratorType.DEBUG_ALL_BLOCK_STATES; + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/village/ZombieSiegeManager;tick()V" + )) + private void tickConditionally(ZombieSiegeManager zombieSiegeManager) + { + if (TickSpeed.process_entities) zombieSiegeManager.tick(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/raid/RaidManager;tick()V" + )) + private void tickConditionally(RaidManager raidManager) + { + if (TickSpeed.process_entities) raidManager.tick(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/WanderingTraderManager;tick()V" + )) + private void tickConditionally(WanderingTraderManager wanderingTraderManager) + { + if (TickSpeed.process_entities) wanderingTraderManager.tick(); + } + + @Redirect(method = "tick", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/world/ServerWorld;sendBlockActions()V" + )) + private void tickConditionally(ServerWorld serverWorld) + { + if (TickSpeed.process_entities) sendBlockActions(); + } + + +} diff --git a/src/main/java/carpet/mixins/World_tickMixin.java b/src/main/java/carpet/mixins/World_tickMixin.java new file mode 100644 index 00000000..c299c293 --- /dev/null +++ b/src/main/java/carpet/mixins/World_tickMixin.java @@ -0,0 +1,81 @@ +package carpet.mixins; + +import carpet.helpers.TickSpeed; +import carpet.utils.CarpetProfiler; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Iterator; +import java.util.function.Consumer; + +@Mixin(World.class) +public abstract class World_tickMixin +{ + CarpetProfiler.ProfilerToken currentSection; + CarpetProfiler.ProfilerToken entitySection; + + @Inject(method = "tickBlockEntities", at = @At("HEAD")) + private void startBlockEntities(CallbackInfo ci) { + currentSection = CarpetProfiler.start_section((World) (Object) this, "Tile Entities", CarpetProfiler.TYPE.GENERAL); + } + + @Inject(method = "tickBlockEntities", at = @At("TAIL")) + private void endBlockEntities(CallbackInfo ci) { + CarpetProfiler.end_current_section(currentSection); + } + + @Inject(method = "tickBlockEntities", locals = LocalCapture.CAPTURE_FAILHARD, at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/entity/BlockEntity;isInvalid()Z", + shift = At.Shift.BEFORE, + ordinal = 0 + )) + private void startTileEntitySection(CallbackInfo ci, Profiler profiler_1, Iterator i, BlockEntity blockEntity_2) + { + entitySection = CarpetProfiler.start_entity_section((World)(Object)this, blockEntity_2, CarpetProfiler.TYPE.TILEENTITY); + } + + @Redirect(method = "tickBlockEntities", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/entity/BlockEntity;hasWorld()Z" + )) + private boolean checkProcessTEs(BlockEntity blockEntity) + { + return blockEntity.hasWorld() && TickSpeed.process_entities; + } + + @Inject(method = "tickBlockEntities", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/entity/BlockEntity;isInvalid()Z", + shift = At.Shift.BEFORE, + ordinal = 1 + )) + private void endTileEntitySection(CallbackInfo ci) + { + CarpetProfiler.end_current_entity_section(entitySection); + } + + @Inject(method = "tickEntity", at = @At("HEAD"), cancellable = true) + private void startEntity(Consumer consumer_1, Entity e, CallbackInfo ci) + { + if (!TickSpeed.process_entities && !(e instanceof PlayerEntity)) + ci.cancel(); + entitySection = CarpetProfiler.start_entity_section((World) (Object) this, e, CarpetProfiler.TYPE.ENTITY); + } + + @Inject(method = "tickEntity", at = @At("TAIL")) + private void endEntity(Consumer call, Entity e, CallbackInfo ci) { + CarpetProfiler.end_current_entity_section(entitySection); + } + + +} diff --git a/src/main/java/carpet/utils/CarpetProfiler.java b/src/main/java/carpet/utils/CarpetProfiler.java index 59f4aa18..fd1e3ac9 100644 --- a/src/main/java/carpet/utils/CarpetProfiler.java +++ b/src/main/java/carpet/utils/CarpetProfiler.java @@ -4,8 +4,8 @@ import carpet.CarpetSettings; import net.minecraft.entity.Entity; import net.minecraft.server.MinecraftServer; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.BlockEntityType; import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; import java.util.Comparator; import java.util.HashMap; @@ -17,196 +17,153 @@ public class CarpetProfiler private static final HashMap time_repo = new HashMap<>(); public static int tick_health_requested = 0; private static int tick_health_elapsed = 0; - private static int test_type = 0; //1 for ticks, 2 for entities; - private static String current_section = null; - private static long current_section_start = 0; + private static TYPE test_type = TYPE.NONE; //1 for ticks, 2 for entities; private static long current_tick_start = 0; - private static String [] GENERAL_SECTIONS = {"Network", "Autosave"}; - private static String [] DIMENSIONS = {"Overworld","The End","The Nether"}; - private static String [] SECTIONS = {"Spawning","Blocks","Entities","Tile Entities","Entities(client)","Tile Entities(client)","Villages"}; + private static String[] GENERAL_SECTIONS = {"Network", "Autosave", "Async Tasks"}; + private static String[] DIMENSIONS = {"overworld", "the_end", "the_nether"}; + private static String[] SECTIONS = { + "Spawning and Random Ticks", "Ticket Manager","Unloading", + "Blocks", "Entities", "Tile Entities", + "Entities (Client)", "Tile Entities (Client)", + "Village", "Environment"}; - public static void prepare_tick_report(int ticks) + public enum TYPE + { + NONE, + GENERAL, + ENTITY, + TILEENTITY + } + + public static class ProfilerToken + { + public TYPE type; + public Object section; + public long start; + public World world; + + public ProfilerToken(TYPE type, Object section, World world, long start) + { + this.type = type; + this.section = section; + this.start = start; + this.world = world; + } + } + + private static String getWorldString(World world) + { + return world.getDimension().getType().toString().replaceFirst("minecraft:", "") + (world.isClient ? "(Client)" : ""); + } + + private static String getSectionString(World world, String section) + { + return String.format("%s.%s%s", + world.getDimension().getType().toString().replaceFirst("minecraft:", ""), + section, + world.isClient ? " (Client)" : ""); + } + + private static String getEntityString(World world, Entity e) + { + return String.format("%s.%s%s", + world.getDimension().getType().toString().replaceFirst("minecraft:", ""), + Registry.ENTITY_TYPE.getId(e.getType()).toString().replaceFirst("minecraft:", ""), + world.isClient ? " (Client)" : ""); + } + + private static String getTEntityString(World world, BlockEntity be) + { + return String.format("%s.%s%s", + world.getDimension().getType().toString().replaceFirst("minecraft:", ""), + Registry.BLOCK_ENTITY.getId(be.getType()).toString().replaceFirst("minecraft:", ""), + world.isClient ? " (Client)" : ""); + } + + public static void prepare_tick_report(MinecraftServer server, int ticks) { //maybe add so it only spams the sending player, but honestly - all may want to see it time_repo.clear(); - test_type = 1; - time_repo.put("tick",0L); - time_repo.put("Network",0L); - time_repo.put("Autosave",0L); - for (String section: GENERAL_SECTIONS) + test_type = TYPE.GENERAL; + time_repo.put("tick", 0L); + time_repo.put("Network", 0L); + time_repo.put("Autosave", 0L); + for (String section : GENERAL_SECTIONS) { - time_repo.put(section,0L); + time_repo.put(section, 0L); } - for (String level: DIMENSIONS) + for (String level : DIMENSIONS) { - for (String section: SECTIONS) + for (String section : SECTIONS) { - time_repo.put(level+"."+section,0L); + time_repo.put(level + "." + section, 0L); } } tick_health_elapsed = ticks; tick_health_requested = ticks; current_tick_start = 0L; - current_section_start = 0L; - current_section = null; - } - public static void start_section(String dimension, String name) + public static void prepare_entity_report(MinecraftServer server, int ticks) { - if (tick_health_requested == 0L || test_type != 1) - { + //maybe add so it only spams the sending player, but honestly - all may want to see it + time_repo.clear(); + time_repo.put("tick", 0L); + test_type = TYPE.ENTITY; + tick_health_elapsed = ticks; + tick_health_requested = ticks; + current_tick_start = 0L; + } + + public static ProfilerToken start_section(World world, String name, TYPE type) + { + if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0) + return null; + return new ProfilerToken(type, name, world, System.nanoTime()); + } + + public static ProfilerToken start_entity_section(World world, Object e, TYPE type) + { + if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0) + return null; + return new ProfilerToken(type, e, world, System.nanoTime()); + } + + public static void end_current_section(ProfilerToken tok) + { + if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0) return; - } - if (current_tick_start == 0L) - { - return; - } - if (current_section != null) - { - end_current_section(); - } - String key = name; - if (dimension != null) - { - key = dimension+"."+name; - } - current_section = key; - current_section_start = System.nanoTime(); - } - - - public static class ProfilerToken - { - public String section; - public long start; - public ProfilerToken(String name, long start) - { - this.section = name; - this.start = start; - } - } - - public static ProfilerToken start_section_concurrent(String dimension, String name, boolean isRemote) - { - if (tick_health_requested == 0L || test_type != 1) - { - return null; - } - if (current_tick_start == 0L) - { - return null; - } - - String key = name; - if (dimension != null) - { - key = dimension+"."+name; - } - if (isRemote) key += "(client)"; - long time = System.nanoTime(); - return new ProfilerToken(key, time); - } - - public static ProfilerToken start_entity_section(String dimension, Entity e) - { - if (tick_health_requested == 0L || test_type != 2) - { - return null; - } - if (current_tick_start == 0L) - { - return null; - } - String section = dimension+"."+ Registry.ENTITY_TYPE.getId(e.getType()).toString().replaceFirst("minecraft:",""); - if (e.getEntityWorld().isClient) - section += "(client)"; - long section_start = System.nanoTime(); - return new ProfilerToken(section, section_start); - } - - public static ProfilerToken start_tileentity_section(String dimension, BlockEntity e) - { - if (tick_health_requested == 0L || test_type != 2) - { - return null; - } - if (current_tick_start == 0L) - { - return null; - } - String section = dimension+"."+ BlockEntityType.getId(e.getType()).toString().replaceFirst("minecraft:",""); - if (e.getWorld() == null) - section += "??"; - else if (e.getWorld().isClient) - section += "(client)"; - long section_start = System.nanoTime(); - return new ProfilerToken(section, section_start); - } - - public static void end_current_section() - { - if (tick_health_requested == 0L || test_type != 1) - { - return; - } long end_time = System.nanoTime(); - if (current_tick_start == 0L) + if (tok.type == TYPE.GENERAL) { - return; + String current_section = (tok.world == null) ? (String) tok.section : + getSectionString(tok.world, (String)tok.section); + if (time_repo.get(current_section) == null) + { + CarpetSettings.LOG.error("Current section: "+current_section); + } + else + time_repo.put(current_section, time_repo.get(current_section) + end_time - tok.start); } - if (current_section == null) - { - CarpetSettings.LOG.error("finishing section that hasn't started"); - return; - } - //CarpetSettings.LOG.error("finishing section "+current_section); - time_repo.put(current_section,time_repo.get(current_section)+end_time-current_section_start); - current_section = null; - current_section_start = 0; - } - - public static void end_current_section_concurrent(ProfilerToken tok) - { - if (tick_health_requested == 0L || test_type != 1) - { - return; - } - long end_time = System.nanoTime(); - if (current_tick_start == 0L) - { - return; - } - if (tok == null) - { - CarpetSettings.LOG.error("finishing section that hasn't started"); - return; - } - //CarpetSettings.LOG.error("finishing section "+current_section); - time_repo.put(tok.section,time_repo.get(tok.section)+end_time-tok.start); } public static void end_current_entity_section(ProfilerToken tok) { - if (tick_health_requested == 0L || test_type != 2) - { + if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0) return; - } long end_time = System.nanoTime(); - if (current_tick_start == 0L) - { + String section; + if (tok.type == TYPE.ENTITY) + section = getEntityString(tok.world, (Entity) tok.section); + else if (tok.type == TYPE.TILEENTITY) + section = getTEntityString(tok.world, (BlockEntity) tok.section); + else return; - } - if (tok == null) - { - CarpetSettings.LOG.error("finishing entity/TE section that hasn't started"); - return; - } - String time_section = "t."+tok.section; - String count_section = "c."+tok.section; - time_repo.put(time_section,time_repo.getOrDefault(time_section,0L)+end_time-tok.start); - time_repo.put(count_section,time_repo.getOrDefault(count_section,0L)+1); + String time_section = "t." + section; + String count_section = "c." + section; + time_repo.put(time_section, time_repo.getOrDefault(time_section, 0L) + end_time - tok.start); + time_repo.put(count_section, time_repo.getOrDefault(count_section, 0L) + 1); } public static void start_tick_profiling() @@ -214,15 +171,14 @@ public class CarpetProfiler current_tick_start = System.nanoTime(); } - public static void end_tick_profiling(MinecraftServer server) + public static void end_tick_profiling(MinecraftServer server, ProfilerToken postTasks) { if (current_tick_start == 0L) - { return; - } - time_repo.put("tick",time_repo.get("tick")+System.nanoTime()-current_tick_start); - tick_health_elapsed --; - //CarpetSettings.LOG.error("tick count current at "+time_repo.get("tick")); + if (postTasks != null) + end_current_section(postTasks); + time_repo.put("tick", time_repo.get("tick") + System.nanoTime() - current_tick_start); + tick_health_elapsed--; if (tick_health_elapsed <= 0) { finalize_tick_report(server); @@ -231,41 +187,35 @@ public class CarpetProfiler public static void finalize_tick_report(MinecraftServer server) { - if (test_type == 1) - { + if (test_type == TYPE.GENERAL) finalize_tick_report_for_time(server); - } - if (test_type == 2) - { + if (test_type == TYPE.ENTITY) finalize_tick_report_for_entities(server); - } cleanup_tick_report(); } public static void cleanup_tick_report() { time_repo.clear(); - time_repo.put("tick",0L); - test_type = 0; + time_repo.put("tick", 0L); + test_type = TYPE.NONE; tick_health_elapsed = 0; tick_health_requested = 0; current_tick_start = 0L; - current_section_start = 0L; - current_section = null; - } public static void finalize_tick_report_for_time(MinecraftServer server) { //print stats long total_tick_time = time_repo.get("tick"); - double divider = 1.0D/tick_health_requested/1000000; - Messenger.print_server_message(server, String.format("Average tick time: %.3fms",divider*total_tick_time)); + double divider = 1.0D / tick_health_requested / 1000000; + Messenger.print_server_message(server, ""); + Messenger.print_server_message(server, String.format("Average tick time: %.3fms", divider * total_tick_time)); long accumulated = 0L; - for (String section: GENERAL_SECTIONS) + for (String section : GENERAL_SECTIONS) { - double amount = divider*time_repo.get(section); + double amount = divider * time_repo.get(section); if (amount > 0.01) { accumulated += time_repo.get(section); @@ -273,12 +223,12 @@ public class CarpetProfiler } } - for (String dimension: DIMENSIONS) + for (String dimension : DIMENSIONS) { boolean hasSomethin = false; - for (String section: SECTIONS) + for (String section : SECTIONS) { - double amount = divider*time_repo.get(dimension+"."+section); + double amount = divider * time_repo.get(dimension + "." + section); if (amount > 0.01) { hasSomethin = true; @@ -289,35 +239,36 @@ public class CarpetProfiler { continue; } - Messenger.print_server_message(server, dimension+":"); - for (String section: SECTIONS) + Messenger.print_server_message(server, dimension + ":"); + for (String section : SECTIONS) { - double amount = divider*time_repo.get(dimension+"."+section); + double amount = divider * time_repo.get(dimension + "." + section); if (amount > 0.01) { if (!(section.endsWith("(client)"))) - accumulated += time_repo.get(dimension+"."+section); + accumulated += time_repo.get(dimension + "." + section); Messenger.print_server_message(server, String.format(" - %s: %.3fms", section, amount)); } } } - long rest = total_tick_time-accumulated; + long rest = total_tick_time - accumulated; - Messenger.print_server_message(server, String.format("The Rest, whatever that might be: %.3fms",divider*rest)); + Messenger.print_server_message(server, String.format("The Rest, whatever that might be: %.3fms", divider * rest)); } public static void finalize_tick_report_for_entities(MinecraftServer server) { //print stats long total_tick_time = time_repo.get("tick"); - double divider = 1.0D/tick_health_requested/1000000; - double divider_1 = 1.0D/(tick_health_requested-1)/1000000; - Messenger.print_server_message(server, String.format("Average tick time: %.3fms",divider*total_tick_time)); + double divider = 1.0D / tick_health_requested / 1000000; + double divider_1 = 1.0D / (tick_health_requested - 1) / 1000000; + Messenger.print_server_message(server, ""); + Messenger.print_server_message(server, String.format("Average tick time: %.3fms", divider * total_tick_time)); time_repo.remove("tick"); Messenger.print_server_message(server, "Top 10 counts:"); int total = 0; - for ( Map.Entry entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()) ) + for (Map.Entry entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList())) { if (entry.getKey().startsWith("t.")) { @@ -331,12 +282,12 @@ public class CarpetProfiler String[] parts = entry.getKey().split("\\."); String dim = parts[1]; String name = parts[2]; - int penalty = name.endsWith("(client)") ? 1 :0; - Messenger.print_server_message(server, String.format(" - %s in %s: %.3f",name, dim, 1.0D*entry.getValue()/(tick_health_requested-penalty))); + int penalty = name.endsWith("(client)") ? 1 : 0; + Messenger.print_server_message(server, String.format(" - %s in %s: %.3f", name, dim, 1.0D * entry.getValue() / (tick_health_requested - penalty))); } Messenger.print_server_message(server, "Top 10 grossing:"); total = 0; - for ( Map.Entry entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()) ) + for (Map.Entry entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList())) { if (entry.getKey().startsWith("c.")) { @@ -350,23 +301,8 @@ public class CarpetProfiler String[] parts = entry.getKey().split("\\."); String dim = parts[1]; String name = parts[2]; - double applicableDivider = name.endsWith("(client)") ? divider :divider_1; - Messenger.print_server_message(server, String.format(" - %s in %s: %.3fms",name, dim, applicableDivider*entry.getValue())); + double applicableDivider = name.endsWith("(client)") ? divider : divider_1; + Messenger.print_server_message(server, String.format(" - %s in %s: %.3fms", name, dim, applicableDivider * entry.getValue())); } - } - - public static void prepare_entity_report(int ticks) - { - //maybe add so it only spams the sending player, but honestly - all may want to see it - time_repo.clear(); - time_repo.put("tick",0L); - test_type = 2; - tick_health_elapsed = ticks; - tick_health_requested = ticks; - current_tick_start = 0L; - current_section_start = 0L; - current_section = null; - - } -} +} \ No newline at end of file diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 3d3d8f20..571fc132 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -5,7 +5,14 @@ "mixins": [ "CommandManagerMixin", "MinecraftServer_coreMixin", + "MinecraftServer_tickspeedMixin", + "World_tickMixin", + "ServerWorld_tickMixin", + "ServerChunkManager_tickMixin", + + "MinecraftServer_motdMixin", + "ServerPlayNetworkHandlerMixin", "MinecraftServer_scarpetMixin", "PlayerListHeaderS2CPacketMixin", "WeightedPickerEntryMixin",