diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java index 773b61e3..c8cae167 100644 --- a/src/main/java/carpet/CarpetServer.java +++ b/src/main/java/carpet/CarpetServer.java @@ -109,7 +109,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit public static void tick(MinecraftServer server) { - TickSpeed.tick(server); + TickSpeed.tick(); HUDController.update_hud(server); scriptServer.tick(); diff --git a/src/main/java/carpet/commands/TickCommand.java b/src/main/java/carpet/commands/TickCommand.java index 27c6ef27..8b6b3826 100644 --- a/src/main/java/carpet/commands/TickCommand.java +++ b/src/main/java/carpet/commands/TickCommand.java @@ -2,6 +2,7 @@ package carpet.commands; import carpet.CarpetSettings; import carpet.helpers.TickSpeed; +import carpet.network.ServerNetworkHandler; import carpet.settings.SettingsManager; import carpet.utils.CarpetProfiler; import carpet.utils.Messenger; @@ -95,16 +96,13 @@ public class TickCommand private static int toggleFreeze(ServerCommandSource source, boolean isDeep) { - TickSpeed.is_paused = !TickSpeed.is_paused; - if (TickSpeed.is_paused) + TickSpeed.setFrozenState(!TickSpeed.isPaused(), isDeep); + if (TickSpeed.isPaused()) { - TickSpeed.deepFreeze = isDeep; Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen"); - } else { - TickSpeed.deepFreeze = false; Messenger.m(source, "gi Game runs normally"); } return 1; @@ -119,9 +117,10 @@ public class TickCommand private static int toggleSuperHot(ServerCommandSource source) { TickSpeed.is_superHot = !TickSpeed.is_superHot; + ServerNetworkHandler.updateSuperHotStateToConnectedPlayers(); if (TickSpeed.is_superHot) { - Messenger.m(source,"gi Superhot enabled"); + Messenger.m(source, "gi Superhot enabled"); } else { diff --git a/src/main/java/carpet/helpers/TickSpeed.java b/src/main/java/carpet/helpers/TickSpeed.java index 575867eb..a71301fb 100644 --- a/src/main/java/carpet/helpers/TickSpeed.java +++ b/src/main/java/carpet/helpers/TickSpeed.java @@ -7,7 +7,6 @@ import java.util.function.BiConsumer; import carpet.CarpetServer; import carpet.network.ServerNetworkHandler; import carpet.utils.Messenger; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; @@ -26,10 +25,43 @@ public class TickSpeed public static ServerCommandSource tick_warp_sender = null; public static int player_active_timeout = 0; public static boolean process_entities = true; - public static boolean deepFreeze = false; - public static boolean is_paused = false; + private static boolean deepFreeze = false; + private static boolean is_paused = false; public static boolean is_superHot = false; + /** + * @return Whether or not the game is in a frozen state. + * You should normally use {@link #process_entities} instead, + * since that one accounts for tick steps and superhot + */ + public static boolean isPaused() { + return is_paused; + } + + /** + * Whether or not the game is deeply frozen. + * This can be used for things that you may not normally want + * to freeze, but may need to in some situations. + * This should be checked with {@link #process_entities} to make sure the + * current tick is actually frozen, not only the game + * @return Whether or not the game is deeply frozen. + */ + public static boolean deeplyFrozen() { + return deepFreeze; + } + + /** + * Used to update the frozen state of the game. + * Handles connected clients as well. + * @param isPaused Whether or not the game is paused + * @param isDeepFreeze Whether or not the game is deeply frozen + */ + public static void setFrozenState(boolean isPaused, boolean isDeepFreeze) { + is_paused = isPaused; + deepFreeze = isPaused ? isDeepFreeze : false; + ServerNetworkHandler.updateFrozenStateToConnectedPlayers(); + } + /** * Functional interface that listens for tickrate changes. This is * implemented to allow tickrate compatibility with other mods etc. @@ -66,6 +98,7 @@ public class TickSpeed public static void add_ticks_to_run_in_pause(int ticks) { player_active_timeout = PLAYER_GRACE+ticks; + ServerNetworkHandler.updateTickPlayerActiveTimeoutToConnectedPlayers(); } public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source) @@ -155,7 +188,7 @@ public class TickSpeed } } - public static void tick(MinecraftServer server) + public static void tick() { process_entities = true; if (player_active_timeout > 0) diff --git a/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java b/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java index 9b70b707..bbaeea46 100644 --- a/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java +++ b/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java @@ -18,6 +18,6 @@ public class ChunkTicketManager_tickMixin { // pausing expiry of tickets // that will prevent also chunks from unloading, so require a deep frozen state - if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--; + if (!TickSpeed.process_entities && TickSpeed.deeplyFrozen()) age--; } } diff --git a/src/main/java/carpet/mixins/CommandFunctionManager_tickMixin.java b/src/main/java/carpet/mixins/CommandFunctionManager_tickMixin.java index 301a05d5..034f06db 100644 --- a/src/main/java/carpet/mixins/CommandFunctionManager_tickMixin.java +++ b/src/main/java/carpet/mixins/CommandFunctionManager_tickMixin.java @@ -1,5 +1,6 @@ package carpet.mixins; +import carpet.helpers.TickSpeed; import carpet.utils.CarpetProfiler; import net.minecraft.server.function.CommandFunctionManager; import org.spongepowered.asm.mixin.Mixin; @@ -12,9 +13,11 @@ public class CommandFunctionManager_tickMixin { CarpetProfiler.ProfilerToken currentSection; - @Inject(method = "tick", at = @At("HEAD")) + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) private void beforeDatapacks(CallbackInfo ci) { + if (!TickSpeed.process_entities) ci.cancel(); + else currentSection = CarpetProfiler.start_section(null, "Datapacks", CarpetProfiler.TYPE.GENERAL); } diff --git a/src/main/java/carpet/mixins/MinecraftClientMixin.java b/src/main/java/carpet/mixins/MinecraftClientMixin.java index 7e94395e..d0a340a5 100644 --- a/src/main/java/carpet/mixins/MinecraftClientMixin.java +++ b/src/main/java/carpet/mixins/MinecraftClientMixin.java @@ -1,9 +1,13 @@ package carpet.mixins; +import carpet.CarpetServer; +import carpet.helpers.TickSpeed; import carpet.network.CarpetClient; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.world.ClientWorld; 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.callback.CallbackInfo; @@ -11,9 +15,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MinecraftClient.class) public class MinecraftClientMixin { + @Shadow public ClientWorld world; + @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("HEAD")) private void onCloseGame(Screen screen, CallbackInfo ci) { CarpetClient.disconnect(); } + + @Inject(at = @At("HEAD"), method = "tick") + private void onClientTick(CallbackInfo info) { + if (this.world != null) { + if (CarpetServer.minecraft_server == null) + TickSpeed.tick(); + if (!TickSpeed.process_entities) + CarpetClient.shapes.renewShapes(); + } + } } diff --git a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java index 705a3e1e..a4934a7f 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.fakes.MinecraftServerInterface; +import carpet.helpers.TickSpeed; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; import net.minecraft.server.world.ServerWorld; @@ -69,6 +70,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut )) public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci) { + if (!TickSpeed.process_entities) + return; TICK.onTick(); NETHER_TICK.onTick(); ENDER_TICK.onTick(); diff --git a/src/main/java/carpet/network/ClientNetworkHandler.java b/src/main/java/carpet/network/ClientNetworkHandler.java index 4d3cc1ad..0d60acc0 100644 --- a/src/main/java/carpet/network/ClientNetworkHandler.java +++ b/src/main/java/carpet/network/ClientNetworkHandler.java @@ -9,6 +9,7 @@ import carpet.settings.SettingsManager; import io.netty.buffer.Unpooled; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.nbt.AbstractNumberTag; +import net.minecraft.nbt.ByteTag; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; @@ -65,6 +66,14 @@ public class ClientNetworkHandler } }); dataHandlers.put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false)); + dataHandlers.put("TickingState", (p, t) -> { + CompoundTag tickingState = (CompoundTag)t; + TickSpeed.setFrozenState(tickingState.getBoolean("is_paused"), tickingState.getBoolean("deepFreeze")); + }); + dataHandlers.put("SuperHotState", (p, t) -> { + TickSpeed.is_superHot = ((ByteTag) t).equals(ByteTag.ONE); + }); + dataHandlers.put("TickPlayerActiveTimeout", (p, t) -> TickSpeed.player_active_timeout = ((AbstractNumberTag)t).getInt()); dataHandlers.put("scShape", (p, t) -> { // deprecated if (CarpetClient.shapes != null) CarpetClient.shapes.addShape((CompoundTag)t); diff --git a/src/main/java/carpet/network/ServerNetworkHandler.java b/src/main/java/carpet/network/ServerNetworkHandler.java index 72ffc14f..f5959561 100644 --- a/src/main/java/carpet/network/ServerNetworkHandler.java +++ b/src/main/java/carpet/network/ServerNetworkHandler.java @@ -77,7 +77,7 @@ public class ServerNetworkHandler CarpetSettings.LOG.info("Player "+playerEntity.getName().getString()+" joined with a matching carpet client"); else CarpetSettings.LOG.warn("Player "+playerEntity.getName().getString()+" joined with another carpet version: "+clientVersion); - DataBuilder data = DataBuilder.create().withTickRate(); + DataBuilder data = DataBuilder.create().withTickRate().withFrozenState().withTickPlayerActiveTimeout(); // .withSuperHotState() CarpetServer.settingsManager.getRules().forEach(data::withRule); CarpetServer.extensions.forEach(e -> { SettingsManager eManager = e.customSettingsManager(); @@ -173,6 +173,42 @@ public class ServerNetworkHandler } } + public static void updateFrozenStateToConnectedPlayers() + { + if (CarpetSettings.superSecretSetting) return; + for (ServerPlayerEntity player : remoteCarpetPlayers.keySet()) + { + player.networkHandler.sendPacket(new CustomPayloadS2CPacket( + CarpetClient.CARPET_CHANNEL, + DataBuilder.create().withFrozenState().build() + )); + } + } + + public static void updateSuperHotStateToConnectedPlayers() + { + if(CarpetSettings.superSecretSetting) return; + for (ServerPlayerEntity player : remoteCarpetPlayers.keySet()) + { + player.networkHandler.sendPacket(new CustomPayloadS2CPacket( + CarpetClient.CARPET_CHANNEL, + DataBuilder.create().withSuperHotState().build() + )); + } + } + + public static void updateTickPlayerActiveTimeoutToConnectedPlayers() + { + if (CarpetSettings.superSecretSetting) return; + for (ServerPlayerEntity player : remoteCarpetPlayers.keySet()) + { + player.networkHandler.sendPacket(new CustomPayloadS2CPacket( + CarpetClient.CARPET_CHANNEL, + DataBuilder.create().withTickPlayerActiveTimeout().build() + )); + } + } + public static void broadcastCustomCommand(String command, Tag data) { if (CarpetSettings.superSecretSetting) return; @@ -240,6 +276,24 @@ public class ServerNetworkHandler tag.putFloat("TickRate", TickSpeed.tickrate); return this; } + private DataBuilder withFrozenState() + { + CompoundTag tickingState = new CompoundTag(); + tickingState.putBoolean("is_paused", TickSpeed.isPaused()); + tickingState.putBoolean("deepFreeze", TickSpeed.deeplyFrozen()); + tag.put("TickingState", tickingState); + return this; + } + private DataBuilder withSuperHotState() + { + tag.putBoolean("SuperHotState", TickSpeed.is_superHot); + return this; + } + private DataBuilder withTickPlayerActiveTimeout() + { + tag.putInt("TickPlayerActiveTimeout", TickSpeed.player_active_timeout); + return this; + } private DataBuilder withRule(ParsedRule rule) { CompoundTag rules = (CompoundTag) tag.get("Rules"); diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index fe3a8c3a..7bcab974 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -2,6 +2,7 @@ package carpet.script; import carpet.CarpetServer; import carpet.CarpetSettings; +import carpet.helpers.TickSpeed; import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; import carpet.script.value.EntityValue; @@ -973,6 +974,8 @@ public class CarpetEventServer public void tick() { + if (!TickSpeed.process_entities) + return; Iterator eventIterator = scheduledCalls.iterator(); List currentCalls = new ArrayList<>(); while(eventIterator.hasNext()) diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java index 371b840d..8be35a77 100644 --- a/src/main/java/carpet/script/utils/ShapesRenderer.java +++ b/src/main/java/carpet/script/utils/ShapesRenderer.java @@ -167,6 +167,15 @@ public class ShapesRenderer } } + public void renewShapes() + { + synchronized (shapes) + { + shapes.values().forEach(el -> el.values().forEach(shape -> { + shape.expiryTick++; + })); + } + } public abstract static class RenderedShape {