diff --git a/.gitignore b/.gitignore index b96c7fb2..b4e87106 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ build # other eclipse run/ -logs/ \ No newline at end of file +logs/ +/.apt_generated_tests/ diff --git a/build.gradle b/build.gradle index 5c41a5a6..f2cf6ac8 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,7 @@ dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.legacyfabric:yarn:${project.minecraft_version}+build.mcp" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' } // task for downloading KillTheRng diff --git a/gradle.properties b/gradle.properties index 4ff403e9..4b1eced2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.jvmargs=-Xmx3G # Fabric properties minecraft_version=1.12.2 -loader_version=0.14.19 -loom_version=1.2-SNAPSHOT +loader_version=0.14.23 +loom_version=1.3-SNAPSHOT # Mod properties mod_name=Tool-Assisted Speedrun Mod diff --git a/src/main/java/com/minecrafttas/common/Common.java b/src/main/java/com/minecrafttas/common/Common.java index 70c25759..283640ce 100644 --- a/src/main/java/com/minecrafttas/common/Common.java +++ b/src/main/java/com/minecrafttas/common/Common.java @@ -9,4 +9,10 @@ public class Common { public static final Logger LOGGER = LogManager.getLogger("Common"); public static final Marker Event = MarkerManager.getMarker("Event"); + + public static final Marker Server = MarkerManager.getMarker("Server"); + + public static final Marker Client = MarkerManager.getMarker("Client"); + + public static final Marker Timeout = MarkerManager.getMarker("Timeout"); } diff --git a/src/main/java/com/minecrafttas/common/Configuration.java b/src/main/java/com/minecrafttas/common/Configuration.java index 17c6420a..6fd879d9 100644 --- a/src/main/java/com/minecrafttas/common/Configuration.java +++ b/src/main/java/com/minecrafttas/common/Configuration.java @@ -118,7 +118,8 @@ public void delete(ConfigOptions configOption) { } public static enum ConfigOptions{ - FileToOpen("fileToOpen", ""); + FileToOpen("fileToOpen", ""), + ServerConnection("serverConnection", ""); private String configKey; private String defaultValue; diff --git a/src/main/java/com/minecrafttas/common/KeybindManager.java b/src/main/java/com/minecrafttas/common/KeybindManager.java index 538a7b0b..9a13c9f2 100644 --- a/src/main/java/com/minecrafttas/common/KeybindManager.java +++ b/src/main/java/com/minecrafttas/common/KeybindManager.java @@ -13,21 +13,23 @@ /** * Keybind manager + * @author Pancake */ -public abstract class KeybindManager implements EventClientGameLoop { +public abstract class KeybindManager implements EventClientGameLoop { public static class Keybind { - + private KeyBinding keyBinding; private String category; private Runnable onKeyDown; /** * Initialize keybind - * @param name Name of keybind - * @param category Category of keybind + * + * @param name Name of keybind + * @param category Category of keybind * @param defaultKey Default key of keybind - * @param onKeyDown Will be run when the keybind is pressed + * @param onKeyDown Will be run when the keybind is pressed */ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown) { this.keyBinding = new KeyBinding(name, defaultKey, category); @@ -36,16 +38,16 @@ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown) } } - + private List keybindings; - + /** * Initialize keybind manager */ public KeybindManager() { this.keybindings = new ArrayList<>(); } - + /** * Handle registered keybindings on game loop */ @@ -57,23 +59,24 @@ public void onRunClientGameLoop(Minecraft mc) { } protected abstract boolean isKeyDown(KeyBinding i); - + /** * Register new keybind - * @param keybind Keybind + * + * @param keybind Keybind to register */ public KeyBinding registerKeybind(Keybind keybind) { this.keybindings.add(keybind); KeyBinding keyBinding = keybind.keyBinding; - + // add category GameSettings options = Minecraft.getMinecraft().gameSettings; if (!KeyBinding.CATEGORY_ORDER.containsKey(keybind.category)) KeyBinding.CATEGORY_ORDER.put(keybind.category, KeyBinding.CATEGORY_ORDER.size() + 1); - + // add keybinding options.keyBindings = ArrayUtils.add(options.keyBindings, keyBinding); return keyBinding; } - + } \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/common/events/CompactPacketHandler.java b/src/main/java/com/minecrafttas/common/events/CompactPacketHandler.java new file mode 100644 index 00000000..15f79e7c --- /dev/null +++ b/src/main/java/com/minecrafttas/common/events/CompactPacketHandler.java @@ -0,0 +1,11 @@ +package com.minecrafttas.common.events; + +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.exception.PacketNotImplementedException; + +@FunctionalInterface +public interface CompactPacketHandler { + + public void onPacket(ByteBuffer buf, String username) throws PacketNotImplementedException; +} diff --git a/src/main/java/com/minecrafttas/common/events/EventClient.java b/src/main/java/com/minecrafttas/common/events/EventClient.java index 48cbb5d2..c789148c 100644 --- a/src/main/java/com/minecrafttas/common/events/EventClient.java +++ b/src/main/java/com/minecrafttas/common/events/EventClient.java @@ -1,7 +1,8 @@ package com.minecrafttas.common.events; import com.minecrafttas.common.Common; -import com.minecrafttas.common.events.EventListener.EventBase; +import com.minecrafttas.common.events.EventListenerRegistry.EventBase; +import com.minecrafttas.common.server.Client; import com.mojang.authlib.GameProfile; import net.minecraft.client.Minecraft; @@ -26,7 +27,7 @@ public static interface EventOpenGui extends EventBase { public static GuiScreen fireOpenGuiEvent(GuiScreen gui) { Common.LOGGER.trace(Common.Event, "Firing OpenGuiEvent"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventOpenGui) { EventOpenGui event = (EventOpenGui) eventListener; GuiScreen newGui = event.onOpenGui(gui); @@ -53,7 +54,7 @@ public static interface EventLaunchIntegratedServer extends EventBase { public static void fireOnLaunchIntegratedServer() { Common.LOGGER.trace(Common.Event, "Firing LaunchIntegratedServer"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventLaunchIntegratedServer) { EventLaunchIntegratedServer event = (EventLaunchIntegratedServer) eventListener; event.onLaunchIntegratedServer(); @@ -76,7 +77,7 @@ public static interface EventDoneLoadingWorld extends EventBase { public static void fireOnDoneLoadingWorld() { Common.LOGGER.trace(Common.Event, "Firing DoneLoadingWorld"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventDoneLoadingWorld) { EventDoneLoadingWorld event = (EventDoneLoadingWorld) eventListener; event.onDoneLoadingWorld(); @@ -99,7 +100,7 @@ public static interface EventClientTick extends EventBase { public void onClientTick(Minecraft mc); public static void fireOnClientTick(Minecraft mc) { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventClientTick) { EventClientTick event = (EventClientTick) eventListener; event.onClientTick(mc); @@ -123,7 +124,7 @@ public static interface EventClientInit extends EventBase { public static void fireOnClientInit(Minecraft mc) { Common.LOGGER.trace(Common.Event, "Firing ClientInit"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventClientInit) { EventClientInit event = (EventClientInit) eventListener; event.onClientInit(mc); @@ -146,7 +147,7 @@ public static interface EventClientGameLoop extends EventBase { public void onRunClientGameLoop(Minecraft mc); public static void fireOnClientGameLoop(Minecraft mc) { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventClientGameLoop) { EventClientGameLoop event = (EventClientGameLoop) eventListener; event.onRunClientGameLoop(mc); @@ -170,7 +171,7 @@ public static interface EventCamera extends EventBase { public CameraData onCameraEvent(CameraData dataIn); public static CameraData fireCameraEvent(CameraData dataIn) { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventCamera) { EventCamera event = (EventCamera) eventListener; CameraData data = event.onCameraEvent(dataIn); @@ -223,7 +224,7 @@ public static interface EventPlayerLeaveClientSide extends EventBase { public static void firePlayerLeaveClientSide(EntityPlayerSP player) { Common.LOGGER.trace(Common.Event, "Firing PlayerLeaveClientSideEvent"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventPlayerLeaveClientSide) { EventPlayerLeaveClientSide event = (EventPlayerLeaveClientSide) eventListener; event.onPlayerLeaveClientSide(player); @@ -247,7 +248,7 @@ public static interface EventPlayerJoinedClientSide extends EventBase { public static void firePlayerJoinedClientSide(EntityPlayerSP player) { Common.LOGGER.trace(Common.Event, "Firing PlayerJoinedClientSide"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventPlayerJoinedClientSide) { EventPlayerJoinedClientSide event = (EventPlayerJoinedClientSide) eventListener; event.onPlayerJoinedClientSide(player); @@ -264,21 +265,44 @@ public static void firePlayerJoinedClientSide(EntityPlayerSP player) { */ public static interface EventOtherPlayerJoinedClientSide extends EventBase { - public void onOtherPlayerJoinedClientSide(GameProfile profile); - /** * Fired when a different player other than yourself joins a server or a world * @param player The game profile of the player that joins the server or the world */ + public void onOtherPlayerJoinedClientSide(GameProfile profile); + + public static void fireOtherPlayerJoinedClientSide(GameProfile profile) { Common.LOGGER.trace(Common.Event, "Firing OtherPlayerJoinedClientSide"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventOtherPlayerJoinedClientSide) { EventOtherPlayerJoinedClientSide event = (EventOtherPlayerJoinedClientSide) eventListener; event.onOtherPlayerJoinedClientSide(profile); } } } - + + } + + /** + * Fired when the connection to the custom server was closed on the client side. + */ + public static interface EventDisconnectClient extends EventBase { + + /** + * Fired when the connection to the custom server was closed on the client side. + * @param client The client that is disconnecting + */ + public void onDisconnectClient(Client client); + + public static void fireDisconnectClient(Client client) { + Common.LOGGER.trace(Common.Event, "Firing EventDisconnectClient"); + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if(eventListener instanceof EventDisconnectClient) { + EventDisconnectClient event = (EventDisconnectClient) eventListener; + event.onDisconnectClient(client); + } + } + } } } diff --git a/src/main/java/com/minecrafttas/common/events/EventListener.java b/src/main/java/com/minecrafttas/common/events/EventListenerRegistry.java similarity index 64% rename from src/main/java/com/minecrafttas/common/events/EventListener.java rename to src/main/java/com/minecrafttas/common/events/EventListenerRegistry.java index b568e52c..e425d3fc 100644 --- a/src/main/java/com/minecrafttas/common/events/EventListener.java +++ b/src/main/java/com/minecrafttas/common/events/EventListenerRegistry.java @@ -2,16 +2,22 @@ import java.util.ArrayList; -public class EventListener { +public class EventListenerRegistry { private static ArrayList EVENTLISTENER_REGISTRY = new ArrayList<>(); public static void register(EventBase eventListener) { + if (eventListener == null) { + throw new NullPointerException("Tried to register a packethandler with value null"); + } EVENTLISTENER_REGISTRY.add(eventListener); } public static void unregister(EventBase eventListener) { + if (eventListener == null) { + throw new NullPointerException("Tried to unregister a packethandler with value null"); + } EVENTLISTENER_REGISTRY.remove(eventListener); } diff --git a/src/main/java/com/minecrafttas/common/events/EventServer.java b/src/main/java/com/minecrafttas/common/events/EventServer.java index 96b444d3..852d05aa 100644 --- a/src/main/java/com/minecrafttas/common/events/EventServer.java +++ b/src/main/java/com/minecrafttas/common/events/EventServer.java @@ -1,7 +1,8 @@ package com.minecrafttas.common.events; import com.minecrafttas.common.Common; -import com.minecrafttas.common.events.EventListener.EventBase; +import com.minecrafttas.common.events.EventListenerRegistry.EventBase; +import com.minecrafttas.common.server.Client; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; @@ -23,7 +24,7 @@ public static interface EventServerInit extends EventBase { public static void fireServerStartEvent(MinecraftServer server) { Common.LOGGER.trace(Common.Event, "Firing ServerStartEvent"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventServerInit) { EventServerInit event = (EventServerInit) eventListener; event.onServerInit(server); @@ -46,7 +47,7 @@ public static interface EventServerTick extends EventBase { public void onServerTick(MinecraftServer server); public static void fireOnServerTick(MinecraftServer server) { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventServerTick) { EventServerTick event = (EventServerTick) eventListener; event.onServerTick(server); @@ -64,13 +65,16 @@ public static interface EventServerStop extends EventBase { /** * Fired when the server is about to stop + *

WARNING!

+ * This method may run twice!

+ * * @param server The stopping server */ public void onServerStop(MinecraftServer server); public static void fireOnServerStop(MinecraftServer server) { Common.LOGGER.trace(Common.Event, "Firing ServerStopEvent"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventServerStop) { EventServerStop event = (EventServerStop) eventListener; event.onServerStop(server); @@ -93,7 +97,7 @@ public static interface EventServerGameLoop extends EventBase { public void onRunServerGameLoop(MinecraftServer server); public static void fireOnServerGameLoop(MinecraftServer server) { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventServerGameLoop) { EventServerGameLoop event = (EventServerGameLoop) eventListener; event.onRunServerGameLoop(server); @@ -108,7 +112,7 @@ public static interface EventPlayerJoinedServerSide extends EventBase { public static void firePlayerJoinedServerSide(EntityPlayerMP player) { Common.LOGGER.trace(Common.Event, "Firing PlayerJoinedServerSide"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventPlayerJoinedServerSide) { EventPlayerJoinedServerSide event = (EventPlayerJoinedServerSide) eventListener; event.onPlayerJoinedServerSide(player); @@ -132,7 +136,7 @@ public static interface EventPlayerLeaveServerSide extends EventBase { public static void firePlayerLeaveServerSide(EntityPlayerMP player) { Common.LOGGER.trace(Common.Event, "Firing PlayerLeaveServerSideEvent"); - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if (eventListener instanceof EventPlayerLeaveServerSide) { EventPlayerLeaveServerSide event = (EventPlayerLeaveServerSide) eventListener; event.onPlayerLeaveServerSide(player); @@ -140,4 +144,48 @@ public static void firePlayerLeaveServerSide(EntityPlayerMP player) { } } } + + /** + * Fired when authentication was successful on the server side + */ + public static interface EventClientCompleteAuthentication extends EventBase { + + /** + * Fired when authentication was successful on the server side + * @param username The username of the client that is connecting + */ + public void onClientCompleteAuthentication(String username); + + public static void fireClientCompleteAuthentication(String username) { + Common.LOGGER.trace(Common.Event, "Firing ClientCompleteAuthenticationEvent"); + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if (eventListener instanceof EventClientCompleteAuthentication) { + EventClientCompleteAuthentication event = (EventClientCompleteAuthentication) eventListener; + event.onClientCompleteAuthentication(username); + } + } + } + } + + /** + * Fired when the connection to the custom server was closed on the server side. + */ + public static interface EventDisconnectServer extends EventBase { + + /** + * Fired when the connection to the custom server was closed on the server side. + * @param client The client that is disconnecting + */ + public void onDisconnectServer(Client client); + + public static void fireDisconnectServer(Client client) { + Common.LOGGER.trace(Common.Event, "Firing CustomServerClientDisconnect"); + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if(eventListener instanceof EventDisconnectServer) { + EventDisconnectServer event = (EventDisconnectServer) eventListener; + event.onDisconnectServer(client); + } + } + } + } } diff --git a/src/main/java/com/minecrafttas/common/mixin/MixinMinecraft.java b/src/main/java/com/minecrafttas/common/mixin/MixinMinecraft.java index af5cfbe3..8a93b27b 100644 --- a/src/main/java/com/minecrafttas/common/mixin/MixinMinecraft.java +++ b/src/main/java/com/minecrafttas/common/mixin/MixinMinecraft.java @@ -1,9 +1,11 @@ package com.minecrafttas.common.mixin; +import org.objectweb.asm.Opcodes; 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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minecrafttas.common.events.EventClient.EventClientGameLoop; @@ -44,9 +46,12 @@ public void inject_loadWorld(CallbackInfo ci) { EventDoneLoadingWorld.fireOnDoneLoadingWorld(); } - @ModifyVariable(method = "displayGuiScreen", at = @At(value = "STORE")) - public GuiScreen inject_displayGuiScreen(GuiScreen guiScreen) { + @Shadow + private GuiScreen currentScreen; + + @Redirect(method = "displayGuiScreen", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;currentScreen:Lnet/minecraft/client/gui/GuiScreen;", opcode = Opcodes.PUTFIELD)) + public void modify_displayGuiScreen(Minecraft mc, GuiScreen guiScreen) { guiScreen = EventOpenGui.fireOpenGuiEvent(guiScreen); - return guiScreen; + currentScreen = guiScreen; } } diff --git a/src/main/java/com/minecrafttas/common/server/ByteBufferBuilder.java b/src/main/java/com/minecrafttas/common/server/ByteBufferBuilder.java new file mode 100644 index 00000000..fedfc8a7 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/ByteBufferBuilder.java @@ -0,0 +1,221 @@ +package com.minecrafttas.common.server; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.UUID; + +import com.minecrafttas.common.server.interfaces.PacketID; + +/** + * Helper method for creating byte buffers which get pooled from a + * {@link SecureList} + * + * @author Scribble + */ +public class ByteBufferBuilder { + + private int bufferIndex; + protected ByteBuffer buffer; + + /* Debug PacketName */ + private PacketID bufferPacketId; + /* Debug PacketContent */ + private ArrayList bufferContent = new ArrayList<>(); + + public ByteBufferBuilder(int id) { + bufferIndex = SecureList.POOL.available(); + buffer = SecureList.POOL.lock(bufferIndex); + buffer.putInt(id); + } + + /** + * Creates a new ByteBufferBuilder with a packetId + * @param packet The address to which the packet is sent. Usually an enum. + */ + public ByteBufferBuilder(PacketID packet) { + this(packet.getID()); + this.bufferPacketId = packet; + } + + /** + * Creates a ByteBufferBuilder from an existing buffer. Useful for sending the same buffer back. + * @param buf + */ + public ByteBufferBuilder(ByteBuffer buf) { + bufferIndex = SecureList.POOL.available(); + buffer = SecureList.POOL.lock(bufferIndex); + buffer.put(buf); + } + + private ByteBufferBuilder(int bufferIndex, ByteBuffer buffer, PacketID bufferName, ArrayList bufferContent) { + this.bufferIndex = bufferIndex; + this.buffer = buffer; + this.bufferPacketId = bufferName; + this.bufferContent = new ArrayList<>(bufferContent); + } + + public ByteBuffer build() { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + return this.buffer; + } + + public ByteBufferBuilder writeInt(int value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putInt(value); + bufferContent.add("int " + value); + return this; + } + + public ByteBufferBuilder writeDouble(double value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putDouble(value); + bufferContent.add("double " + value); + return this; + } + + public ByteBufferBuilder writeFloat(float value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putFloat(value); + bufferContent.add("float " + value); + return this; + } + + public ByteBufferBuilder writeLong(long value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putLong(value); + bufferContent.add("long " + value); + return this; + } + + public ByteBufferBuilder writeShort(short value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putShort(value); + bufferContent.add("short " + value); + return this; + } + + public ByteBufferBuilder writeBoolean(boolean value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.put((byte) (value ? 1 : 0)); + bufferContent.add("boolean " + value); + return this; + } + + public ByteBufferBuilder writeString(String value) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + byte[] stringbytes = value.getBytes(); + buffer.putInt(stringbytes.length); + buffer.put(stringbytes); + bufferContent.add("String " + value); + return this; + } + + public ByteBufferBuilder writeUUID(UUID uuid) { + if (buffer == null) + throw new IllegalStateException("This buffer is already closed"); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + bufferContent.add("UUID " + uuid); + return this; + } + + public ByteBufferBuilder writeByteArray(byte[] value) { + buffer.putInt(value.length); + buffer.put(value); + + bufferContent.add("ByteArray length(" + value.length + ")"); + return this; + } + + /** + * Unlocks the buffer from the pool making it available for other uses + */ + public void close() { + if (buffer != null) { + SecureList.POOL.unlock(bufferIndex); + this.buffer = null; + } + } + + @Override + public ByteBufferBuilder clone() throws CloneNotSupportedException { + int current = this.buffer.position(); + int sid = SecureList.POOL.available(); + ByteBuffer clone = SecureList.POOL.lock(sid); + + this.buffer.limit(current).position(0); + + clone.put(this.buffer); + + this.buffer.position(current); + + return new ByteBufferBuilder(sid, clone, this.bufferPacketId, this.bufferContent); + } + + public static int readInt(ByteBuffer buf) { + return buf.getInt(); + } + + public static double readDouble(ByteBuffer buf) { + return buf.getDouble(); + } + + public static float readFloat(ByteBuffer buf) { + return buf.getFloat(); + } + + public static long readLong(ByteBuffer buf) { + return buf.getLong(); + } + + public static short readShort(ByteBuffer buf) { + return buf.getShort(); + } + + public static boolean readBoolean(ByteBuffer buf) { + return buf.get() == 1 ? true : false; + } + + public static UUID readUUID(ByteBuffer buf) { + return new UUID(buf.getLong(), buf.getLong()); + } + + public static String readString(ByteBuffer buf) { + byte[] nameBytes = new byte[buf.getInt()]; + buf.get(nameBytes); + return new String(nameBytes); + } + + public static byte[] readByteArray(ByteBuffer buf) { + int length = buf.getInt(); + byte[] array = new byte[length]; + buf.get(array); + return array; + } + + /** + * Debug packetname + * + * @return + */ + public PacketID getPacketID() { + return bufferPacketId; + } + + public String getPacketContent() { + return String.join("\n", this.bufferContent); + } + + @Override + public String toString() { + return getPacketID().getName() + getPacketContent(); + } +} diff --git a/src/main/java/com/minecrafttas/common/server/Client.java b/src/main/java/com/minecrafttas/common/server/Client.java new file mode 100644 index 00000000..fedfbcd2 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/Client.java @@ -0,0 +1,344 @@ +package com.minecrafttas.common.server; + +import static com.minecrafttas.common.Common.Client; +import static com.minecrafttas.common.Common.LOGGER; +import static com.minecrafttas.common.Common.Server; +import static com.minecrafttas.common.server.SecureList.BUFFER_SIZE; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; + +import com.minecrafttas.common.Common; +import com.minecrafttas.common.events.EventClient.EventDisconnectClient; +import com.minecrafttas.common.events.EventServer.EventClientCompleteAuthentication; +import com.minecrafttas.common.server.exception.InvalidPacketException; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.PacketID; + +/** + * A custom asynchronous client + * + * @author Pancake + */ +public class Client { + + private final AsynchronousSocketChannel socket; + private final PacketID[] packetIDs; + private final ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private final ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private Future future; + + private String username; + private String ip; + private int port; + + private Side side; + + private ClientCallback callback; + /** + * True, if the client is connected to a local "integrated" server. Special + * conditions may apply + */ + private boolean local = false; + + + public enum Side { + CLIENT, SERVER; + } + + /** + * Create and connect socket + * + * @param host Host + * @param port Port + * @param packetIDs A list of PacketIDs which are registered + * @param local Property to check, if the server is a local server. If yes, + * special conditions may apply + * @throws Exception Unable to connect + */ + public Client(String host, int port, PacketID[] packetIDs, String name, boolean local) throws Exception { + LOGGER.info(Client, "Connecting server to {}:{}", host, port); + this.socket = AsynchronousSocketChannel.open(); + this.socket.connect(new InetSocketAddress(host, port)).get(); + this.socket.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + this.socket.setOption(StandardSocketOptions.TCP_NODELAY, true); + + ip = host; + this.port = port; + + this.side = Side.CLIENT; + this.packetIDs = packetIDs; + + this.createHandlers(); + + this.callback = (client) -> { + EventDisconnectClient.fireDisconnectClient(client); + }; + + this.local = local; + + LOGGER.info(Client, "Connected to server"); + + username = name; + + authenticate(name); + } + + /** + * Fork existing socket + * + * @param socket Socket + */ + public Client(AsynchronousSocketChannel socket, PacketID[] packetIDs, ClientCallback callback) { + this.socket = socket; + this.callback = callback; + try { + this.socket.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + this.socket.setOption(StandardSocketOptions.TCP_NODELAY, true); + } catch (IOException e) { + LOGGER.error(Server, "Unable to set socket options", e); + } + this.packetIDs = packetIDs; + this.createHandlers(); + this.side = Side.SERVER; + } + + /** + * Disconnecting and closing the socket. Sends a disconnect packet to the other + * side + */ + public void disconnect() { + + if (isClosed()) { + LOGGER.warn(getLoggerMarker(), "Tried to disconnect, but client {} is already closed", getId()); + return; + } + + // Sending the disconnect packet + try { + send(new ByteBufferBuilder(-2)); + } catch (Exception e) { + LOGGER.error(getLoggerMarker(), "Tried to send disconnect packet, but failed", e); + } + + try { + this.close(); + } catch (IOException e) { + LOGGER.error(getLoggerMarker(), "Tried to close socket, but failed", e); + } + } + + /** + * Create read/write buffers and handlers for socket + */ + private void createHandlers() { + // create input handler + this.readBuffer.limit(4); + if (socket == null || !socket.isOpen()) { + LOGGER.info(getLoggerMarker(), "Connection was closed"); + return; + } + this.socket.read(this.readBuffer, null, new CompletionHandler() { + + @Override + public void completed(Integer result, Object attachment) { + if (result == -1) { + LOGGER.info(getLoggerMarker(), "Stream was closed"); + return; + } + try { + // read rest of packet + readBuffer.flip(); + int lim = readBuffer.getInt(); + readBuffer.clear().limit(lim); + socket.read(readBuffer).get(); + + // handle packet + readBuffer.position(0); + handle(readBuffer); + + // read packet header again + readBuffer.clear().limit(4); + socket.read(readBuffer, null, this); + } catch (Throwable exc) { + if(exc instanceof ExecutionException && !isClosed()) { + LOGGER.debug(getLoggerMarker(), "{} terminated the connection!", getOppositeSide().name()); + try { + close(); + } catch (IOException e) { + e.printStackTrace(); + } + return; + } + LOGGER.error(getLoggerMarker(), "Unable to read packet!", exc); + } + } + + @Override + public void failed(Throwable exc, Object attachment) { + if (exc instanceof AsynchronousCloseException || exc instanceof IOException) { + if(isClosed()) { + return; + } + LOGGER.debug(getLoggerMarker(), "{} terminated the connection!", getOppositeSide().name()); + try { + close(); + } catch (IOException e) { + LOGGER.error(getLoggerMarker(), "Attempted to close connection but failed", e); + } + } else { + if(isClosed()) { + return; + } + LOGGER.error(getLoggerMarker(), "Something went wrong, terminating connection!", exc); + try { + close(); + } catch (IOException e) { + LOGGER.error(getLoggerMarker(), "Attempted to close connection but failed", e); + } + } + } + + }); + } + + /** + * Write packet to server + * + * @param id Buffer id + * @param buf Buffer + * @throws Exception Networking exception + */ + public void send(ByteBufferBuilder bufferBuilder) throws Exception { + if(bufferBuilder.getPacketID() != null && bufferBuilder.getPacketID().shouldTrace()) + LOGGER.trace(getLoggerMarker(), "Sending a {} packet to the {} with content:\n{}", bufferBuilder.getPacketID(), getOppositeSide(), bufferBuilder.getPacketContent()); + // wait for previous buffer to send + if (this.future != null && !this.future.isDone()) + this.future.get(); + + ByteBuffer buf = bufferBuilder.build(); + + // prepare buffer + buf.flip(); + this.writeBuffer.clear(); + this.writeBuffer.putInt(buf.limit()); + this.writeBuffer.put(buf); + this.writeBuffer.flip(); + + // send buffer async + this.future = this.socket.write(this.writeBuffer); + bufferBuilder.close(); + } + + /** + * Try to close socket + * + * @throws IOException Unable to close + */ + private void close() throws IOException { + if (this.socket == null || !this.socket.isOpen()) { + Common.LOGGER.warn(getLoggerMarker(), "Tried to close dead socket"); + return; + } + this.future = null; + + // Running the callback + if (callback != null) { + callback.onClose(this); + } + + this.socket.close(); + } + + private Marker getLoggerMarker() { + return side == Side.CLIENT ? Client : Server; + } + + private Side getOppositeSide() { + return side == Side.CLIENT ? Side.SERVER : Side.CLIENT; + } + + /** + * Sends then authentication packet to the server + * + * @param id Unique ID + * @throws Exception Unable to send packet + */ + private void authenticate(String id) throws Exception { + this.username = id; + LOGGER.debug(getLoggerMarker(), "Authenticating with UUID {}", id.toString()); + this.send(new ByteBufferBuilder(-1).writeString(id)); + } + + private void completeAuthentication(ByteBuffer buf) throws Exception { + if (this.username != null) { + throw new Exception("The client tried to authenticate while being authenticated already"); + } + + this.username = ByteBufferBuilder.readString(buf); + LOGGER.debug(getLoggerMarker(), "Completing authentication for user {}", username); + EventClientCompleteAuthentication.fireClientCompleteAuthentication(username); + } + + private void handle(ByteBuffer buf) { + int id = buf.getInt(); + try { + if (id == -1) { + completeAuthentication(buf); + return; + } else if (id == -2) { + LOGGER.info(getLoggerMarker(), "Disconnected by the {}", getOppositeSide().name()); + close(); + return; + } + PacketID packet = getPacketFromID(id); + PacketHandlerRegistry.handle(side, packet, buf, this.username); + } catch (PacketNotImplementedException | WrongSideException e) { + Common.LOGGER.throwing(Level.ERROR, e); + } catch (Exception e) { + Common.LOGGER.throwing(Level.ERROR, e); + } + + } + + public String getId() { + return this.username; + } + + private PacketID getPacketFromID(int id) throws InvalidPacketException { + for (PacketID packet : packetIDs) { + if (packet.getID() == id) { + return packet; + } + } + throw new InvalidPacketException(String.format("Received invalid packet with id {}", id)); + } + + public boolean isClosed() { + return this.socket == null || !this.socket.isOpen(); + } + + public String getRemote() throws IOException { + return ip + ":" + port; + } + + public boolean isLocal() { + return this.local; + } + + @FunctionalInterface + public interface ClientCallback { + public void onClose(Client client); + } +} diff --git a/src/main/java/com/minecrafttas/common/server/PacketHandlerRegistry.java b/src/main/java/com/minecrafttas/common/server/PacketHandlerRegistry.java new file mode 100644 index 00000000..8c60685a --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/PacketHandlerRegistry.java @@ -0,0 +1,81 @@ +package com.minecrafttas.common.server; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.minecrafttas.common.Common; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketHandlerBase; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; + +public class PacketHandlerRegistry { + private static final List REGISTRY = new ArrayList<>(); + + public static void register(PacketHandlerBase handler) { + if(handler==null) { + throw new NullPointerException("Tried to register a handler with value null"); + } + + if(containsClass(handler)) { + Common.LOGGER.warn("Trying to register packet handler {}, but another instance of this class is already registered!", handler.getClass().getName()); + return; + } + + if (!REGISTRY.contains(handler)) { + REGISTRY.add(handler); + } else { + Common.LOGGER.warn("Trying to register packet handler {}, but it is already registered!", handler.getClass().getName()); + } + } + + public static void unregister(PacketHandlerBase handler) { + if(handler==null) { + throw new NullPointerException("Tried to unregister a handler with value null"); + } + if (REGISTRY.contains(handler)) { + REGISTRY.remove(handler); + } else { + Common.LOGGER.warn("Trying to unregister packet handler {}, but is was not registered!", handler.getClass().getName()); + } + } + + public static void handle(Side side, PacketID packet, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + if (side != null && side == packet.getSide()) { + packet.getLambda().onPacket(buf, username); + return; + } + + boolean isImplemented = false; + for (PacketHandlerBase handler : REGISTRY) { + if (Arrays.stream(handler.getAcceptedPacketIDs()).anyMatch(packet::equals)) { + if (side == Side.CLIENT && handler instanceof ClientPacketHandler) { + ClientPacketHandler clientHandler = (ClientPacketHandler) handler; + clientHandler.onClientPacket(packet, buf, username); + isImplemented = true; + } else if (side == Side.SERVER && handler instanceof ServerPacketHandler) { + ServerPacketHandler serverHandler = (ServerPacketHandler) handler; + serverHandler.onServerPacket(packet, buf, username); + isImplemented = true; + } + } + } + if(!isImplemented) { + throw new PacketNotImplementedException(packet, side); + } + } + + private static boolean containsClass(PacketHandlerBase handler) { + for(PacketHandlerBase packethandler : REGISTRY) { + if(packethandler.getClass().equals(handler.getClass())) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/minecrafttas/common/server/SecureList.java b/src/main/java/com/minecrafttas/common/server/SecureList.java new file mode 100644 index 00000000..cb1ea11e --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/SecureList.java @@ -0,0 +1,64 @@ +package com.minecrafttas.common.server; + +import java.nio.ByteBuffer; + +/** + * Thread safe list (probably) for recycling byte buffers without reallocation + * @author Pancake + */ +public class SecureList { + + static final int BUFFER_SIZE = 1000 * 20; // 20 kB + static final int BUFFER_COUNT = 100; // * 100 => 2 MB + + public static SecureList POOL = new SecureList(BUFFER_COUNT, BUFFER_SIZE); + + private final ByteBuffer[] buffers; + private final boolean[] locked; + + /** + * Initialize secure list + * @param length Amount of byte buffers + * @param size Length of each byte buffer + */ + public SecureList(int length, int size) { + this.buffers = new ByteBuffer[length]; + this.locked = new boolean[length]; + + for (int i = 0; i < length; i++) + this.buffers[i] = ByteBuffer.allocate(size); + } + + /** + * Find available byte buffer + * @return Available byte buffer or -1 + */ + public int available() { + for (int i = 0; i < this.locked.length; i++) + if (!this.locked[i]) + return i; + return -1; + } + + /** + * Lock and return byte buffer + * @param i Index to lock + * @return Byte buffer + */ + public ByteBuffer lock(int i) { + if (this.locked[i]) + throw new RuntimeException("Tried to lock already locked buffer"); + + this.locked[i] = true; + return (ByteBuffer) this.buffers[i].clear(); + } + + /** + * Unlocke byte buffer + * @param index Index to unlock + */ + public void unlock(int index) { + this.locked[index] = false; + } + +} diff --git a/src/main/java/com/minecrafttas/common/server/Server.java b/src/main/java/com/minecrafttas/common/server/Server.java new file mode 100644 index 00000000..63ebe6f7 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/Server.java @@ -0,0 +1,177 @@ +package com.minecrafttas.common.server; + +import static com.minecrafttas.common.Common.LOGGER; +import static com.minecrafttas.common.Common.Server; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.ArrayList; +import java.util.List; + +import com.minecrafttas.common.Common; +import com.minecrafttas.common.events.EventServer.EventDisconnectServer; +import com.minecrafttas.common.server.Client.ClientCallback; +import com.minecrafttas.common.server.interfaces.PacketID; + +import net.minecraft.entity.player.EntityPlayer; + +/** + * A custom asynchronous server + * + * @author Pancake + */ +public class Server { + + private final AsynchronousServerSocketChannel serverSocket; + private final List clients; + + /** + * Create and bind socket + * + * @param port Port + * @throws Exception Unable to bind + */ + public Server(int port, PacketID[] packetIDs) throws Exception { + // create connection + LOGGER.info(Server, "Creating server on port {}", port); + this.serverSocket = AsynchronousServerSocketChannel.open(); + this.serverSocket.bind(new InetSocketAddress(port)); + + // create connection handler + this.clients = new ArrayList<>(); + this.serverSocket.accept(null, new CompletionHandler() { + + @Override + public void completed(AsynchronousSocketChannel clientSocket, Object attachment) { + + ClientCallback callback = (client) -> { + EventDisconnectServer.fireDisconnectServer(client); + clients.remove(client); + LOGGER.debug(Server, "Disconnecting player from server"); + }; + + Client newclient = new Client(clientSocket, packetIDs, callback); + clients.add(newclient); + + serverSocket.accept(null, this); + } + + @Override + public void failed(Throwable exc, Object attachment) { + if(exc instanceof AsynchronousCloseException) { + LOGGER.info(Server, "Connection to the player was closed!"); + } else { + LOGGER.error(Server, "Unable to accept client!", exc); + } + } + }); + + LOGGER.info(Server, "Server created"); + } + + /** + * Write packet to all clients + * + * @param builder The packet contents + * @throws Exception Networking exception + */ + public void sendToAll(ByteBufferBuilder builder) throws Exception { + for (Client client : this.clients) { + client.send(builder.clone()); + } + builder.close(); + } + + /** + * Send a packet to the specified username + * + * @param username The username to send the packet to + * @param builder The packet contents + * @throws Exception Networking exception + */ + public void sendTo(String username, ByteBufferBuilder builder) throws Exception { + Client client = getClient(username); + if(client != null && !client.isClosed()) { + client.send(builder); + } else { + Common.LOGGER.warn(Server, "Buffer with id {} could not be sent to the client {}: The client is closed", builder.getPacketID(), username); + removeClient(client); + } + } + + /** + * Send a packet to a specified player + * + * Similar to {@link #sendTo(String, ByteBufferBuilder)} + * + * @param player The player to send to + * @param builder The packet contents + * @throws Exception Networking exception + */ + public void sendTo(EntityPlayer player, ByteBufferBuilder builder) throws Exception { + sendTo(player.getName(), builder); + } + + + public void disconnect(String username) { + Client client = getClient(username); + client.disconnect(); + } + + public void disconnect(EntityPlayer player) { + disconnect(player.getName()); + } + + public void disconnectAll() { + for (Client client : getClients()) { + client.disconnect(); + } + } + + /** + * Try to close socket + * + * @throws IOException Unable to close + */ + public void close() throws IOException { + if (this.serverSocket == null || !this.serverSocket.isOpen()) { + Common.LOGGER.warn(Server, "Tried to close dead socket on server"); + return; + } + + this.serverSocket.close(); + } + + public boolean isClosed() { + return this.serverSocket == null || !this.serverSocket.isOpen(); + } + + /** + * Get client from username + * + * @param name Username + */ + private Client getClient(String name) { + for (Client client : this.clients) + if (client.getId().equals(name)) + return client; + + return null; + } + + public List getClients() { + return this.clients; + } + + public AsynchronousServerSocketChannel getAsynchronousSocketChannel() { + return this.serverSocket; + } + + private void removeClient(Client client) { + getClients().remove(client); + } +} diff --git a/src/main/java/com/minecrafttas/common/server/exception/InvalidPacketException.java b/src/main/java/com/minecrafttas/common/server/exception/InvalidPacketException.java new file mode 100644 index 00000000..b7d04661 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/exception/InvalidPacketException.java @@ -0,0 +1,13 @@ +package com.minecrafttas.common.server.exception; + +@SuppressWarnings("serial") +public class InvalidPacketException extends Exception { + + public InvalidPacketException() { + super(); + } + + public InvalidPacketException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/minecrafttas/common/server/exception/PacketNotImplementedException.java b/src/main/java/com/minecrafttas/common/server/exception/PacketNotImplementedException.java new file mode 100644 index 00000000..21d583dc --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/exception/PacketNotImplementedException.java @@ -0,0 +1,22 @@ +package com.minecrafttas.common.server.exception; + +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.interfaces.PacketHandlerBase; +import com.minecrafttas.common.server.interfaces.PacketID; + +@SuppressWarnings("serial") +public class PacketNotImplementedException extends Exception { + + public PacketNotImplementedException(String msg) { + super(msg); + } + + public PacketNotImplementedException(PacketID packet, Class clazz, Side side) { + super(String.format("The packet %s is not implemented in %s on the %s-Side", packet.getName(), clazz.getCanonicalName(), side)); + } + + public PacketNotImplementedException(PacketID packet, Side side) { + super(String.format("The packet %s is not implemented or not registered in getAssociatedPacketIDs on the %s-Side", packet.getName(), side)); + } + +} diff --git a/src/main/java/com/minecrafttas/common/server/exception/WrongSideException.java b/src/main/java/com/minecrafttas/common/server/exception/WrongSideException.java new file mode 100644 index 00000000..f18d7725 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/exception/WrongSideException.java @@ -0,0 +1,18 @@ +package com.minecrafttas.common.server.exception; + +import com.minecrafttas.common.server.Client; +import com.minecrafttas.common.server.interfaces.PacketID; + +public class WrongSideException extends Exception { + + private static final long serialVersionUID = 1439028694540465537L; + + public WrongSideException(PacketID packet, Client.Side side) { + super(String.format("The packet %s is sent to the wrong side: %s", packet.getName(), side.name())); + } + + public WrongSideException(String msg) { + super(msg); + } + +} diff --git a/src/main/java/com/minecrafttas/common/server/interfaces/ClientPacketHandler.java b/src/main/java/com/minecrafttas/common/server/interfaces/ClientPacketHandler.java new file mode 100644 index 00000000..0e08ae98 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/interfaces/ClientPacketHandler.java @@ -0,0 +1,11 @@ +package com.minecrafttas.common.server.interfaces; + +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; + +public interface ClientPacketHandler extends PacketHandlerBase{ + + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception; +} diff --git a/src/main/java/com/minecrafttas/common/server/interfaces/PacketHandlerBase.java b/src/main/java/com/minecrafttas/common/server/interfaces/PacketHandlerBase.java new file mode 100644 index 00000000..9c65996d --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/interfaces/PacketHandlerBase.java @@ -0,0 +1,11 @@ +package com.minecrafttas.common.server.interfaces; + +public interface PacketHandlerBase { + /** + * Declares all packet types that get routed into the {@link ClientPacketHandler#onClientPacket(PacketID, java.nio.ByteBuffer, java.util.UUID)}
+ *
+ * or {@link ServerPacketHandler#onServerPacket(PacketID, java.nio.ByteBuffer, java.util.UUID)} methods. + */ + public PacketID[] getAcceptedPacketIDs(); + +} diff --git a/src/main/java/com/minecrafttas/common/server/interfaces/PacketID.java b/src/main/java/com/minecrafttas/common/server/interfaces/PacketID.java new file mode 100644 index 00000000..522cd42b --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/interfaces/PacketID.java @@ -0,0 +1,30 @@ +package com.minecrafttas.common.server.interfaces; + +import com.minecrafttas.common.events.CompactPacketHandler; +import com.minecrafttas.common.server.Client.Side; + +public interface PacketID { + /** + * @return The numerical ID of the packet + */ + public int getID(); + /** + * Only used in combination with {@link #getLambda()} + * @return The side of the packet this is registered to + */ + public Side getSide(); + /** + * Used for compact small lambda packet handlers + * @return The lamda to run when receiving a packet + */ + public CompactPacketHandler getLambda(); + /** + * @return The name of the packet + */ + public String getName(); + + /** + * @return Whether the packet should be used in trace messages + */ + public boolean shouldTrace(); +} diff --git a/src/main/java/com/minecrafttas/common/server/interfaces/ServerPacketHandler.java b/src/main/java/com/minecrafttas/common/server/interfaces/ServerPacketHandler.java new file mode 100644 index 00000000..835aac92 --- /dev/null +++ b/src/main/java/com/minecrafttas/common/server/interfaces/ServerPacketHandler.java @@ -0,0 +1,11 @@ +package com.minecrafttas.common.server.interfaces; + +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; + +public interface ServerPacketHandler extends PacketHandlerBase{ + + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception; +} diff --git a/src/main/java/com/minecrafttas/tasmod/TASmod.java b/src/main/java/com/minecrafttas/tasmod/TASmod.java index 5447c011..8fb47b22 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmod.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmod.java @@ -7,53 +7,32 @@ import org.apache.logging.log4j.Logger; import com.minecrafttas.common.CommandRegistry; -import com.minecrafttas.common.events.EventListener; +import com.minecrafttas.common.events.EventListenerRegistry; import com.minecrafttas.common.events.EventServer.EventServerInit; import com.minecrafttas.common.events.EventServer.EventServerStop; -import com.minecrafttas.tasmod.commands.clearinputs.ClearInputsPacket; -import com.minecrafttas.tasmod.commands.clearinputs.CommandClearInputs; -import com.minecrafttas.tasmod.commands.folder.CommandFolder; -import com.minecrafttas.tasmod.commands.folder.FolderPacket; -import com.minecrafttas.tasmod.commands.fullplay.CommandFullPlay; -import com.minecrafttas.tasmod.commands.fullplay.FullPlayPacket; -import com.minecrafttas.tasmod.commands.fullrecord.CommandFullRecord; -import com.minecrafttas.tasmod.commands.fullrecord.FullRecordPacket; -import com.minecrafttas.tasmod.commands.loadtas.CommandLoadTAS; -import com.minecrafttas.tasmod.commands.loadtas.LoadTASPacket; -import com.minecrafttas.tasmod.commands.playback.CommandPlay; -import com.minecrafttas.tasmod.commands.playuntil.CommandPlayUntil; -import com.minecrafttas.tasmod.commands.playuntil.PlayUntilPacket; -import com.minecrafttas.tasmod.commands.recording.CommandRecord; -import com.minecrafttas.tasmod.commands.restartandplay.CommandRestartAndPlay; -import com.minecrafttas.tasmod.commands.restartandplay.RestartAndPlayPacket; -import com.minecrafttas.tasmod.commands.savetas.CommandSaveTAS; -import com.minecrafttas.tasmod.commands.savetas.SaveTASPacket; -import com.minecrafttas.tasmod.ktrng.KTRNGSeedPacket; -import com.minecrafttas.tasmod.ktrng.KTRNGStartSeedPacket; +import com.minecrafttas.common.server.PacketHandlerRegistry; +import com.minecrafttas.common.server.Server; +import com.minecrafttas.tasmod.commands.CommandClearInputs; +import com.minecrafttas.tasmod.commands.CommandFolder; +import com.minecrafttas.tasmod.commands.CommandFullPlay; +import com.minecrafttas.tasmod.commands.CommandFullRecord; +import com.minecrafttas.tasmod.commands.CommandLoadTAS; +import com.minecrafttas.tasmod.commands.CommandPlay; +import com.minecrafttas.tasmod.commands.CommandPlayUntil; +import com.minecrafttas.tasmod.commands.CommandRecord; +import com.minecrafttas.tasmod.commands.CommandRestartAndPlay; +import com.minecrafttas.tasmod.commands.CommandSaveTAS; +import com.minecrafttas.tasmod.commands.CommandSavestate; +import com.minecrafttas.tasmod.commands.CommandTickrate; import com.minecrafttas.tasmod.ktrng.KillTheRNGHandler; -import com.minecrafttas.tasmod.networking.IdentificationPacket; -import com.minecrafttas.tasmod.networking.PacketSerializer; -import com.minecrafttas.tasmod.networking.TASmodNetworkServer; -import com.minecrafttas.tasmod.playback.PlaybackController; -import com.minecrafttas.tasmod.playback.server.InitialSyncStatePacket; -import com.minecrafttas.tasmod.playback.server.SyncStatePacket; -import com.minecrafttas.tasmod.playback.server.TASstateServer; -import com.minecrafttas.tasmod.savestates.client.InputSavestatesPacket; -import com.minecrafttas.tasmod.savestates.server.LoadstatePacket; -import com.minecrafttas.tasmod.savestates.server.SavestateCommand; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler; -import com.minecrafttas.tasmod.savestates.server.SavestatePacket; -import com.minecrafttas.tasmod.savestates.server.files.SavestateTrackerFile; -import com.minecrafttas.tasmod.savestates.server.motion.MotionPacket; -import com.minecrafttas.tasmod.savestates.server.motion.RequestMotionPacket; -import com.minecrafttas.tasmod.savestates.server.playerloading.SavestatePlayerLoadingPacket; -import com.minecrafttas.tasmod.tickratechanger.AdvanceTickratePacket; -import com.minecrafttas.tasmod.tickratechanger.ChangeTickratePacket; -import com.minecrafttas.tasmod.tickratechanger.CommandTickrate; -import com.minecrafttas.tasmod.tickratechanger.PauseTickratePacket; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerServer; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; +import com.minecrafttas.tasmod.savestates.files.SavestateTrackerFile; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer; -import com.minecrafttas.tasmod.ticksync.TickSyncPacket; -import com.minecrafttas.tasmod.util.TickScheduler; +import com.minecrafttas.tasmod.ticksync.TickSyncServer; +import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.util.Scheduler; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.impl.FabricLoaderImpl; @@ -69,24 +48,61 @@ public class TASmod implements ModInitializer, EventServerInit, EventServerStop{ private static MinecraftServer serverInstance; - public static final Logger logger = LogManager.getLogger("TASmod"); + public static final Logger LOGGER = LogManager.getLogger("TASmod"); - public static TASstateServer containerStateServer; + public static PlaybackControllerServer playbackControllerServer=new PlaybackControllerServer();; - public static SavestateHandler savestateHandler; + public static SavestateHandlerServer savestateHandlerServer; public static KillTheRNGHandler ktrngHandler; - public static TASmodNetworkServer packetServer; - public static TickrateChangerServer tickratechanger; - public static final TickScheduler tickSchedulerServer = new TickScheduler(); + public static TickSyncServer ticksyncServer; + + public static final Scheduler tickSchedulerServer = new Scheduler(); + + public static Server server; + + public static final int networkingport = 8999; + public static final boolean isDevEnvironment = FabricLoaderImpl.INSTANCE.isDevelopmentEnvironment(); + + @Override + public void onInitialize() { + + LOGGER.info("Initializing TASmod"); + + + // Start ticksync + ticksyncServer = new TickSyncServer(); + + // Initilize KillTheRNG + LOGGER.info("Testing connection with KillTheRNG"); + ktrngHandler=new KillTheRNGHandler(FabricLoaderImpl.INSTANCE.isModLoaded("killtherng")); + + // Initialize TickrateChanger + tickratechanger = new TickrateChangerServer(LOGGER); + + // Register event listeners + EventListenerRegistry.register(this); + EventListenerRegistry.register(ticksyncServer); + EventListenerRegistry.register(tickratechanger); + EventListenerRegistry.register(ktrngHandler); + + // Register packet handlers + LOGGER.info(LoggerMarkers.Networking, "Registering network handlers"); + PacketHandlerRegistry.register(ticksyncServer); + PacketHandlerRegistry.register(tickratechanger); + PacketHandlerRegistry.register(ktrngHandler); + PacketHandlerRegistry.register(playbackControllerServer); + } + @Override public void onServerInit(MinecraftServer server) { + LOGGER.info("Initializing server"); serverInstance = server; - containerStateServer=new TASstateServer(); + // Command handling CommandRegistry.registerServerCommand(new CommandTickrate(), server); @@ -96,7 +112,7 @@ public void onServerInit(MinecraftServer server) { CommandRegistry.registerServerCommand(new CommandLoadTAS(), server); CommandRegistry.registerServerCommand(new CommandFolder(), server); CommandRegistry.registerServerCommand(new CommandClearInputs(), server); - CommandRegistry.registerServerCommand(new SavestateCommand(), server); + CommandRegistry.registerServerCommand(new CommandSavestate(), server); CommandRegistry.registerServerCommand(new CommandFullRecord(), server); CommandRegistry.registerServerCommand(new CommandFullPlay(), server); CommandRegistry.registerServerCommand(new CommandRestartAndPlay(), server); @@ -109,89 +125,44 @@ public void onServerInit(MinecraftServer server) { } catch (IOException e) { e.printStackTrace(); } - - savestateHandler=new SavestateHandler(server, logger); - - try { - packetServer = new TASmodNetworkServer(logger); - } catch (IOException e) { - e.printStackTrace(); - } + + savestateHandlerServer = new SavestateHandlerServer(server, LOGGER); + PacketHandlerRegistry.register(savestateHandlerServer); if(!server.isDedicatedServer()) { TASmod.tickratechanger.ticksPerSecond=0F; TASmod.tickratechanger.tickrateSaved=20F; + } else { + // Starting custom server instance + try { + TASmod.server = new Server(networkingport, TASmodPackets.values()); + } catch (Exception e) { + LOGGER.error("Unable to launch TASmod server: {}", e.getMessage()); + } } } @Override - public void onServerStop(MinecraftServer server) { + public void onServerStop(MinecraftServer mcserver) { serverInstance=null; - packetServer.close(); + + if(mcserver.isDedicatedServer()) { + try { + if (server != null) server.close(); + } catch (IOException e) { + LOGGER.error("Unable to close TASmod server: {}", e); + e.printStackTrace(); + } + } + + if(savestateHandlerServer != null) { + PacketHandlerRegistry.unregister(savestateHandlerServer); // Unregistering the savestatehandler, as a new instance is registered in onServerStart() + savestateHandlerServer = null; + } } public static MinecraftServer getServerInstance() { return serverInstance; } - @Override - public void onInitialize() { - logger.info("Initializing TASmod"); - EventListener.register(this); - - logger.info("Testing connection with KillTheRNG"); - ktrngHandler=new KillTheRNGHandler(FabricLoaderImpl.INSTANCE.isModLoaded("killtherng")); - EventListener.register(ktrngHandler); - - tickratechanger = new TickrateChangerServer(logger); - EventListener.register(tickratechanger); - - - PacketSerializer.registerPacket(IdentificationPacket.class); - // Ticksync - PacketSerializer.registerPacket(TickSyncPacket.class); - - - //Tickratechanger - PacketSerializer.registerPacket(ChangeTickratePacket.class); - PacketSerializer.registerPacket(PauseTickratePacket.class); - PacketSerializer.registerPacket(AdvanceTickratePacket.class); - - - // Savestates - PacketSerializer.registerPacket(SavestatePacket.class); - PacketSerializer.registerPacket(LoadstatePacket.class); - - PacketSerializer.registerPacket(InputSavestatesPacket.class); - PacketSerializer.registerPacket(SavestatePlayerLoadingPacket.class); - - PacketSerializer.registerPacket(RequestMotionPacket.class); - PacketSerializer.registerPacket(MotionPacket.class); - - // KillTheRNG - PacketSerializer.registerPacket(KTRNGSeedPacket.class); - PacketSerializer.registerPacket(KTRNGStartSeedPacket.class); - - // Recording/Playback - PacketSerializer.registerPacket(SyncStatePacket.class); - PacketSerializer.registerPacket(InitialSyncStatePacket.class); - - PacketSerializer.registerPacket(ClearInputsPacket.class); - - PacketSerializer.registerPacket(FullRecordPacket.class); - PacketSerializer.registerPacket(FullPlayPacket.class); - - PacketSerializer.registerPacket(RestartAndPlayPacket.class); - - // Storing - PacketSerializer.registerPacket(SaveTASPacket.class); - PacketSerializer.registerPacket(LoadTASPacket.class); - - // Misc - PacketSerializer.registerPacket(PlaybackController.TeleportPlayerPacket.class); - PacketSerializer.registerPacket(FolderPacket.class); - - PacketSerializer.registerPacket(PlayUntilPacket.class); - - } } diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index ee3e2fb4..9ee3c417 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -1,10 +1,13 @@ package com.minecrafttas.tasmod; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.apache.logging.log4j.Level; import org.lwjgl.input.Keyboard; import com.minecrafttas.common.Configuration; @@ -12,39 +15,47 @@ import com.minecrafttas.common.KeybindManager; import com.minecrafttas.common.KeybindManager.Keybind; import com.minecrafttas.common.events.EventClient.EventClientInit; +import com.minecrafttas.common.events.EventClient.EventOpenGui; import com.minecrafttas.common.events.EventClient.EventPlayerJoinedClientSide; import com.minecrafttas.common.events.EventClient.EventPlayerLeaveClientSide; -import com.minecrafttas.common.events.EventListener; +import com.minecrafttas.common.events.EventListenerRegistry; +import com.minecrafttas.common.server.Client; +import com.minecrafttas.common.server.PacketHandlerRegistry; +import com.minecrafttas.common.server.Server; import com.minecrafttas.tasmod.externalGui.InputContainerView; import com.minecrafttas.tasmod.gui.InfoHud; import com.minecrafttas.tasmod.handlers.InterpolationHandler; import com.minecrafttas.tasmod.handlers.LoadingScreenHandler; -import com.minecrafttas.tasmod.networking.TASmodNetworkClient; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.playback.PlaybackSerialiser; -import com.minecrafttas.tasmod.playback.server.InitialSyncStatePacket; -import com.minecrafttas.tasmod.playback.server.TASstateClient; -import com.minecrafttas.tasmod.savestates.server.LoadstatePacket; -import com.minecrafttas.tasmod.savestates.server.SavestatePacket; +import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerClient; +import com.minecrafttas.tasmod.ticksync.TickSyncClient; +import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.util.Scheduler; import com.minecrafttas.tasmod.util.ShieldDownloader; -import com.minecrafttas.tasmod.util.TickScheduler; import com.minecrafttas.tasmod.virtual.VirtualInput; import com.minecrafttas.tasmod.virtual.VirtualKeybindings; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.loader.impl.FabricLoaderImpl; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiControls; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.settings.KeyBinding; +import net.minecraft.server.MinecraftServer; -public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventPlayerLeaveClientSide{ - +public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventOpenGui{ - public static boolean isDevEnvironment; public static VirtualInput virtual; + public static TickSyncClient ticksyncClient; + public static PlaybackSerialiser serialiser = new PlaybackSerialiser(); public static final String tasdirectory = Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles"; @@ -55,13 +66,13 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even public static ShieldDownloader shieldDownloader; - public static TASmodNetworkClient packetClient; - public static TickrateChangerClient tickratechanger = new TickrateChangerClient(); - public static TickScheduler gameLoopSchedulerClient = new TickScheduler(); + public static Scheduler gameLoopSchedulerClient = new Scheduler(); - public static TickScheduler tickSchedulerClient = new TickScheduler(); + public static Scheduler tickSchedulerClient = new Scheduler(); + + public static Scheduler openMainMenuScheduler = new Scheduler(); public static Configuration config; @@ -71,6 +82,10 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even public static InterpolationHandler interpolation = new InterpolationHandler(); + public static SavestateHandlerClient savestateHandlerClient = new SavestateHandlerClient(); + + public static Client client; + public static void createTASDir() { File tasDir=new File(tasdirectory); if(!tasDir.exists()) { @@ -87,34 +102,29 @@ public static void createSavestatesDir() { @Override public void onInitializeClient() { - EventListener.register(this); - isDevEnvironment = FabricLoaderImpl.INSTANCE.isDevelopmentEnvironment(); + // Load config Minecraft mc = Minecraft.getMinecraft(); config = new Configuration("TASmod configuration", new File(mc.mcDataDir, "config/tasmod.cfg")); + // Execute /restartandplay. Load the file to start from the config. If it exists load the playback file on start. String fileOnStart = config.get(ConfigOptions.FileToOpen); - if (fileOnStart.isEmpty()) { fileOnStart = null; } else { config.reset(ConfigOptions.FileToOpen); } - virtual=new VirtualInput(fileOnStart); - EventListener.register(virtual); - EventListener.register(virtual.getContainer()); - + // Initialize InfoHud hud = new InfoHud(); - EventListener.register(hud); - + // Initialize shield downloader shieldDownloader = new ShieldDownloader(); - EventListener.register(shieldDownloader); - + // Initialize loading screen handler loadingScreenHandler = new LoadingScreenHandler(); - EventListener.register(loadingScreenHandler); - + // Initialize Ticksync + ticksyncClient = new TickSyncClient(); + // Initialize keybind manager keybindManager = new KeybindManager() { protected boolean isKeyDown(KeyBinding i) { @@ -122,9 +132,37 @@ protected boolean isKeyDown(KeyBinding i) { }; }; - EventListener.register(keybindManager); - EventListener.register(interpolation); + // Register event listeners + EventListenerRegistry.register(this); + EventListenerRegistry.register(virtual); + EventListenerRegistry.register(hud); + EventListenerRegistry.register(shieldDownloader); + EventListenerRegistry.register(loadingScreenHandler); + EventListenerRegistry.register(ticksyncClient); + EventListenerRegistry.register(keybindManager); + EventListenerRegistry.register(interpolation); + EventListenerRegistry.register((EventOpenGui)(gui -> { + if(gui instanceof GuiMainMenu) { + openMainMenuScheduler.runAllTasks(); + } + return gui; + })); + + // Register packet handlers + LOGGER.info(LoggerMarkers.Networking, "Registering network handlers on client"); + PacketHandlerRegistry.register(virtual.getContainer()); //TODO Move container/playbackcontroller out of virtual package + PacketHandlerRegistry.register(ticksyncClient); + PacketHandlerRegistry.register(tickratechanger); + PacketHandlerRegistry.register(savestateHandlerClient); + + // Starting local server instance + try { + TASmod.server = new Server(TASmod.networkingport-1, TASmodPackets.values()); + } catch (Exception e) { + LOGGER.error("Unable to launch TASmod server: {}", e.getMessage()); + } + } @Override @@ -133,19 +171,32 @@ public void onClientInit(Minecraft mc) { List blockedKeybindings = new ArrayList<>(); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Tickrate 0 Key", "TASmod", Keyboard.KEY_F8, () -> TASmodClient.tickratechanger.togglePause()))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Advance Tick", "TASmod", Keyboard.KEY_F9, () -> TASmodClient.tickratechanger.advanceTick()))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASstateClient.setOrSend(TASstate.NONE)))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Create Savestate", "TASmod", Keyboard.KEY_J, () -> TASmodClient.packetClient.sendToServer(new SavestatePacket())))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Load Latest Savestate", "TASmod", Keyboard.KEY_K, () -> TASmodClient.packetClient.sendToServer(new LoadstatePacket())))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASmodClient.virtual.getContainer().setTASState(TASstate.NONE)))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Create Savestate", "TASmod", Keyboard.KEY_J, () -> { + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SAVE).writeInt(-1)); + } catch (Exception e) { + e.printStackTrace(); + } + }))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Load Latest Savestate", "TASmod", Keyboard.KEY_K, () -> { + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeInt(-1)); + } catch (Exception e) { + e.printStackTrace(); + } + }))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Open InfoGui Editor", "TASmod", Keyboard.KEY_F6, () -> Minecraft.getMinecraft().displayGuiScreen(TASmodClient.hud)))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Buffer View", "TASmod", Keyboard.KEY_NUMPAD0, () -> InputContainerView.startBufferView()))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing", "TASmod", Keyboard.KEY_F12, () -> { - TASmod.tickSchedulerServer.add(() -> { - try { - Thread.sleep(1000L); - } catch (InterruptedException e) { - e.printStackTrace(); - } - }); + TASmodClient.client.disconnect(); + }))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing2", "TASmod", Keyboard.KEY_F7, () -> { + try { + TASmodClient.client = new Client("localhost", TASmod.networkingport-1, TASmodPackets.values(), mc.getSession().getProfile().getName(), true); + } catch (Exception e) { + e.printStackTrace(); + } }))); blockedKeybindings.forEach(VirtualKeybindings::registerBlockedKeyBinding); @@ -160,37 +211,96 @@ public void onClientInit(Minecraft mc) { @Override public void onPlayerJoinedClientSide(EntityPlayerSP player) { Minecraft mc = Minecraft.getMinecraft(); + ServerData data = mc.getCurrentServerData(); + MinecraftServer server = TASmod.getServerInstance(); - if(mc.isIntegratedServerRunning()) - TASmodClient.packetClient = new TASmodNetworkClient(TASmod.logger); - else { - String full = mc.getCurrentServerData().serverIP; - String[] fullsplit = full.split(":"); - if(fullsplit.length == 1) { - TASmodClient.packetClient = new TASmodNetworkClient(TASmod.logger, full, 3111); - } else if(fullsplit.length == 2){ - String ip = fullsplit[0]; - TASmodClient.packetClient = new TASmodNetworkClient(TASmod.logger, ip, 3111); - } else { - TASmod.logger.error("Something went wrong while connecting. The ip seems to be wrong"); - } + String ip = null; + int port; + boolean local; + if(server!=null) { + ip = "localhost"; + port = TASmod.networkingport-1; + local = true; + } else { + ip = data.serverIP.split(":")[0]; + port = TASmod.networkingport; + local = false; } - TASmodClient.packetClient.sendToServer(new InitialSyncStatePacket(TASmodClient.virtual.getContainer().getState())); + String connectedIP = null; + try { + connectedIP = client.getRemote(); + } catch (IOException e) { + e.printStackTrace(); + } + if(!(ip+":"+port).equals(connectedIP)) { + try { + LOGGER.info("Closing client connection: {}", client.getRemote()); + client.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + final String IP = ip; + final int PORT = port; + gameLoopSchedulerClient.add(()->{ + try { + // connect to server and authenticate + client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), local); //TODO set timeout by tickrate + } catch (Exception e) { + LOGGER.error("Unable to connect TASmod client: {}", e.getMessage()); + e.printStackTrace(); + } + ticksyncClient.setEnabled(true); + }); + } } @Override - public void onPlayerLeaveClientSide(EntityPlayerSP player) { - try { - if(TASmodClient.packetClient!=null) { - TASmodClient.packetClient.killClient(); - TASmodClient.packetClient=null; + public GuiScreen onOpenGui(GuiScreen gui) { + if (gui instanceof GuiMainMenu) { + if (client == null) { + Minecraft mc = Minecraft.getMinecraft(); + + String IP = "localhost"; + int PORT = TASmod.networkingport - 1; + + // Get the connection on startup from config + String configAddress = config.get(ConfigOptions.ServerConnection); + if(configAddress != null && !configAddress.isEmpty()) { + String[] ipSplit = configAddress.split(":"); + IP = ipSplit[0]; + try { + PORT = Integer.parseInt(ipSplit[1]); + } catch (Exception e) { + LOGGER.catching(Level.ERROR, e); + IP = "localhost"; + PORT = TASmod.networkingport - 1; + } + } + + try { + // connect to server and authenticate + client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), true); + } catch (Exception e) { + LOGGER.error("Unable to connect TASmod client: {}", e); + } + ticksyncClient.setEnabled(true); + } + } else if (gui instanceof GuiControls) { + TASmodClient.virtual.getContainer().setTASState(TASstate.NONE); // Set the TASState to nothing to avoid collisions + if (TASmodClient.tickratechanger.ticksPerSecond == 0) { + TASmodClient.tickratechanger.pauseClientGame(false); // Unpause the game + waszero = true; + } + } else if (!(gui instanceof GuiControls)) { + if (waszero) { + waszero = false; + TASmodClient.tickratechanger.pauseClientGame(true); } - } catch (IOException e) { - e.printStackTrace(); } + return gui; } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/clearinputs/CommandClearInputs.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java similarity index 69% rename from src/main/java/com/minecrafttas/tasmod/commands/clearinputs/CommandClearInputs.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java index e37f89f8..b93ac489 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/clearinputs/CommandClearInputs.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java @@ -1,6 +1,8 @@ -package com.minecrafttas.tasmod.commands.clearinputs; +package com.minecrafttas.tasmod.commands; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -23,7 +25,11 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { if(sender instanceof EntityPlayer) { - TASmod.packetServer.sendToAll(new ClearInputsPacket()); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_CLEAR_INPUTS)); + } catch (Exception e) { + e.printStackTrace(); + } } } @Override diff --git a/src/main/java/com/minecrafttas/tasmod/commands/folder/CommandFolder.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java similarity index 51% rename from src/main/java/com/minecrafttas/tasmod/commands/folder/CommandFolder.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java index 1876c9cd..3642126f 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/folder/CommandFolder.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java @@ -1,9 +1,17 @@ -package com.minecrafttas.tasmod.commands.folder; +package com.minecrafttas.tasmod.commands; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -32,10 +40,16 @@ public int getRequiredPermissionLevel() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { if (args.length == 1) { + short action = 0; if (args[0].equalsIgnoreCase("savestates")) { - TASmod.packetServer.sendTo(new FolderPacket(0), (EntityPlayerMP) sender); + action = 0; } else if (args[0].equalsIgnoreCase("tasfiles")) { - TASmod.packetServer.sendTo(new FolderPacket(1), (EntityPlayerMP) sender); + action = 1; + } + try { + TASmod.server.sendTo((EntityPlayerMP) sender, new TASmodBufferBuilder(TASmodPackets.OPEN_FOLDER).writeShort(action)); + } catch (Exception e) { + e.printStackTrace(); } } } @@ -51,4 +65,27 @@ public List getTabCompletions(MinecraftServer server, ICommandSender sen return tab; } + public static void openTASFolder() { + File file = new File(TASmodClient.tasdirectory); + try { + if (!file.exists()) + file.mkdir(); + Desktop.getDesktop().open(file); + } catch (IOException e) { + LOGGER.error("Something went wrong while opening ", file.getPath()); + e.printStackTrace(); + } + } + + public static void openSavestates() { + File file = new File(TASmodClient.savestatedirectory); + try { + if (!file.exists()) + file.mkdir(); + Desktop.getDesktop().open(file); + } catch (IOException e) { + LOGGER.error("Something went wrong while opening ", file.getPath()); + e.printStackTrace(); + } + } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/fullplay/CommandFullPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java similarity index 58% rename from src/main/java/com/minecrafttas/tasmod/commands/fullplay/CommandFullPlay.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java index cfd0521a..d4d3f18b 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/fullplay/CommandFullPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java @@ -1,9 +1,11 @@ -package com.minecrafttas.tasmod.commands.fullplay; +package com.minecrafttas.tasmod.commands; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.savestates.server.exceptions.LoadstateException; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; +import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -27,7 +29,7 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { try { - TASmod.savestateHandler.loadState(0, false, false); + TASmod.savestateHandlerServer.loadState(0, false, false); } catch (LoadstateException e) { sender.sendMessage(new TextComponentString(TextFormatting.RED+"Failed to load a savestate: "+e.getMessage())); return; @@ -36,10 +38,14 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args e.printStackTrace(); return; } finally { - TASmod.savestateHandler.state=SavestateState.NONE; + TASmod.savestateHandlerServer.state=SavestateState.NONE; + } + TASmod.playbackControllerServer.setServerState(TASstate.PLAYBACK); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_FULLPLAY)); + } catch (Exception e) { + e.printStackTrace(); } - TASmod.containerStateServer.setServerState(TASstate.PLAYBACK); - TASmod.packetServer.sendToAll(new FullPlayPacket()); } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/fullrecord/CommandFullRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java similarity index 58% rename from src/main/java/com/minecrafttas/tasmod/commands/fullrecord/CommandFullRecord.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java index b2b438be..585a50d1 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/fullrecord/CommandFullRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java @@ -1,9 +1,11 @@ -package com.minecrafttas.tasmod.commands.fullrecord; +package com.minecrafttas.tasmod.commands; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -27,7 +29,7 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { try { - TASmod.savestateHandler.saveState(0, false); + TASmod.savestateHandlerServer.saveState(0, false); } catch (SavestateException e) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getMessage())); return; @@ -36,10 +38,13 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getCause().toString())); return; } finally { - TASmod.savestateHandler.state = SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; + } + TASmod.playbackControllerServer.setServerState(TASstate.RECORDING); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_FULLRECORD)); + } catch (Exception e) { + e.printStackTrace(); } - TASmod.containerStateServer.setServerState(TASstate.RECORDING); - TASmod.packetServer.sendToAll(new FullRecordPacket()); } - } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/loadtas/CommandLoadTAS.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java similarity index 88% rename from src/main/java/com/minecrafttas/tasmod/commands/loadtas/CommandLoadTAS.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java index e2f18298..858b4de2 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/loadtas/CommandLoadTAS.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.commands.loadtas; +package com.minecrafttas.tasmod.commands; import java.io.File; import java.io.FileFilter; @@ -6,6 +6,8 @@ import java.util.List; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; @@ -44,7 +46,11 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args } name=name.concat(args[i]+spacer); } - TASmod.packetServer.sendToAll(new LoadTASPacket(name)); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_LOAD).writeString(name)); + } catch (Exception e) { + e.printStackTrace(); + } } } else { sender.sendMessage(new TextComponentString(TextFormatting.RED + "You have no permission to use this command")); diff --git a/src/main/java/com/minecrafttas/tasmod/commands/playback/CommandPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java similarity index 93% rename from src/main/java/com/minecrafttas/tasmod/commands/playback/CommandPlay.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java index 096ffff6..2f03c713 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/playback/CommandPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.commands.playback; +package com.minecrafttas.tasmod.commands; import java.util.List; @@ -42,7 +42,7 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args return; } if (args.length < 1) { - TASmod.containerStateServer.togglePlayback(); + TASmod.playbackControllerServer.togglePlayback(); } else if (args.length > 1) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "Too many arguments. " + getUsage(sender))); } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/playuntil/CommandPlayUntil.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java similarity index 76% rename from src/main/java/com/minecrafttas/tasmod/commands/playuntil/CommandPlayUntil.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java index 9140f222..d9f284af 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/playuntil/CommandPlayUntil.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java @@ -1,6 +1,8 @@ -package com.minecrafttas.tasmod.commands.playuntil; +package com.minecrafttas.tasmod.commands; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -29,7 +31,11 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args } catch (NumberFormatException e) { throw new CommandException("{} is not a number", args[0]); } - TASmod.packetServer.sendToAll(new PlayUntilPacket(i)); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_PLAYUNTIL).writeInt(i)); + } catch (Exception e) { + e.printStackTrace(); + } } else { sender.sendMessage(new TextComponentString("Stops the next playback one tick before the specified tick and lets you record from there:\n\n/playuntil 10, runs the playback until tick 9 and will record from there. Useful when you can't savestate")); diff --git a/src/main/java/com/minecrafttas/tasmod/commands/recording/CommandRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java similarity index 93% rename from src/main/java/com/minecrafttas/tasmod/commands/recording/CommandRecord.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java index 85411b30..56f58e36 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/recording/CommandRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.commands.recording; +package com.minecrafttas.tasmod.commands; import java.util.List; @@ -42,7 +42,7 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args return; } if (args.length < 1) { - TASmod.containerStateServer.toggleRecording(); + TASmod.playbackControllerServer.toggleRecording(); TASmod.tickSchedulerServer.add(() ->{ TASmod.ktrngHandler.broadcastStartSeed(); }); diff --git a/src/main/java/com/minecrafttas/tasmod/commands/restartandplay/CommandRestartAndPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java similarity index 80% rename from src/main/java/com/minecrafttas/tasmod/commands/restartandplay/CommandRestartAndPlay.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java index dcec9534..60bd5f6d 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/restartandplay/CommandRestartAndPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.commands.restartandplay; +package com.minecrafttas.tasmod.commands; import java.io.File; import java.io.FileFilter; @@ -7,8 +7,10 @@ import java.util.List; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.savestates.server.exceptions.LoadstateException; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; @@ -46,14 +48,18 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args name=name.concat(args[i]+spacer); } try { - TASmod.savestateHandler.loadState(0, false); + TASmod.savestateHandlerServer.loadState(0, false); } catch (LoadstateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - TASmod.containerStateServer.setServerState(TASstate.PLAYBACK); - TASmod.packetServer.sendToAll(new RestartAndPlayPacket(args[0])); + TASmod.playbackControllerServer.setServerState(TASstate.PLAYBACK); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_RESTARTANDPLAY).writeString(args[0])); + } catch (Exception e) { + e.printStackTrace(); + } } } else { sender.sendMessage(new TextComponentString(TextFormatting.RED + "You have no permission to use this command")); diff --git a/src/main/java/com/minecrafttas/tasmod/commands/savetas/CommandSaveTAS.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java similarity index 89% rename from src/main/java/com/minecrafttas/tasmod/commands/savetas/CommandSaveTAS.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java index 58922bd5..4eaa6202 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/savetas/CommandSaveTAS.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.commands.savetas; +package com.minecrafttas.tasmod.commands; import java.io.File; import java.io.FileFilter; @@ -7,6 +7,8 @@ import com.google.common.collect.ImmutableList; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; @@ -52,7 +54,11 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args } name = name.concat(args[i] + spacer); } - TASmod.packetServer.sendToAll(new SaveTASPacket(name)); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_SAVE).writeString(name)); + } catch (Exception e) { + e.printStackTrace(); + } } } else { sender.sendMessage(new TextComponentString(TextFormatting.RED + "You have no permission to use this command")); @@ -81,6 +87,7 @@ public List getTabCompletions(MinecraftServer server, ICommandSender sen public List getFilenames() { List tab = new ArrayList(); File folder = new File(Minecraft.getMinecraft().mcDataDir, "saves" + File.separator + "tasfiles"); + File[] listOfFiles = folder.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateCommand.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java similarity index 86% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateCommand.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java index 26048d98..7aee1de1 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateCommand.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java @@ -1,13 +1,13 @@ -package com.minecrafttas.tasmod.savestates.server; +package com.minecrafttas.tasmod.commands; import java.io.IOException; import java.util.List; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.savestates.server.exceptions.LoadstateException; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateDeleteException; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; +import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateDeleteException; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -18,7 +18,7 @@ import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.event.ClickEvent; -public class SavestateCommand extends CommandBase { +public class CommandSavestate extends CommandBase { @Override public String getName() { @@ -74,8 +74,8 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args deleteMultiple(args); } } else if ("list".equals(args[0])) { - sender.sendMessage(new TextComponentString(String.format("The current savestate index is %s%s", TextFormatting.AQUA, TASmod.savestateHandler.getCurrentIndex()))); - sender.sendMessage(new TextComponentString(String.format("Available indexes are %s%s", TextFormatting.AQUA, TASmod.savestateHandler.getIndexesAsString().isEmpty() ? "None" : TASmod.savestateHandler.getIndexesAsString()))); + sender.sendMessage(new TextComponentString(String.format("The current savestate index is %s%s", TextFormatting.AQUA, TASmod.savestateHandlerServer.getCurrentIndex()))); + sender.sendMessage(new TextComponentString(String.format("Available indexes are %s%s", TextFormatting.AQUA, TASmod.savestateHandlerServer.getIndexesAsString().isEmpty() ? "None" : TASmod.savestateHandlerServer.getIndexesAsString()))); } else if ("help".equals(args[0])) { if (args.length == 1) { sendHelp(sender); @@ -100,7 +100,7 @@ private void sendHelp(ICommandSender sender) throws CommandException { } private void sendHelp(ICommandSender sender, int i) throws CommandException { - int currentIndex = TASmod.savestateHandler.getCurrentIndex(); + int currentIndex = TASmod.savestateHandlerServer.getCurrentIndex(); if (i > 3) { throw new CommandException("This help page doesn't exist (yet?)", new Object[] {}); } @@ -142,7 +142,7 @@ public List getTabCompletions(MinecraftServer server, ICommandSender sen if (args.length == 1) { return getListOfStringsMatchingLastWord(args, new String[] { "save", "load", "delete", "list", "help"}); } else if (args.length == 2 && !"list".equals(args[0])) { - sender.sendMessage(new TextComponentString("Available indexes: " + TextFormatting.AQUA + TASmod.savestateHandler.getIndexesAsString())); + sender.sendMessage(new TextComponentString("Available indexes: " + TextFormatting.AQUA + TASmod.savestateHandlerServer.getIndexesAsString())); } return super.getTabCompletions(server, sender, args, targetPos); } @@ -151,14 +151,14 @@ public List getTabCompletions(MinecraftServer server, ICommandSender sen private void saveLatest() throws CommandException { try { - TASmod.savestateHandler.saveState(); + TASmod.savestateHandlerServer.saveState(); } catch (SavestateException e) { throw new CommandException(e.getMessage(), new Object[] {}); } catch (IOException e) { e.printStackTrace(); throw new CommandException(e.getMessage(), new Object[] {}); } finally { - TASmod.savestateHandler.state = SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; } } @@ -168,47 +168,47 @@ private void saveWithIndex(String[] args) throws CommandException { if (indexToSave <= 0) { // Disallow to save on Savestate 0 indexToSave = -1; } - TASmod.savestateHandler.saveState(indexToSave, true); + TASmod.savestateHandlerServer.saveState(indexToSave, true); } catch (SavestateException e) { throw new CommandException(e.getMessage(), new Object[] {}); } catch (IOException e) { e.printStackTrace(); throw new CommandException(e.getMessage(), new Object[] {}); } finally { - TASmod.savestateHandler.state = SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; } } private void loadLatest() throws CommandException { try { - TASmod.savestateHandler.loadState(); + TASmod.savestateHandlerServer.loadState(); } catch (LoadstateException e) { throw new CommandException(e.getMessage(), new Object[] {}); } catch (IOException e) { e.printStackTrace(); throw new CommandException(e.getMessage(), new Object[] {}); } finally { - TASmod.savestateHandler.state = SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; } } private void loadLatest(String[] args) throws CommandException { try { - TASmod.savestateHandler.loadState(processIndex(args[1]), true); + TASmod.savestateHandlerServer.loadState(processIndex(args[1]), true); } catch (LoadstateException e) { throw new CommandException(e.getMessage(), new Object[] {}); } catch (IOException e) { e.printStackTrace(); throw new CommandException(e.getMessage(), new Object[] {}); } finally { - TASmod.savestateHandler.state = SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; } } private void delete(String[] args) throws CommandException { int arg1 = processIndex(args[1]); try { - TASmod.savestateHandler.deleteSavestate(arg1); + TASmod.savestateHandlerServer.deleteSavestate(arg1); } catch (SavestateDeleteException e) { throw new CommandException(e.getMessage(), new Object[] {}); } @@ -216,7 +216,7 @@ private void delete(String[] args) throws CommandException { private void deleteMultiple(String[] args) throws CommandException { try { - TASmod.savestateHandler.deleteSavestate(processIndex(args[1]), processIndex(args[2])); + TASmod.savestateHandlerServer.deleteSavestate(processIndex(args[1]), processIndex(args[2])); } catch (SavestateDeleteException e) { throw new CommandException(e.getMessage(), new Object[] {}); } @@ -226,11 +226,11 @@ private void deleteMultiple(String[] args) throws CommandException { private int processIndex(String arg) throws CommandException { if ("~".equals(arg)) { - return TASmod.savestateHandler.getCurrentIndex(); + return TASmod.savestateHandlerServer.getCurrentIndex(); } else if (arg.matches("~-?\\d")) { arg = arg.replace("~", ""); int i = Integer.parseInt(arg); - return TASmod.savestateHandler.getCurrentIndex() + i; + return TASmod.savestateHandlerServer.getCurrentIndex() + i; } else { int i = 0; try { diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/CommandTickrate.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandTickrate.java similarity index 96% rename from src/main/java/com/minecrafttas/tasmod/tickratechanger/CommandTickrate.java rename to src/main/java/com/minecrafttas/tasmod/commands/CommandTickrate.java index e0bf9e1b..e004798e 100644 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/CommandTickrate.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandTickrate.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.tickratechanger; +package com.minecrafttas.tasmod.commands; import java.util.List; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/clearinputs/ClearInputsPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/clearinputs/ClearInputsPacket.java deleted file mode 100644 index f22b5fb6..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/clearinputs/ClearInputsPacket.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.minecrafttas.tasmod.commands.clearinputs; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; - -public class ClearInputsPacket implements Packet{ - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if(side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP)playerz; - player.getServerWorld().addScheduledTask(()->{ - if(player.canUseCommand(2, "clearinputs")) { - TASmod.packetServer.sendToAll(this); - } - }); - } else { - Minecraft.getMinecraft().addScheduledTask(()->{ - TASmodClient.virtual.getContainer().clear(); - }); - } - - } - - @Override - public void serialize(PacketBuffer buf) { - - } - - @Override - public void deserialize(PacketBuffer buf) { - - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/folder/FolderPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/folder/FolderPacket.java deleted file mode 100644 index cb805329..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/folder/FolderPacket.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.minecrafttas.tasmod.commands.folder; - -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class FolderPacket implements Packet { - int command; - - public FolderPacket() { - } - - /** - * 0: Open savestates folder - * 1: Open TASdir folder - * @param command The folder to open - */ - public FolderPacket(int command) { - this.command=command; - } - - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - switch(command) { - case 0: - OpenStuff.openSavestates(); - break; - case 1: - OpenStuff.openTASFolder(); - break; - } - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(command); - } - - @Override - public void deserialize(PacketBuffer buf) { - command=buf.readInt(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/folder/OpenStuff.java b/src/main/java/com/minecrafttas/tasmod/commands/folder/OpenStuff.java deleted file mode 100644 index ee84ebf4..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/folder/OpenStuff.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.minecrafttas.tasmod.commands.folder; - -import java.awt.Desktop; -import java.io.File; -import java.io.IOException; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; - -public class OpenStuff { - - public static void openTASFolder() { - File file = new File(TASmodClient.tasdirectory); - try { - if (!file.exists()) - file.mkdir(); - Desktop.getDesktop().open(file); - } catch (IOException e) { - TASmod.logger.error("Something went wrong while opening ", file.getPath()); - e.printStackTrace(); - } - } - - public static void openSavestates() { - File file = new File(TASmodClient.savestatedirectory); - try { - if (!file.exists()) - file.mkdir(); - Desktop.getDesktop().open(file); - } catch (IOException e) { - TASmod.logger.error("Something went wrong while opening ", file.getPath()); - e.printStackTrace(); - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/fullplay/FullPlayPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/fullplay/FullPlayPacket.java deleted file mode 100644 index 86a7ae0e..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/fullplay/FullPlayPacket.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.minecrafttas.tasmod.commands.fullplay; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.events.OpenGuiEvents; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class FullPlayPacket implements Packet { - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - OpenGuiEvents.stateWhenOpened = TASstate.PLAYBACK; - Minecraft mc = Minecraft.getMinecraft(); - TASmodClient.tickSchedulerClient.add(()->{ - mc.world.sendQuittingDisconnectingPacket(); - mc.loadWorld((WorldClient) null); - mc.displayGuiScreen(new GuiMainMenu()); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - } - - @Override - public void deserialize(PacketBuffer buf) { - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/fullrecord/FullRecordPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/fullrecord/FullRecordPacket.java deleted file mode 100644 index 55c4242e..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/fullrecord/FullRecordPacket.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.minecrafttas.tasmod.commands.fullrecord; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.events.OpenGuiEvents; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class FullRecordPacket implements Packet{ - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - OpenGuiEvents.stateWhenOpened = TASstate.RECORDING; - TASmodClient.virtual.getContainer().clear(); - Minecraft mc = Minecraft.getMinecraft(); - TASmodClient.tickSchedulerClient.add(()->{ - mc.world.sendQuittingDisconnectingPacket(); - mc.loadWorld((WorldClient) null); - mc.displayGuiScreen(new GuiMainMenu()); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - - } - - @Override - public void deserialize(PacketBuffer buf) { - - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/loadtas/LoadTASPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/loadtas/LoadTASPacket.java deleted file mode 100644 index 09c5059c..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/loadtas/LoadTASPacket.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.minecrafttas.tasmod.commands.loadtas; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; - -public class LoadTASPacket implements Packet{ - private String name; - - public LoadTASPacket() { - } - - public LoadTASPacket(String name) { - this.name=name; - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if (side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP) playerz; - player.getServerWorld().addScheduledTask(() -> { - if (player.canUseCommand(2, "loadtas")) { - TASmod.packetServer.sendToAll(this); - } - }); - } else { - Minecraft mc = Minecraft.getMinecraft(); - mc.addScheduledTask(() -> { - try { - TASmodClient.virtual.loadInputs(name); - } catch (IOException e) { - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); - return; - } - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Loaded inputs from " + name + ".mctas")); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(name.getBytes().length); - buf.writeCharSequence(name, Charset.defaultCharset()); - } - - @Override - public void deserialize(PacketBuffer buf) { - int length = buf.readInt(); - name = (String) buf.readCharSequence(length, StandardCharsets.UTF_8); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/playuntil/PlayUntilPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/playuntil/PlayUntilPacket.java deleted file mode 100644 index b4bdbe58..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/playuntil/PlayUntilPacket.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.minecrafttas.tasmod.commands.playuntil; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class PlayUntilPacket implements Packet { - - private int until; - - public PlayUntilPacket() { - } - - public PlayUntilPacket(int until) { - this.until = until; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - TASmodClient.virtual.getContainer().setPlayUntil(until); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(until); - } - - @Override - public void deserialize(PacketBuffer buf) { - until = buf.readInt(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/restartandplay/RestartAndPlayPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/restartandplay/RestartAndPlayPacket.java deleted file mode 100644 index 044db2cf..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/restartandplay/RestartAndPlayPacket.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.minecrafttas.tasmod.commands.restartandplay; - -import java.nio.charset.Charset; - -import com.minecrafttas.common.Configuration.ConfigOptions; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class RestartAndPlayPacket implements Packet{ - private String name; - - public RestartAndPlayPacket() { - } - - public RestartAndPlayPacket(String name) { - this.name=name; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - try { - Thread.sleep(100L); - } catch (InterruptedException e) { - e.printStackTrace(); - } - Minecraft.getMinecraft().addScheduledTask(() -> { - TASmodClient.config.set(ConfigOptions.FileToOpen, name); - System.exit(0); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(name.getBytes().length); - buf.writeCharSequence(name, Charset.defaultCharset()); - - } - - @Override - public void deserialize(PacketBuffer buf) { - int length = buf.readInt(); - name = (String) buf.readCharSequence(length, Charset.defaultCharset()); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/savetas/SaveTASPacket.java b/src/main/java/com/minecrafttas/tasmod/commands/savetas/SaveTASPacket.java deleted file mode 100644 index f2f067db..00000000 --- a/src/main/java/com/minecrafttas/tasmod/commands/savetas/SaveTASPacket.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.minecrafttas.tasmod.commands.savetas; - -import java.io.IOException; -import java.nio.charset.Charset; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; - -public class SaveTASPacket implements Packet { - String name; - - public SaveTASPacket() { - } - - public SaveTASPacket(String recordingName) { - name = recordingName; - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if (side.isClient()) { - Minecraft mc = Minecraft.getMinecraft(); - mc.addScheduledTask(() -> { - try { - TASmodClient.virtual.saveInputs(name); - } catch (IOException e) { - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); - return; - } - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Saved inputs to " + name + ".mctas")); - }); - } else { - EntityPlayerMP player = (EntityPlayerMP) playerz; - player.getServerWorld().addScheduledTask(() -> { - if (player.canUseCommand(2, "savetas")) { - TASmod.packetServer.sendToAll(this); - } - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(name.getBytes().length); - buf.writeCharSequence(name, Charset.defaultCharset()); - } - - @Override - public void deserialize(PacketBuffer buf) { - int length = buf.readInt(); - name = (String) buf.readCharSequence(length, Charset.defaultCharset()); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java index cf8ad171..a22d3359 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java @@ -1,7 +1,9 @@ package com.minecrafttas.tasmod.events; -import com.minecrafttas.common.events.EventListener; -import com.minecrafttas.common.events.EventListener.EventBase; +import com.minecrafttas.common.events.EventListenerRegistry; +import com.minecrafttas.common.events.EventListenerRegistry.EventBase; + +import net.minecraft.client.Minecraft; public interface EventClient { @@ -17,7 +19,7 @@ public static interface EventDrawHotbar extends EventBase{ public void onDrawHotbar(); public static void fireOnDrawHotbar() { - for (EventBase eventListener : EventListener.getEventListeners()) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { if(eventListener instanceof EventDrawHotbar) { EventDrawHotbar event = (EventDrawHotbar) eventListener; event.onDrawHotbar(); @@ -25,4 +27,48 @@ public static void fireOnDrawHotbar() { } } } + + /** + * Fired at the end of a client tick + * @author Scribble + * + */ + public static interface EventClientTickPost extends EventBase{ + + /** + * Fired at the end of a client tick + */ + public void onClientTickPost(Minecraft mc); + + public static void fireOnClientPostTick(Minecraft mc) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if(eventListener instanceof EventClientTickPost) { + EventClientTickPost event = (EventClientTickPost) eventListener; + event.onClientTickPost(mc); + } + } + } + } + + /** + * Fired when the tickrate changes on the client side + * @author Scribble + * + */ + public static interface EventClientTickrateChange extends EventBase{ + + /** + * Fired at the end of a client tick + */ + public void onClientTickrateChange(float tickrate); + + public static void fireOnClientTickrateChange(float tickrate) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if(eventListener instanceof EventClientTickrateChange) { + EventClientTickrateChange event = (EventClientTickrateChange) eventListener; + event.onClientTickrateChange(tickrate); + } + } + } + } } diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventServer.java b/src/main/java/com/minecrafttas/tasmod/events/EventServer.java index 80acd424..6b591dda 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventServer.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventServer.java @@ -1,11 +1,15 @@ package com.minecrafttas.tasmod.events; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + import java.io.File; -import com.minecrafttas.common.events.EventListener.EventBase; -import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.common.events.EventListenerRegistry; +import com.minecrafttas.common.events.EventListenerRegistry.EventBase; import com.minecrafttas.tasmod.util.LoggerMarkers; +import net.minecraft.server.MinecraftServer; + public interface EventServer { /** @@ -24,7 +28,7 @@ public static interface EventSavestate extends EventBase { public void onSavestateEvent(int index, File target, File current); public static void fireSavestateEvent(int index, File target, File current) { - TASmod.logger.trace(LoggerMarkers.Event, "SavestateEvent {} {} {}", index, target, current); + LOGGER.trace(LoggerMarkers.Event, "SavestateEvent {} {} {}", index, target, current); for (EventBase eventListener : TASmodEventListener.getEventListeners()) { if(eventListener instanceof EventSavestate) { EventSavestate event = (EventSavestate) eventListener; @@ -50,7 +54,7 @@ public static interface EventLoadstate extends EventBase { public void onLoadstateEvent(int index, File target, File current); public static void fireLoadstateEvent(int index, File target, File current) { - TASmod.logger.trace(LoggerMarkers.Event, "LoadstateEvent {} {} {}", index, target, current); + LOGGER.trace(LoggerMarkers.Event, "LoadstateEvent {} {} {}", index, target, current); for (EventBase eventListener : TASmodEventListener.getEventListeners()) { if(eventListener instanceof EventLoadstate) { EventLoadstate event = (EventLoadstate) eventListener; @@ -74,7 +78,7 @@ public static interface EventCompleteLoadstate extends EventBase{ public void onLoadstateComplete(); public static void fireLoadstateComplete() { - TASmod.logger.trace(LoggerMarkers.Event, "LoadstateCompleteEvent"); + LOGGER.trace(LoggerMarkers.Event, "LoadstateCompleteEvent"); for (EventBase eventListener : TASmodEventListener.getEventListeners()) { if(eventListener instanceof EventCompleteLoadstate) { EventCompleteLoadstate event = (EventCompleteLoadstate) eventListener; @@ -83,5 +87,50 @@ public static void fireLoadstateComplete() { } } } + + /** + * Fired at the end of a server tick + * @author Scribble + * + */ + public static interface EventServerTickPost extends EventBase{ + + /** + * Fired at the end of a server tick + */ + public void onServerTickPost(MinecraftServer minecraftServer); + + public static void fireServerTickPost(MinecraftServer minecraftServer) { +// LOGGER.trace(LoggerMarkers.Event, "ServerTickPostEvent"); + for (EventBase eventListener : TASmodEventListener.getEventListeners()) { + if(eventListener instanceof EventServerTickPost) { + EventServerTickPost event = (EventServerTickPost) eventListener; + event.onServerTickPost(minecraftServer); + } + } + } + } + + /** + * Fired when the tickrate changes on the server side + * @author Scribble + * + */ + public static interface EventServerTickrateChange extends EventBase{ + + /** + * Fired at the end of a client tick + */ + public void onServerTickrateChange(float tickrate); + + public static void fireOnServerTickrateChange(float tickrate) { + for (EventBase eventListener : EventListenerRegistry.getEventListeners()) { + if(eventListener instanceof EventServerTickrateChange) { + EventServerTickrateChange event = (EventServerTickrateChange) eventListener; + event.onServerTickrateChange(tickrate); + } + } + } + } } diff --git a/src/main/java/com/minecrafttas/tasmod/events/OpenGuiEvents.java b/src/main/java/com/minecrafttas/tasmod/events/OpenGuiEvents.java deleted file mode 100644 index ab07fe34..00000000 --- a/src/main/java/com/minecrafttas/tasmod/events/OpenGuiEvents.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.minecrafttas.tasmod.events; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.playback.server.TASstateClient; - -import net.minecraft.client.gui.GuiControls; -import net.minecraft.client.gui.GuiIngameMenu; -import net.minecraft.client.gui.GuiMainMenu; - -public class OpenGuiEvents { - /** - * The state that should be used when the main menu opens - */ - public static TASstate stateWhenOpened = null; - - /** - * Called when the main menu opens - * - * @param guiMainMenu The menu that was opened - */ - public static void openGuiMainMenu(GuiMainMenu guiMainMenu) { - } - - /** - * Called when the Ingame Menu opens - * - * @param guiIngameMenu The menu that was opened - */ - public static void openGuiIngameMenu(GuiIngameMenu guiIngameMenu) { - } - - public static boolean waszero; - - /** - * Called then the Controls Gui opens - * - * @param guiControls The gui that was opened - */ - public static void openGuiControls(GuiControls guiControls) { - if (TASmodClient.tickratechanger.ticksPerSecond == 0 || TASmodClient.tickratechanger.advanceTick) { - TASmod.logger.info("Pausing game during GuiControls"); - TASmodClient.tickratechanger.pauseGame(false); - TASstateClient.setOrSend(stateWhenOpened); - waszero = true; - } - } - - /** - * Called then the Controls Gui closes - * - * @param guiControls The gui that was opened - */ - public static void closeGuiControls(GuiControls guiControls) { - if (waszero) { - TASmod.logger.info("Unpausing the game again"); - waszero = false; - TASmodClient.tickratechanger.pauseGame(true); - } - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java b/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java index 94a158b3..2b0e7b9f 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java +++ b/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java @@ -1,6 +1,6 @@ package com.minecrafttas.tasmod.events; -import com.minecrafttas.common.events.EventListener; +import com.minecrafttas.common.events.EventListenerRegistry; -public class TASmodEventListener extends EventListener{ +public class TASmodEventListener extends EventListenerRegistry{ } diff --git a/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java b/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java index 17f4b569..c6bd87f3 100644 --- a/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java +++ b/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java @@ -16,12 +16,16 @@ import javax.swing.border.EmptyBorder; import javax.swing.table.DefaultTableModel; -import com.minecrafttas.tasmod.playback.PlaybackController; -import com.minecrafttas.tasmod.playback.PlaybackController.TickInputContainer; +import com.minecrafttas.common.events.EventClient.EventClientTick; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; import com.minecrafttas.tasmod.virtual.VirtualInput; +import net.minecraft.client.Minecraft; + @Deprecated -public class InputContainerView extends JFrame { +public class InputContainerView extends JFrame implements EventClientTick{ private static final long serialVersionUID = -1823965270972132025L; private JPanel contentPane; @@ -114,7 +118,7 @@ public static void update(VirtualInput input) { if (model == null) { return; } - PlaybackController container = input.getContainer(); + PlaybackControllerClient container = input.getContainer(); if (container == null || container.isEmpty()) { return; } @@ -175,4 +179,9 @@ public void run() { } }); } + + @Override + public void onClientTick(Minecraft mc) { + update(TASmodClient.virtual); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerTimeOut.java b/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerTimeOut.java index 6909adfc..a36a7c96 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerTimeOut.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerTimeOut.java @@ -8,35 +8,38 @@ import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.resources.I18n; -public class GuiMultiplayerTimeOut extends GuiScreen{ +public class GuiMultiplayerTimeOut extends GuiScreen { private GuiScreen previous; - + public GuiMultiplayerTimeOut() { - previous=new GuiMainMenu(); + previous = new GuiMainMenu(); } + @Override public void initGui() { - this.buttonList.add(new GuiButton(0, width / 2 -100, height / 2 + 70, "Continue")); + this.buttonList.add(new GuiButton(0, width / 2 - 100, height / 2 + 70, "Continue")); super.initGui(); } + @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); - + ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); int width = scaled.getScaledWidth(); int height = scaled.getScaledHeight(); - - drawCenteredString(fontRenderer,I18n.format("TASmod: Timed out"), width / 2, height / 4 + 50 + -16, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("Lost or could not make a connection to the TASmod on the server side"), width / 2, height / 4 + 50 + -6, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("Possible Cause:"), width / 2, height / 4 + 50 + 14, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("The server has no TASmod installed or the server lagged too much."), width / 2, height / 4 + 50 + 24, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("It's also possible to get this message in singleplayer if the integrated server stopped responding."), width / 2, height / 4 + 50 + 34, 0xFFFFFF); + + drawCenteredString(fontRenderer, I18n.format("TASmod: Timed out"), width / 2, height / 4 + 50 + -16, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("Lost or could not make a connection to the TASmod on the server side"), width / 2, height / 4 + 50 + -6, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("Possible Cause:"), width / 2, height / 4 + 50 + 14, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("The server has no TASmod installed or the server lagged too much."), width / 2, height / 4 + 50 + 24, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("It's also possible to get this message in singleplayer if the integrated server stopped responding."), width / 2, height / 4 + 50 + 34, 0xFFFFFF); super.drawScreen(mouseX, mouseY, partialTicks); } + @Override protected void actionPerformed(GuiButton button) { - if(button.id==0) { + if (button.id == 0) { Minecraft.getMinecraft().displayGuiScreen(new GuiMultiplayer(previous)); } } diff --git a/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerWarn.java b/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerWarn.java index c745f279..e079ed48 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerWarn.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/GuiMultiplayerWarn.java @@ -7,34 +7,38 @@ import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.resources.I18n; -public class GuiMultiplayerWarn extends GuiScreen{ +public class GuiMultiplayerWarn extends GuiScreen { private GuiScreen previous; + public GuiMultiplayerWarn(GuiScreen screen) { - previous=screen; + previous = screen; } + @Override public void initGui() { - this.buttonList.add(new GuiButton(0, width / 2 -100, height / 2 + 70, "Continue")); + this.buttonList.add(new GuiButton(0, width / 2 - 100, height / 2 + 70, "Continue")); super.initGui(); } + @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); - + ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); int width = scaled.getScaledWidth(); int height = scaled.getScaledHeight(); - - drawCenteredString(fontRenderer,I18n.format("WARNING"), width / 2, height / 4 + 50 + -16, 0xCE0000); - drawCenteredString(fontRenderer,I18n.format("Do NOT join a server that has not installed the TASmod (e.g. Hypixel)."), width / 2, height / 4 + 50 + -6, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("You will softlock your game for a few seconds then disconnect!"), width / 2, height / 4 + 50 + 4, 0xFFFFFF); - drawCenteredString(fontRenderer,I18n.format("This mod only works together with a server."), width / 2, height / 4 + 50 + 14, 0xFFFFFF); - + + drawCenteredString(fontRenderer, I18n.format("WARNING"), width / 2, height / 4 + 50 + -16, 0xCE0000); + drawCenteredString(fontRenderer, I18n.format("Do NOT join a server that has not installed the TASmod (e.g. Hypixel)."), width / 2, height / 4 + 50 + -6, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("You will softlock your game for a few seconds then disconnect!"), width / 2, height / 4 + 50 + 4, 0xFFFFFF); + drawCenteredString(fontRenderer, I18n.format("This mod only works together with a server."), width / 2, height / 4 + 50 + 14, 0xFFFFFF); + super.drawScreen(mouseX, mouseY, partialTicks); } + @Override protected void actionPerformed(GuiButton button) { - if(button.id==0) { + if (button.id == 0) { Minecraft.getMinecraft().displayGuiScreen(new GuiMultiplayer(previous)); } } diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index d8c9b0c9..b1bcd126 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -9,6 +9,8 @@ import java.util.Properties; import java.util.concurrent.Callable; +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; @@ -18,8 +20,8 @@ import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbar; import com.minecrafttas.tasmod.handlers.InterpolationHandler; import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.playback.controlbytes.ControlByteHandler; +import com.minecrafttas.tasmod.playback.ControlByteHandler; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.util.TrajectoriesCalculator; import com.mojang.realmsclient.gui.ChatFormatting; @@ -73,12 +75,17 @@ public void tick() { private int gridSizeY=14; public Properties configuration; + private boolean resetLayout; public static List lists = new ArrayList<>(); private void setDefaults(String string, int y) { + setDefaults(string, y, false); + } + + private void setDefaults(String string, int y, boolean enabled) { configuration.setProperty(string + "_x", "0"); configuration.setProperty(string + "_y", y + ""); - configuration.setProperty(string + "_visible", "false"); + configuration.setProperty(string + "_visible", enabled?"true":"false"); configuration.setProperty(string + "_rect", "false"); saveConfig(); } @@ -94,6 +101,11 @@ public void identify(int mouseX, int mouseY) { try { x = Integer.parseInt(configuration.getProperty(label.displayName + "_x")); y = Integer.parseInt(configuration.getProperty(label.displayName + "_y")); + + Pair newPos = getScreenOffset(x, y, label); + + x = newPos.getLeft(); + y = newPos.getRight(); } catch (NumberFormatException e) { configuration.setProperty(label.displayName + "_x", "0"); configuration.setProperty(label.displayName + "_y", "0"); @@ -116,7 +128,7 @@ public void identify(int mouseX, int mouseY) { } @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { - if (mouseButton == 1) { + if (mouseButton == 2) { identify(mouseX, mouseY); if (currentlyDraggedIndex != -1) { String id = lists.get(currentlyDraggedIndex).displayName; @@ -126,7 +138,7 @@ public void identify(int mouseX, int mouseY) { currentlyDraggedIndex = -1; } return; - } else if (mouseButton == 2) { + } else if (mouseButton == 1) { identify(mouseX, mouseY); if (currentlyDraggedIndex != -1) { String id = lists.get(currentlyDraggedIndex).displayName; @@ -182,6 +194,9 @@ private int snapToGridY(int y) { * Saves the Configuration */ private void saveConfig() { + if(!(Minecraft.getMinecraft().currentScreen instanceof InfoHud) || configuration == null) { + return; + } try { File tasmodDir = new File(Minecraft.getMinecraft().mcDataDir, "tasmod"); tasmodDir.mkdir(); @@ -208,11 +223,15 @@ public boolean checkInit() { /* Check whether already rendered before */ try { configuration = new Properties(); - File tasmodDir = new File(Minecraft.getMinecraft().mcDataDir, "tasmod"); - tasmodDir.mkdir(); - File configFile = new File(tasmodDir, "infogui2.cfg"); - if (!configFile.exists()) configFile.createNewFile(); - configuration.load(new FileReader(configFile)); + if (!resetLayout) { + File tasmodDir = new File(Minecraft.getMinecraft().mcDataDir, "tasmod"); + tasmodDir.mkdir(); + File configFile = new File(tasmodDir, "infogui2.cfg"); + if (!configFile.exists()) configFile.createNewFile(); + configuration.load(new FileReader(configFile)); + }else { + resetLayout = false; + } lists = new ArrayList(); /* ====================== */ String title = "tickrate"; @@ -304,23 +323,23 @@ public boolean checkInit() { lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) { return "State"; - }else { - TASstate state=TASmodClient.virtual.getContainer().getState(); - ChatFormatting format=ChatFormatting.WHITE; - String out=""; - if(state==TASstate.PLAYBACK) { - out="Playback"; - format=ChatFormatting.GREEN; - }else if(state==TASstate.RECORDING){ - out="Recording"; - format=ChatFormatting.RED; - }else if(state==TASstate.PAUSED) { - out="Paused"; - format=ChatFormatting.YELLOW; - }else if(state==TASstate.NONE) { - out=""; - } - return String.format("%s%s", format, out); + } else { + TASstate state = TASmodClient.virtual.getContainer().getState(); + ChatFormatting format = ChatFormatting.WHITE; + String out = ""; + if (state == TASstate.PLAYBACK) { + out = "Playback"; + format = ChatFormatting.GREEN; + } else if (state == TASstate.RECORDING) { + out = "Recording"; + format = ChatFormatting.RED; + } else if (state == TASstate.PAUSED) { + out = "Paused"; + format = ChatFormatting.YELLOW; + } else if (state == TASstate.NONE) { + out = ""; + } + return String.format("%s%s", format, out); } })); @@ -389,17 +408,18 @@ public boolean checkInit() { return dMonitor.getSeed(); })); - y += 14; + + y = height - 28; title = "playback_index"; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); + if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y, true); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "PlaybackIndex"; return Integer.toString(TASmodClient.virtual.getContainer().index()); })); - y += 14; + y = height - 14; title = "keystrokes"; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); + if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y, true); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Keystrokes"; return keystrokes(); @@ -426,34 +446,45 @@ public void onDrawHotbar() { int ypos=190; for (InfoLabel label : lists) { label.tick(); + + int lx = label.x; + int ly = label.y; + + Pair newPos = getScreenOffset(lx, ly, label); + + lx = newPos.getLeft(); + ly = newPos.getRight(); + if (label.visible) { - drawRectWithText(label.renderText, label.x, label.y, label.renderRect); + drawRectWithText(label.renderText, lx, ly, label.renderRect); } else if (Minecraft.getMinecraft().currentScreen != null) { if (Minecraft.getMinecraft().currentScreen.getClass().getSimpleName().contains("InfoHud")) { - GL11.glPushMatrix(); - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(770, 771); Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(label.renderText, label.x + 2, label.y + 3, 0x60FFFFFF); - GL11.glDisable(GL11.GL_BLEND); - GL11.glPopMatrix(); } } if(Minecraft.getMinecraft().currentScreen instanceof InfoHud) { Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Leftclick to move", width-ypos, xpos- 30, 0x60FF00); - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Middleclick to enable", width-ypos, xpos-20, 0x60FF00); - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Rightclick to add black background", width-ypos, xpos-10, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Rightclick to enable", width-ypos, xpos-20, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Middleclick to add black background", width-ypos, xpos-10, 0x60FF00); Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Hold Shift to snap to grid", width-ypos, xpos, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("CTRL+Shift+R to reset the layout", width - ypos, xpos + 10, 0xEE8100); + + if (isCtrlKeyDown() && isShiftKeyDown() && TASmodClient.virtual.isKeyDown(Keyboard.KEY_R)) { + resetLayout = true; + configuration = null; + } } } ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); drawCenteredString(Minecraft.getMinecraft().fontRenderer, "TASmod is still in development! Major issues may arise!", scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 50, 0xFF8400); +// drawCenteredString(Minecraft.getMinecraft().fontRenderer, Float.toString(TASmod.tickratechanger.ticksPerSecond), scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 36, 0xFFFFFF); } /** * Renders a Box with Text in it */ private void drawRectWithText(String text, int x, int y, boolean rect) { - if (rect) drawRect(x, y, x + Minecraft.getMinecraft().fontRenderer.getStringWidth(text) + 4, y + 14, 0x80000000); + if (rect) drawRect(x, y, x + Minecraft.getMinecraft().fontRenderer.getStringWidth(text) + 4, y + 14, 0x60000000); Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(text, x + 2, y + 3, 0xFFFFFF); GL11.glEnable(3042 /*GL_BLEND*/); } @@ -482,4 +513,31 @@ private String keystrokes() { return ""; } + private Pair getScreenOffset(int x, int y, InfoLabel label){ + ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); + + int marginX = 5; + int marginY = 5; + + if (getBBRight(x, label.renderText) > scaled.getScaledWidth()) { + int offset = getBBRight(x, label.renderText); + x = x - (offset - scaled.getScaledWidth()) - marginX; + } + + if (getBBDown(y) > scaled.getScaledHeight()) { + int offset = getBBDown(y); + y = y - (offset - scaled.getScaledHeight()) - marginY; + } + + return Pair.of(x, y); + } + + private int getBBRight(int x, String text) { + return x + Minecraft.getMinecraft().fontRenderer.getStringWidth(text); + } + + private int getBBDown(int y) { + return y + 14; + } + } \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java index ce8325ae..8b03f695 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java @@ -2,31 +2,33 @@ import com.minecrafttas.common.events.EventClient.EventCamera; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController.TickInputContainer; -import com.minecrafttas.tasmod.playback.controlbytes.ControlByteHandler; +import com.minecrafttas.tasmod.playback.ControlByteHandler; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; import net.minecraft.client.Minecraft; import net.minecraft.util.math.MathHelper; /** * Adds interpolation to the camera + * * @author Pancake * */ -public class InterpolationHandler implements EventCamera{ - +public class InterpolationHandler implements EventCamera { + public static float rotationPitch = 0f; public static float rotationYaw = 0f; - + @Override public CameraData onCameraEvent(CameraData dataIn) { if (TASmodClient.virtual.getContainer().isPlayingback() && ControlByteHandler.shouldInterpolate) { TickInputContainer input = TASmodClient.virtual.getContainer().get(); - if (input == null) return dataIn; + if (input == null) + return dataIn; float nextPitch = input.getSubticks().getPitch(); float nextYaw = input.getSubticks().getYaw(); dataIn.pitch = (float) MathHelper.clampedLerp(rotationPitch, nextPitch, Minecraft.getMinecraft().timer.renderPartialTicks); - dataIn.yaw = (float) MathHelper.clampedLerp(rotationYaw, nextYaw+180, Minecraft.getMinecraft().timer.renderPartialTicks); + dataIn.yaw = (float) MathHelper.clampedLerp(rotationYaw, nextYaw + 180, Minecraft.getMinecraft().timer.renderPartialTicks); } else { dataIn.pitch = rotationPitch; dataIn.yaw = rotationYaw; diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java index 35ab6609..15e07110 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java @@ -1,32 +1,34 @@ package com.minecrafttas.tasmod.handlers; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + import com.minecrafttas.common.events.EventClient.EventClientGameLoop; import com.minecrafttas.common.events.EventClient.EventDoneLoadingWorld; import com.minecrafttas.common.events.EventClient.EventLaunchIntegratedServer; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.minecraft.client.Minecraft; /** * Handles logic during a loading screen to transition between states. + * * @author Scribble * */ -public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld{ - - +public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld { + private boolean waszero; private boolean isLoading; private int loadingScreenDelay = -1; @Override public void onLaunchIntegratedServer() { - TASmod.logger.debug(LoggerMarkers.Event, "Starting the integrated server"); - PlaybackController container = TASmodClient.virtual.getContainer(); - if(!container.isNothingPlaying() && !container.isPaused()) { + LOGGER.debug(LoggerMarkers.Event, "Starting the integrated server"); + PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + if (!container.isNothingPlaying() && !container.isPaused()) { container.pause(true); } if (TASmodClient.tickratechanger.ticksPerSecond == 0 || TASmodClient.tickratechanger.advanceTick) { @@ -39,10 +41,10 @@ public void onLaunchIntegratedServer() { public void onRunClientGameLoop(Minecraft mc) { if (loadingScreenDelay > -1) { if (loadingScreenDelay == 0) { - TASmod.logger.debug(LoggerMarkers.Event, "Finished loading screen on the client"); + LOGGER.debug(LoggerMarkers.Event, "Finished loading screen on the client"); TASmodClient.tickratechanger.joinServer(); if (!waszero) { - if(TASmod.getServerInstance()!=null) { //Check if a server is running and if it's an integrated server + if (TASmod.getServerInstance() != null) { // Check if a server is running and if it's an integrated server TASmodClient.tickratechanger.pauseClientGame(false); TASmod.tickratechanger.pauseServerGame(false); } @@ -54,15 +56,15 @@ public void onRunClientGameLoop(Minecraft mc) { loadingScreenDelay--; } } - + @Override public void onDoneLoadingWorld() { - if(TASmod.getServerInstance()!=null) { //Check if a server is running and if it's an integrated server - TASmod.logger.debug(LoggerMarkers.Event, "Finished loading the world on the client"); + if (TASmod.getServerInstance() != null) { // Check if a server is running and if it's an integrated server + LOGGER.debug(LoggerMarkers.Event, "Finished loading the world on the client"); loadingScreenDelay = 1; } } - + public boolean isLoading() { return isLoading; } diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGSeedPacket.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGSeedPacket.java deleted file mode 100644 index c8523c8c..00000000 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGSeedPacket.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.minecrafttas.tasmod.ktrng; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class KTRNGSeedPacket implements Packet { - - private long seed; - - public KTRNGSeedPacket() { - } - - public KTRNGSeedPacket(long seed) { - this.seed = seed; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if (side.isClient()) { - TASmod.ktrngHandler.setGlobalSeedClient(seed); - } else { - TASmod.ktrngHandler.setGlobalSeedServer(seed); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeLong(seed); - } - - @Override - public void deserialize(PacketBuffer buf) { - seed = buf.readLong(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGStartSeedPacket.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGStartSeedPacket.java deleted file mode 100644 index dce516f1..00000000 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGStartSeedPacket.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.minecrafttas.tasmod.ktrng; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class KTRNGStartSeedPacket implements Packet{ - - private long seed; - - /** - * Do not use! - */ - @Deprecated - public KTRNGStartSeedPacket() { - } - - /** - * Set's the start seed of the client - * @param seed - */ - public KTRNGStartSeedPacket(long seed) { - this.seed = seed; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - TASmodClient.virtual.getContainer().setStartSeed(seed); - } else { - TASmod.tickSchedulerServer.add(()->{ - TASmod.ktrngHandler.setGlobalSeedServer(seed); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeLong(seed); - } - - @Override - public void deserialize(PacketBuffer buf) { - seed = buf.readLong(); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java index a78a2a27..6b2cbb4b 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java @@ -1,12 +1,24 @@ package com.minecrafttas.tasmod.ktrng; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.nio.ByteBuffer; + import com.minecrafttas.common.events.EventClient.EventPlayerJoinedClientSide; import com.minecrafttas.common.events.EventServer.EventServerTick; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; import com.minecrafttas.killtherng.KillTheRNG; import com.minecrafttas.killtherng.SeedingModes; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -19,64 +31,65 @@ * @author Scribble * */ -public class KillTheRNGHandler implements EventServerTick, EventPlayerJoinedClientSide{ - +public class KillTheRNGHandler implements EventServerTick, EventPlayerJoinedClientSide, ClientPacketHandler, ServerPacketHandler { + private boolean isLoaded; - + /** * Instantiates a KillTheRNGHandler instance + * * @param isLoaded If the KillTheRNG mod is loaded */ public KillTheRNGHandler(boolean isLoaded) { - - this.isLoaded=isLoaded; - + + this.isLoaded = isLoaded; + if (isLoaded) { KillTheRNG.LOGGER.info("Connection established with TASmod"); - KillTheRNG.isLibrary=true; - KillTheRNG.mode=SeedingModes.TickChange; - + KillTheRNG.isLibrary = true; + KillTheRNG.mode = SeedingModes.TickChange; + KillTheRNG.annotations.register(new KTRNGMonitor()); - }else { - TASmod.logger.info("KillTheRNG doesn't appear to be loaded"); + } else { + LOGGER.info("KillTheRNG doesn't appear to be loaded"); } } - + public long advanceGlobalSeedServer() { - if(isLoaded()) { + if (isLoaded()) { return KillTheRNG.commonRandom.nextSeed(); } else { return 0; } } - + public long getGlobalSeedServer() { - if(isLoaded()) { + if (isLoaded()) { return KillTheRNG.commonRandom.GlobalServer.getSeed(); } else { return 0; } } - + public boolean isLoaded() { return isLoaded; } - - - //=================================================Setting the seed + + // =================================================Setting the seed /** * @return The global seed of the client */ @Environment(EnvType.CLIENT) public long getGlobalSeedClient() { - if(isLoaded()) + if (isLoaded()) return KillTheRNG.clientRandom.GlobalClient.getSeed(); else return 0; } - + /** * Set the global seed on the client + * * @param seedIn The seed on the client */ @Environment(EnvType.CLIENT) @@ -85,53 +98,71 @@ public void setGlobalSeedClient(long seedIn) { KillTheRNG.clientRandom.setSeedAll(seedIn); } } - - + public void setGlobalSeedServer(long seedIn) { if (isLoaded()) { KillTheRNG.commonRandom.setSeedAll(seedIn); } } + /** * Sends a packet to the server, setting the global seed + * * @param seedIn The seed on the server */ @Environment(EnvType.CLIENT) public void sendGlobalSeedToServer(long seedIn) { - if(isLoaded()) { - if(TASmodClient.packetClient!=null) - TASmodClient.packetClient.sendToServer(new KTRNGSeedPacket(seedIn)); + if (isLoaded()) { + if (TASmodClient.client != null) + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.KILLTHERNG_SEED).writeLong(seedIn)); + } catch (Exception e) { + e.printStackTrace(); + } else setGlobalSeedClient(seedIn); } } - //=================================================TASmod integration - + // =================================================TASmod integration + /** * Executed every tick on the server */ @Override public void onServerTick(MinecraftServer server) { - if(isLoaded()) { - if(TASmod.containerStateServer.getState() != TASstate.PAUSED) - TASmod.packetServer.sendToAll(new KTRNGSeedPacket(advanceGlobalSeedServer())); + if (isLoaded()) { + if (TASmod.playbackControllerServer.getState() != TASstate.PAUSED) + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.KILLTHERNG_SEED).writeLong(advanceGlobalSeedServer())); + } catch (Exception e) { + e.printStackTrace(); + } } } - - //================================================= Seedsync - + + // ================================================= Seedsync + public void broadcastStartSeed() { - if(isLoaded()) { + if (isLoaded()) { long seed = getGlobalSeedServer(); - TASmod.packetServer.sendToAll(new KTRNGStartSeedPacket(seed)); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.KILLTHERNG_SEED).writeLong(seed)); + } catch (Exception e) { + e.printStackTrace(); + } } } - + @Environment(EnvType.CLIENT) public void setInitialSeed(long initialSeed) { - if(TASmodClient.packetClient != null) { - TASmod.logger.info("Sending initial client seed: {}", initialSeed); - TASmodClient.packetClient.sendToServer(new KTRNGStartSeedPacket(initialSeed)); // TODO Every new player in multiplayer will currently send the initial seed, which is BAD + if (TASmodClient.client != null) { + LOGGER.info("Sending initial client seed: {}", initialSeed); + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.KILLTHERNG_STARTSEED).writeLong(initialSeed)); // TODO Every new player in multiplayer will currently send the initial seed, + // which is BAD + } catch (Exception e) { + e.printStackTrace(); + } } else { TASmod.ktrngHandler.setGlobalSeedClient(initialSeed); } @@ -142,6 +173,50 @@ public void setInitialSeed(long initialSeed) { public void onPlayerJoinedClientSide(EntityPlayerSP player) { setInitialSeed(getGlobalSeedClient()); } - - + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { TASmodPackets.KILLTHERNG_SEED, TASmodPackets.KILLTHERNG_STARTSEED }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + long seed = TASmodBufferBuilder.readLong(buf); + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case KILLTHERNG_SEED: + setGlobalSeedServer(seed); + break; + + case KILLTHERNG_STARTSEED: + TASmod.tickSchedulerServer.add(() -> { + TASmod.ktrngHandler.setGlobalSeedServer(seed); + }); + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.SERVER); + } + } + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + long seed = TASmodBufferBuilder.readLong(buf); + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case KILLTHERNG_SEED: + setGlobalSeedClient(seed); + break; + + case KILLTHERNG_STARTSEED: + TASmodClient.virtual.getContainer().setStartSeed(seed); + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); + } + } + } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java index d51e7a50..4401621c 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java @@ -14,10 +14,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.externalGui.InputContainerView; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler; -import com.minecrafttas.tasmod.savestates.server.playerloading.SavestatePlayerLoading; -import com.minecrafttas.tasmod.ticksync.TickSyncClient; +import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck; import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; @@ -76,7 +74,7 @@ public void redirectRunTick(Minecraft mc) { TASmodClient.tickratechanger.advanceTick = false; TASmodClient.tickratechanger.changeClientTickrate(0F); } - TickSyncClient.clientPostTick((Minecraft)(Object)this); + EventClientTickPost.fireOnClientPostTick((Minecraft)(Object)this); } @Shadow @@ -85,9 +83,9 @@ public void redirectRunTick(Minecraft mc) { @Inject(method = "shutdownMinecraftApplet", at = @At("HEAD")) public void inject_shutdownMinecraftApplet(CallbackInfo ci) { try { - if (TASmodClient.packetClient != null) { + if (TASmodClient.client != null) { TASmodClient.tickratechanger.changeTickrate(20); - TASmodClient.packetClient.killClient(); + TASmodClient.client.disconnect(); } } catch (Exception e) { e.printStackTrace(); @@ -98,13 +96,11 @@ public void inject_shutdownMinecraftApplet(CallbackInfo ci) { @Inject(method = "runTick", at = @At(value = "HEAD")) public void injectRunTick(CallbackInfo ci) throws IOException { - - InputContainerView.update(TASmodClient.virtual); - if (SavestatePlayerLoading.wasLoading) { - SavestatePlayerLoading.wasLoading = false; + if (SavestateHandlerServer.wasLoading) { + SavestateHandlerServer.wasLoading = false; if(Minecraft.getMinecraft().player!=null) { //The player can be null when loading a savestate and quitting to the main menu - SavestateHandler.playerLoadSavestateEventClient(); + SavestateHandlerServer.playerLoadSavestateEventClient(); } } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java index cbba1bf2..11b8e8ed 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java @@ -11,9 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.events.EventServer.EventCompleteLoadstate; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.ticksync.TickSyncServer; +import com.minecrafttas.tasmod.events.EventServer.EventServerTickPost; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -68,14 +66,9 @@ public void redirectThreadSleep(long msToTick) { /* The server should tick if: * (shouldTick in ticksync is true OR there are no players connected to the custom server) AND the tickrate is not zero. That or advance tick is true*/ - if( (TickSyncServer.shouldTick() && TASmod.tickratechanger.ticksPerSecond != 0) || TASmod.tickratechanger.advanceTick) { + if( (TASmod.ticksyncServer.shouldTick() && TASmod.tickratechanger.ticksPerSecond != 0) || TASmod.tickratechanger.advanceTick) { long timeBeforeTick = System.currentTimeMillis(); - if (TASmod.savestateHandler.state == SavestateState.WASLOADING) { - TASmod.savestateHandler.state = SavestateState.NONE; - EventCompleteLoadstate.fireLoadstateComplete(); - } - this.tick(); TASmod.tickSchedulerServer.runAllTasks(); @@ -83,7 +76,7 @@ public void redirectThreadSleep(long msToTick) { TASmod.tickratechanger.changeServerTickrate(0F); TASmod.tickratechanger.advanceTick = false; } - TickSyncServer.serverPostTick(); + EventServerTickPost.fireServerTickPost((MinecraftServer)(Object)this); long tickDuration = System.currentTimeMillis() - timeBeforeTick; diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinTimer.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinTimer.java index 1b1d3ac8..9462ad79 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinTimer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinTimer.java @@ -42,7 +42,7 @@ public class MixinTimer { @Inject(method = "updateTimer", at = @At("HEAD"), cancellable = true) public void inject_tick(CallbackInfo ci) { - if (Minecraft.getMinecraft().getConnection() != null) { + if (TASmodClient.client != null && !TASmodClient.client.isClosed() && TASmodClient.ticksyncClient.isEnabled()) { lastSyncSysClock = Minecraft.getSystemTime(); // update the tick tracker so that after returning to scheduling the client won't catch up all ticks (max 10) this.elapsedTicks = 0; // do not do any ticks long newGameLoop = Minecraft.getSystemTime(); diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiControls.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiControls.java deleted file mode 100644 index 4f6e496d..00000000 --- a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiControls.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.minecrafttas.tasmod.mixin.events; - -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.callback.CallbackInfo; - -import com.minecrafttas.tasmod.events.OpenGuiEvents; - -import net.minecraft.client.gui.GuiControls; -import net.minecraft.client.gui.GuiScreen; - -@Mixin(GuiControls.class) -public class MixinGuiControls extends GuiScreen{ - - @Inject(method = "initGui", at = @At("HEAD")) - public void inject_initGui(CallbackInfo ci) { - OpenGuiEvents.openGuiControls((GuiControls)(Object)this); - } - - @Override - public void onGuiClosed() { - OpenGuiEvents.closeGuiControls((GuiControls)(Object)this); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java index 9885bd96..1de0ce97 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java @@ -5,8 +5,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.minecrafttas.tasmod.savestates.server.motion.ClientMotionServer; -import com.minecrafttas.tasmod.savestates.server.motion.ClientMotionServer.Saver; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; @@ -17,7 +16,7 @@ public class MixinEntityPlayerMP { @Inject(method = "writeEntityToNBT", at = @At(value = "RETURN")) public void writeClientMotion(NBTTagCompound compound, CallbackInfo ci) { NBTTagCompound nbttagcompound = new NBTTagCompound(); - ClientMotionServer.Saver saver = ClientMotionServer.getMotion().get((EntityPlayerMP) (Object) this); + PlayerHandler.MotionData saver = PlayerHandler.getMotion().get((EntityPlayerMP) (Object) this); if (saver != null) { nbttagcompound.setDouble("x", saver.getClientX()); nbttagcompound.setDouble("y", saver.getClientY()); @@ -52,8 +51,8 @@ public void readClientMotion(NBTTagCompound compound, CallbackInfo ci) { boolean sprinting = nbttagcompound.getBoolean("Sprinting"); float jumpVector = nbttagcompound.getFloat("JumpFactor"); - ClientMotionServer.Saver saver = new Saver(clientmotionX, clientmotionY, clientmotionZ, clientmotionrX, clientmotionrY, clientmotionrZ, sprinting, jumpVector); - ClientMotionServer.getMotion().put((EntityPlayerMP) (Object) this, saver); + PlayerHandler.MotionData saver = new PlayerHandler.MotionData(clientmotionX, clientmotionY, clientmotionZ, clientmotionrX, clientmotionrY, clientmotionrZ, sprinting, jumpVector); + PlayerHandler.getMotion().put((EntityPlayerMP) (Object) this, saver); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java index c41a1568..00641961 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java @@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.NetHandlerPlayServer; @@ -16,6 +16,6 @@ public class MixinNetHandlerPlayServer { @Redirect(method = "processPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayerMP;isInvulnerableDimensionChange()Z")) public boolean redirect_processPlayer(EntityPlayerMP parentIn) { - return !parentIn.isInvulnerableDimensionChange() && (TASmod.savestateHandler.state!=SavestateState.LOADING && TASmod.savestateHandler.state!=SavestateState.WASLOADING); + return !parentIn.isInvulnerableDimensionChange() && TASmod.savestateHandlerServer.state!=SavestateState.LOADING; } } diff --git a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java b/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java index 2533146a..4baeebde 100644 --- a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java +++ b/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java @@ -8,7 +8,7 @@ import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.killtherng.custom.CustomRandom; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackController; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; @@ -27,13 +27,13 @@ public class DesyncMonitoring { private MonitorContainer currentValues; - private PlaybackController controller; + private PlaybackControllerClient controller; /** * Creates an empty desync monitor * @param playbackController */ - public DesyncMonitoring(PlaybackController playbackController) { + public DesyncMonitoring(PlaybackControllerClient playbackController) { controller = playbackController; } @@ -42,7 +42,7 @@ public DesyncMonitoring(PlaybackController playbackController) { * @param playbackController * @param monitorLines */ - public DesyncMonitoring(PlaybackController playbackController, List monitorLines) throws IOException{ + public DesyncMonitoring(PlaybackControllerClient playbackController, List monitorLines) throws IOException{ this(playbackController); container = loadFromFile(monitorLines); } diff --git a/src/main/java/com/minecrafttas/tasmod/networking/IdentificationPacket.java b/src/main/java/com/minecrafttas/tasmod/networking/IdentificationPacket.java deleted file mode 100644 index 3bcb79e5..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/IdentificationPacket.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -import java.util.UUID; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.ticksync.TickSyncServer; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Used to identify the socket. This enables the server to send packets to a specific player - * @author Scribble - */ -public class IdentificationPacket implements Packet { - - private UUID uuid; - - public IdentificationPacket() { - - } - - public IdentificationPacket(UUID uuid) { - this.uuid = uuid; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if (side.isServer()) { - TickSyncServer.onPacket(this.uuid); - } else { - TASmodClient.packetClient.setReady(); - } - } - - @Override - public void serialize(PacketBuffer buf) { - if (uuid != null) { - buf.writeUniqueId(uuid); - } - } - - @Override - public void deserialize(PacketBuffer buf) { - if (buf.capacity() > 0) { - this.uuid = buf.readUniqueId(); - } - } - - public UUID getUuid() { - return uuid; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/Packet.java b/src/main/java/com/minecrafttas/tasmod/networking/Packet.java deleted file mode 100644 index 776774f3..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/Packet.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * This is a Packet transmittable over the TASmod Custom Server. - * - * IMPLEMENTATION NOTICE: - * There are no clientbound or serverbound packets as they were deemed unnecessary. This means both the server and the client can transmit any packet. - * @author Pancake - */ -public interface Packet { - - /** - * Once a packet is being received it is immediately handled in this method. - * - * IMPLEMENTATION NOTICE: - * This process is non-blocking for the game and executed on the tasmod server thread temporarily blocking it. - */ - void handle(PacketSide side, EntityPlayer player); - - /** - * In order to transfer packets over the network connection they need to be serialized into a stream of bytes. - * @param buf Packet buffer to serialize to - * @return A serializable packet buffer - */ - void serialize(PacketBuffer buf); - - /** - * In order to receive packets over the network connection the other end serializes the packet into a stream of bytes. Therefore this end needs to deserialize the packet - * @param buf A deserializable packet buffer - */ - void deserialize(PacketBuffer buf); - -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/PacketSerializer.java b/src/main/java/com/minecrafttas/tasmod/networking/PacketSerializer.java deleted file mode 100644 index d901712c..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/PacketSerializer.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -import java.util.ArrayList; - -import com.minecrafttas.tasmod.TASmod; - -import io.netty.buffer.Unpooled; -import net.minecraft.network.PacketBuffer; - -/** - * This class helps serializing and deserializing packets - * @author Pancake - */ -public class PacketSerializer { - - private static ArrayList> REGISTRY = new ArrayList<>(); - - /** - * Deserialize a TASmod packet from a packet buffer. The packet class is prefixed with an id and read here. - * - * @param buf Serialized byte buffer with id prefix - * @return Deserialized packet - */ - public static Packet deserialize(PacketBuffer buf) { - // Read packet id and deserialize the correct packet - int packetId = buf.readInt(); - - Packet packet=null; - try { - packet = REGISTRY.get(packetId).newInstance(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - if(packet == null) { - TASmod.logger.warn("Unregistered packet received! Packet Id: " + packetId); - return null; - } - - packet.deserialize(buf); - return packet; - } - - /** - * Serialize a TASmod packet to a packet buffer. The packet class is read and a id prefixed packet buffer is returned - * - * @param packet Non-serialized packet - * @return Serialized packet buffer with id prefix - */ - public static PacketBuffer serialize(Packet packet) { - // Figure out packet class and prefix the correct id - - PacketBuffer buf = new PacketBuffer(Unpooled.buffer()); - int packetID = REGISTRY.indexOf(packet.getClass()); - - if(packetID == -1) { - TASmod.logger.warn("Unregistered packet was trying to be serialized! Packet Class: " + packet.getClass().getSimpleName()); - return null; - } - - buf.writeInt(packetID); - - packet.serialize(buf); - return buf; - } - - public static void registerPacket(Class packet) { - TASmod.logger.trace("Registering packet {}", packet.getClass().getSimpleName()); - if(REGISTRY.contains(packet)) { - TASmod.logger.warn("Trying to register packet which already exists: {}", packet.getClass().getSimpleName()); - } - REGISTRY.add(packet); - } - - public static void unregisterPacket(Class packet) { - if(REGISTRY.contains(packet)) { - TASmod.logger.warn("Trying to unregister packet which doesn't exist in the registry: {}", packet.getClass().getSimpleName()); - } - REGISTRY.remove(packet); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/PacketSide.java b/src/main/java/com/minecrafttas/tasmod/networking/PacketSide.java deleted file mode 100644 index 7b9c0e48..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/PacketSide.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -public enum PacketSide { - CLIENT, - SERVER; - - public boolean isClient() { - return this == CLIENT; - } - - public boolean isServer() { - return this == SERVER; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java new file mode 100644 index 00000000..e4f64e1e --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java @@ -0,0 +1,114 @@ +package com.minecrafttas.tasmod.networking; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.ByteBufferBuilder; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; +import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; + +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTSizeTracker; +import net.minecraft.nbt.NBTTagCompound; + +public class TASmodBufferBuilder extends ByteBufferBuilder{ + + public TASmodBufferBuilder(int id) { + super(id); + } + + public TASmodBufferBuilder(PacketID packet) { + super(packet); + } + + public TASmodBufferBuilder(ByteBuffer buf) { + super(buf); + } + + public TASmodBufferBuilder writeTASState(TASstate state) { + this.writeShort((short)state.ordinal()); + return this; + } + + public TASmodBufferBuilder writeNBTTagCompound(NBTTagCompound compound) { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + DataOutputStream dataout = new DataOutputStream(out); + + try { + CompressedStreamTools.write(compound, dataout); + } catch (IOException e) { + e.printStackTrace(); + } + + this.writeByteArray(out.toByteArray()); + + try { + out.close(); + dataout.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return this; + } + + public TASmodBufferBuilder writeTickratePauseState(TickratePauseState state) { + writeShort((short) state.ordinal()); + return this; + } + + public TASmodBufferBuilder writeMotionData(MotionData data) { + writeDouble(data.getClientX()); + writeDouble(data.getClientY()); + writeDouble(data.getClientZ()); + writeFloat(data.getClientrX()); + writeFloat(data.getClientrY()); + writeFloat(data.getClientrZ()); + writeFloat(data.getJumpMovementVector()); + writeBoolean(data.isSprinting()); + return this; + } + + public static TASstate readTASState(ByteBuffer buf) { + return TASstate.values()[buf.getShort()]; + } + + public static NBTTagCompound readNBTTagCompound(ByteBuffer buf) throws IOException { + ByteArrayInputStream input = new ByteArrayInputStream(readByteArray(buf)); + + DataInputStream datain = new DataInputStream(input); + + NBTTagCompound compound = CompressedStreamTools.read(datain, NBTSizeTracker.INFINITE); + + input.close(); + datain.close(); + + return compound; + } + + public static TickratePauseState readTickratePauseState(ByteBuffer buf) { + return TickratePauseState.values()[buf.getShort()]; + } + + public static MotionData readMotionData(ByteBuffer buf) { + double x = TASmodBufferBuilder.readDouble(buf); + double y = TASmodBufferBuilder.readDouble(buf); + double z = TASmodBufferBuilder.readDouble(buf); + float rx = TASmodBufferBuilder.readFloat(buf); + float ry = TASmodBufferBuilder.readFloat(buf); + float rz = TASmodBufferBuilder.readFloat(buf); + float jumpMovementVector = TASmodBufferBuilder.readFloat(buf); + boolean sprinting = TASmodBufferBuilder.readBoolean(buf); + + return new MotionData(x, y, z, rx, ry, rz, sprinting, jumpMovementVector); + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkClient.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkClient.java deleted file mode 100644 index 1fff85d7..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkClient.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.logging.log4j.Logger; - -import com.minecrafttas.tasmod.TASmodClient; - -import io.netty.buffer.Unpooled; -import net.minecraft.client.Minecraft; -import net.minecraft.network.PacketBuffer; - -public class TASmodNetworkClient { - - private Logger logger; - - private Thread clientThread; - - private Socket clientSocket; - - private BlockingQueue packetsToSend = new LinkedBlockingQueue<>(); - - private boolean ready = false; - - public TASmodNetworkClient(Logger logger) { - this(logger, "127.0.0.1", 3111); // Set ip for different server - } - - public TASmodNetworkClient(Logger logger, String serverIP, int port) { - this.logger = logger; - this.logger.info("Trying to connect to {}:{}", serverIP, port); - createClient(serverIP, port); - } - - public void sendToServer(Packet packet) { - if(clientThread == null) - return; - if(!clientThread.isAlive()) - return; - packetsToSend.add(packet); - } - - private void createClient(String serverIp, int port) { - - packetsToSend.add(new IdentificationPacket(Minecraft.getMinecraft().player.getUniqueID())); - - clientThread = new Thread(() -> { - try(Socket cSocket = new Socket()){ - cSocket.connect(new InetSocketAddress(serverIp, port)); - this.clientSocket = cSocket; - - clientSocket.setTcpNoDelay(true); - // Prepare the in and out streams. - DataInputStream inputStream = new DataInputStream(new BufferedInputStream(clientSocket.getInputStream())); - - createSendThread(); - - // Use the current thread to indefinitly fetch packets - Packet packet; - while (clientSocket.isConnected()) { - // Handle the next packet. If no packet is avilable, the readInt() call will hang until there is one. - int packetSize = inputStream.readInt(); - byte[] packetData = new byte[packetSize]; - inputStream.read(packetData, 0, packetSize); - PacketBuffer packetBuf = new PacketBuffer(Unpooled.wrappedBuffer(packetData)); - // Deserialize and run the packet - packet = PacketSerializer.deserialize(packetBuf); - packet.handle(PacketSide.CLIENT, Minecraft.getMinecraft().player); - logger.trace("Handled a " + packet.getClass().getSimpleName() + " from the socket."); - } - - } catch (EOFException | SocketException | InterruptedIOException exception) { - // The custom TASmod client was closed and the end of stream was reached. The socket was shut down properly. - logger.info("Custom TASmod client was shutdown"); - } catch (Exception exception) { - logger.error("Custom TASmod client was unexpectedly shutdown {}", exception); - exception.printStackTrace(); - TASmodClient.gameLoopSchedulerClient.add(()->{ - throw new RuntimeException("TASmod networking was shut down. The real errors are above"); - }); - } - }); - clientThread.setName("TASmod Network Client Accept"); - clientThread.setDaemon(true); - clientThread.start(); - } - - private void createSendThread() throws IOException { - DataOutputStream outputStream = new DataOutputStream(new BufferedOutputStream(clientSocket.getOutputStream())); - // Create a new thread that writes packets if available - Thread outputThread = new Thread(() -> { - try { - Packet packet; - while (!clientSocket.isClosed()) { - // Try to poll another packet that wants to be sent - packet = packetsToSend.peek(); - boolean skip = !(packet instanceof IdentificationPacket) && !ready; - if (packet == null || skip) { - Thread.sleep(1); // If nothing has to be done, let the cpu rest by waiting - continue; - } - packetsToSend.poll(); - // A packet was found: Serialize then send it. - byte[] packetData = PacketSerializer.serialize(packet).array(); - outputStream.writeInt(packetData.length); - outputStream.write(packetData); - outputStream.flush(); - logger.trace("Sent a " + packet.getClass().getSimpleName() + " to the socket."); - } - } catch (Exception e) { - e.printStackTrace(); - // This exception is already logged by the thread one layer above - // therefore nothing needs to be done here. - } - }); - outputThread.setDaemon(true); // If daemon is set, the jvm will quit without waiting for this thread to finish - outputThread.setName("TASmod Network Client Send"); - outputThread.start(); - } - - public void killClient() throws IOException { - if(clientThread != null && clientSocket != null) { - clientSocket.close(); - } - } - - public boolean isClosed() { - return clientSocket.isClosed(); - } - - public void setReady() { - ready = true; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkServer.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkServer.java deleted file mode 100644 index de3daebd..00000000 --- a/src/main/java/com/minecrafttas/tasmod/networking/TASmodNetworkServer.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.minecrafttas.tasmod.networking; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.logging.log4j.Logger; - -import com.minecrafttas.tasmod.TASmod; - -import io.netty.buffer.Unpooled; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; - -/** - * A custom packet server extending beyond the standard Minecraft network manager - * @author Pancake, Scribble - * - */ -public class TASmodNetworkServer { - - private Logger logger; - - private Thread serverThread; - - private ServerSocket serverSocket; - - private Map connectedPlayers = Collections.synchronizedMap(new HashMap()); - - private Map> queues = Collections.synchronizedMap(new HashMap<>()); - - private int connections = 0; - - public TASmodNetworkServer(Logger logger) throws IOException { - this(logger, 3111); - } - - public TASmodNetworkServer(Logger logger, int port) throws IOException { - this.logger = logger; - createServer(port); - } - - private void createServer(int port) throws IOException { - serverThread = new Thread(() -> { - - try(ServerSocket serverS = new ServerSocket(port)){ - this.serverSocket = serverS; - - while (!this.serverSocket.isClosed()) { - Socket socket = null; - - socket = serverSocket.accept(); - socket.setTcpNoDelay(true); - - final LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - - connections++; - createSendThread(socket, queue); - createAcceptThread(socket, queue); - } - - } catch (EOFException | SocketException | InterruptedIOException exception) { - logger.debug("Custom TASmod server was shutdown"); - } catch (Exception exception) { - logger.error("Custom TASmod server was unexpectedly shutdown {}", exception); - exception.printStackTrace(); - } - - }); - serverThread.setName("TASmod Network Server Main"); - serverThread.setDaemon(true); - serverThread.start(); - } - - private void createSendThread(Socket socket, LinkedBlockingQueue packetQueue) throws IOException, InterruptedException { - Thread sendThread = new Thread(()->{ - DataOutputStream outputStream; - try { - outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); - } catch (IOException e) { - e.printStackTrace(); - return; - } - Packet packet; - while (!socket.isClosed()) { - try { - // Try to poll another packet that wants to be sent - packet = packetQueue.poll(); - if (packet == null) { - Thread.sleep(1); // If nothing has to be done, let the cpu rest by waiting - continue; - } - // A packet was found: Serialize then send it. - byte[] packetData = PacketSerializer.serialize(packet).array(); - outputStream.writeInt(packetData.length); - outputStream.write(packetData); - outputStream.flush(); - logger.trace("Sent a " + packet.getClass().getSimpleName() + " to the socket."); - } catch(Exception e) { - logger.catching(e); - } - } - }); - sendThread.setDaemon(true); - sendThread.setName("TASmod Network Server Send #"+connections); - sendThread.start(); - } - - private void createAcceptThread(Socket socket, LinkedBlockingQueue packetQueue) throws IOException { - Thread acceptThread = new Thread(()->{ - DataInputStream inputStream; - try { - inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream())); - } catch (IOException e2) { - e2.printStackTrace(); - return; - } - Packet packet; - while (!socket.isClosed()) { - // Handle the next packet. If no packet is avilable, the readInt() call will - // hang until there is one. - try { - int packetSize = inputStream.readInt(); - byte[] packetData = new byte[packetSize]; - inputStream.read(packetData, 0, packetSize); - - PacketBuffer packetBuf = new PacketBuffer(Unpooled.wrappedBuffer(packetData)); - // Deserialize and run the packet - packet = PacketSerializer.deserialize(packetBuf); - - if(packet instanceof IdentificationPacket) { - handleIdentificationPacket(packet, socket, packetQueue); - } - - if(connectedPlayers.containsKey(socket)) { - UUID id = connectedPlayers.get(socket); - EntityPlayerMP player = TASmod.getServerInstance().getPlayerList().getPlayerByUUID(id); - - packet.handle(PacketSide.SERVER, player); - logger.trace("Handled a " + packet.getClass().getSimpleName() + " from the socket."); - } - }catch (EOFException e){ - logger.info("Client socket was shut down"); - try { - socket.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } catch (Exception e) { - logger.error(e.getMessage()); - e.printStackTrace(); - try { - socket.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - UUID id = connectedPlayers.get(socket); - connectedPlayers.remove(socket); - queues.remove(id); - connections--; - }); - acceptThread.setDaemon(true); - acceptThread.setName("TASmod Network Server Accept #"+connections); - acceptThread.start(); - } - - private void handleIdentificationPacket(Packet packet, Socket socket, LinkedBlockingQueue packetQueue) { - if(!connectedPlayers.containsKey(socket)) { - IdentificationPacket idPacket = (IdentificationPacket) packet; - logger.info("Identified player with uuid: {}", idPacket.getUuid()); - connectedPlayers.put(socket, idPacket.getUuid()); - queues.put(idPacket.getUuid(), packetQueue); - sendTo(new IdentificationPacket(), idPacket.getUuid()); - } - } - - public void sendToAll(Packet packet) { - if(serverThread.isAlive()) { - queues.forEach((id, queue) -> queue.add(packet)); - } - } - - public void sendTo(Packet packet, EntityPlayerMP... players) { - if(serverThread.isAlive()) { - queues.forEach((id, queue) -> { - for(EntityPlayerMP player : players) { - if(player.getUniqueID().equals(id)) { - queue.add(packet); - } - } - - }); - } - } - - public void sendTo(Packet packet, UUID... uuids) { - if(serverThread.isAlive()) { - queues.forEach((id, queue) -> { - for(UUID idToSend : uuids) { - if(idToSend.equals(id)) { - queue.add(packet); - } - } - }); - } - } - - public int getConnections() { - return connections; - } - - public void close() { - if(serverSocket==null) - return; - try { - serverSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - connections = 0; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java new file mode 100644 index 00000000..d62135e0 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java @@ -0,0 +1,243 @@ +package com.minecrafttas.tasmod.networking; + +import com.minecrafttas.common.events.CompactPacketHandler; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.tasmod.commands.CommandFolder; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.PlaybackSerialiser; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; +import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * PacketIDs and handlers specifically for TASmod + * + * @author Pancake, Scribble + */ +public enum TASmodPackets implements PacketID { + /** + *

Ticksync is a system to sync the tick execution between client and server. + * Both can tick independent from each other causing issues with playback. + *

This is used to notify the other to start ticking and shouldn't be used otherwise. + *

SIDE: Both
+ * ARGS: None + */ + TICKSYNC(false), + /** + *

Sets the tickrate/gamespeed + *

SIDE: Both
+ * ARGS: int tickrate + */ + TICKRATE_CHANGE, + /** + *

Sets the tickrate to 0, pausing the game. Also unpauses the game + *

SIDE: Both
+ * ARGS: {@link TickratePauseState} state The paused state + */ + TICKRATE_ZERO, + /** + *

While in tickrate 0, advances the game by one tick + *

SIDE: Both
+ * ARGS: None + */ + TICKRATE_ADVANCE, + /** + *

Creates a savestate + *

SIDE: Both
+ * ARGS:
+ * Client->Server: int The index of the savestate that should be created. -1 to create the latest savestate, might overwrite existing savestates.
+ * Server->Client: String The name of the savestate that is created for the clientside + */ + SAVESTATE_SAVE, + /** + *

Loads a savestate + *

SIDE: Both
+ * ARGS:
+ * Client->Server int The index of the savestate that should be loaded
+ * Server->Client String The name of the savestate that is loaded for the clientside + */ + SAVESTATE_LOAD, + /** + *

Opens or closes the savestate screen on the client + *

SIDE: Client
+ * ARGS: none + */ + SAVESTATE_SCREEN, + /** + *

Sends the playerdata of the player to the client, inluding the motion + *

SIDE: Client
+ * ARGS: {@link NBTTagCompound} compound The playerdata + */ + SAVESTATE_PLAYER, + /** + *

Used for storing the client motion data on the server. + *

SIDE: BOTH
+ * ARGS:
+ * Server->ClientNone
+ * Client->Server {@link MotionData} motionData An Object containing all necessary motion data
+ */ + SAVESTATE_REQUEST_MOTION, + /** + *

Unloads the chunks on the client side + *

SIDE: Client
+ * ARGS: none + */ + SAVESTATE_UNLOAD_CHUNKS, + /** + *

Notifies the client to clear all inputs from the input buffer in {@link PlaybackControllerClient} + *

SIDE: Both
+ * ARGS: none + */ + PLAYBACK_CLEAR_INPUTS, + /** + *

Notifies the client to quit to the main menu and start recording inputs in {@link PlaybackControllerClient} + *

SIDE: Both
+ * ARGS: none + */ + PLAYBACK_FULLRECORD, + /** + *

Notifies the client to quit to the main menu and start playing back inputs in {@link PlaybackControllerClient} + *

SIDE: Both
+ * ARGS: none + */ + PLAYBACK_FULLPLAY, + /** + *

Notifies the client to quit the game. Upon restarting the game, the specified tasfile will be loaded and played back in {@link PlaybackControllerClient} + *

SIDE: Both
+ * ARGS:
+ * Client->Server None
+ * Server->Client String filename The TASfile name to load on restart + */ + PLAYBACK_RESTARTANDPLAY, + /** + *

Notifies the client to store the current inputs to a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser} + *

SIDE: Both
+ * ARGS:
+ * Client->Server None
+ * Server->Client String filename The TASfile name to store + */ + PLAYBACK_SAVE, + /** + *

Notifies the client to load the inputs from a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser} + *

SIDE: Both
+ * ARGS:
+ * Client->Server None
+ * Server->Client String filename The TASfile name to load + */ + PLAYBACK_LOAD, + /** + *

Notifies the client activate "playuntil" in {@link PlaybackControllerClient}. The next playback will stop at the specified tick and the client will enter recording mode + *

SIDE: Both
+ * ARGS:
+ * Client->Server None
+ * Server->Client int tick The tick when to stop playing back and start recording + */ + PLAYBACK_PLAYUNTIL, + /** + *

A permissionless teleport packet, used for setting the playerdata on the server. Used for teleporting the player back to the start of the TAS when using /play + *

SIDE: Server
+ * ARGS:
+ * double x The x value
+ * double y etc...
+ * double z
+ * float angleYaw
+ * float anglePitch
+ */ + PLAYBACK_TELEPORT, + /** + *

Notifies the players to change the {@link TASstate} + *

SIDE: Both
+ * ARGS:
+ * Client->Server {@link TASstate} state The new state everyone should adapt + * Server->Client + * {@link TASstate} state The new state everyone should adapt
+ * boolean verbose If a chat message should be printed + */ + PLAYBACK_STATE, + /** + *

Opens a TASmod related folder on the file system + *

The action describes which folder to open: + *

    + *
  1. Savestate-Folder
  2. + *
  3. TASFiles-Folder
  4. + *
+ * + *

Side: CLIENT
+ * ARGS: short action + */ + OPEN_FOLDER(Side.CLIENT, (buf, clientID) -> { + short action = buf.getShort(); + switch (action) { + case 0: + CommandFolder.openSavestates(); + break; + case 1: + CommandFolder.openTASFolder(); + default: + break; + } + }), + /** + *

Sets the KillTheRNG seed + *

SIDE: Both
+ * ARGS:
+ * long seed The new KillTheRNG seed + */ + KILLTHERNG_SEED, + /** + *

Sets the KillTheRNG start seed when starting recording + *

SIDE: Both
+ * ARGS:
+ * long seed The new KillTheRNG seed + */ + KILLTHERNG_STARTSEED; + + private Side side; + private CompactPacketHandler lambda; + private boolean shouldTrace = true; + + private TASmodPackets() { + } + + private TASmodPackets(boolean shouldTrace) { + this.shouldTrace = shouldTrace; + } + + private TASmodPackets(Side side, CompactPacketHandler lambda) { + this(side, lambda, true); + } + + private TASmodPackets(Side side, CompactPacketHandler lambda, boolean shouldTrace) { + this.side = side; + this.lambda = lambda; + this.shouldTrace = shouldTrace; + } + + @Override + public int getID() { + return this.ordinal(); + } + + @Override + public CompactPacketHandler getLambda() { + return this.lambda; + } + + @Override + public Side getSide() { + return this.side; + } + + @Override + public String getName() { + return this.name(); + } + + @Override + public boolean shouldTrace() { + return shouldTrace; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/controlbytes/ControlByteHandler.java b/src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java similarity index 97% rename from src/main/java/com/minecrafttas/tasmod/playback/controlbytes/ControlByteHandler.java rename to src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java index 055caa9b..c3bfaed2 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/controlbytes/ControlByteHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.controlbytes; +package com.minecrafttas.tasmod.playback; import java.util.List; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackController.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java similarity index 52% rename from src/main/java/com/minecrafttas/tasmod/playback/PlaybackController.java rename to src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 84320e27..c00a5aa3 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackController.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -1,7 +1,20 @@ package com.minecrafttas.tasmod.playback; +import static com.minecrafttas.tasmod.TASmod.LOGGER; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_CLEAR_INPUTS; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLPLAY; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLRECORD; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_LOAD; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_PLAYUNTIL; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_RESTARTANDPLAY; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_SAVE; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_TELEPORT; +import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_STATE; + import java.io.File; +import java.io.IOException; import java.io.Serializable; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,15 +22,20 @@ import org.lwjgl.opengl.Display; import com.dselent.bigarraylist.BigArrayList; -import com.minecrafttas.common.events.EventClient.EventOpenGui; +import com.minecrafttas.common.Configuration.ConfigOptions; +import com.minecrafttas.common.server.ByteBufferBuilder; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.playback.controlbytes.ControlByteHandler; -import com.minecrafttas.tasmod.playback.server.TASstateClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.util.Scheduler.Task; import com.minecrafttas.tasmod.virtual.VirtualInput; import com.minecrafttas.tasmod.virtual.VirtualKeyboard; import com.minecrafttas.tasmod.virtual.VirtualMouse; @@ -28,10 +46,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; +import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; @@ -45,15 +60,15 @@ * These inputs can be played back at any time by setting * {@linkplain #setPlayback(boolean)} to true.
*
- * Information about the author etc. get stored in the playback controller too and - * will be printed out in chat when the player loads into a world
+ * Information about the author etc. get stored in the playback controller too + * and will be printed out in chat when the player loads into a world
* Inputs are saved and loaded to/from file via the * {@linkplain PlaybackSerialiser} * * @author Scribble * */ -public class PlaybackController implements EventOpenGui{ +public class PlaybackControllerClient implements ClientPacketHandler { /** * The current state of the controller. @@ -68,7 +83,7 @@ public class PlaybackController implements EventOpenGui{ * The current index of the inputs */ private int index; - + private VirtualKeyboard keyboard = new VirtualKeyboard(); private VirtualMouse mouse = new VirtualMouse(); @@ -83,41 +98,60 @@ public class PlaybackController implements EventOpenGui{ private BigArrayList inputs = new BigArrayList(directory + File.separator + "temp"); /** - * A map of control bytes. Used to change settings during playback via the playback file. + * A map of control bytes. Used to change settings during playback via the + * playback file. *

* A full list of changes can be found in {@link ControlByteHandler} *

- * The values are as follows:

+ * The values are as follows: + *

* Map(int playbackLine, List(Pair(String controlCommand, String[] arguments))" */ private Map>> controlBytes = new HashMap>>(); - + /** * The comments in the file, used to store them again later */ private Map> comments = new HashMap<>(); - + public DesyncMonitoring desyncMonitor = new DesyncMonitoring(this); - + // ===================================================================================================== private String title = "Insert TAS category here"; - + private String authors = "Insert author here"; private String playtime = "00:00.0"; - + private int rerecords = 0; private String startLocation = ""; - + private long startSeed = TASmod.ktrngHandler.getGlobalSeedClient(); // ===================================================================================================== - private boolean creditsPrinted=false; + private boolean creditsPrinted = false; private Integer playUntil = null; + + /** + * Sets the current {@link TASstate} + * + * First sends the state to the server. + * + * To set the client state, see {@link #setTASStateClient(TASstate)} + * + * @param stateIn The new state for all players + */ + public void setTASState(TASstate stateIn) { + try { + TASmodClient.client.send(new TASmodBufferBuilder(PLAYBACK_STATE).writeTASState(stateIn)); + } catch (Exception e) { + e.printStackTrace(); + } + } /** * Starts or stops a recording/playback @@ -125,8 +159,8 @@ public class PlaybackController implements EventOpenGui{ * @param stateIn stateIn The desired state of the container * @return */ - public String setTASState(TASstate stateIn) { - return setTASState(stateIn, true); + public String setTASStateClient(TASstate stateIn) { + return setTASStateClient(stateIn, true); } /** @@ -136,111 +170,111 @@ public String setTASState(TASstate stateIn) { * @param verbose Whether the output should be printed in the chat * @return The message printed in the chat */ - public String setTASState(TASstate stateIn, boolean verbose) { - ControlByteHandler.reset(); + public String setTASStateClient(TASstate stateIn, boolean verbose) { + ControlByteHandler.reset(); // FIXME Controlbytes are resetting when loading a world, due to "Paused" state being active during loading... Fix Paused state shenanigans? if (state == stateIn) { switch (stateIn) { - case PLAYBACK: - return verbose ? TextFormatting.RED + "A playback is already running" : ""; - case RECORDING: - return verbose ? TextFormatting.RED + "A recording is already running" : ""; - case PAUSED: - return verbose ? TextFormatting.RED + "The game is already paused" : ""; - case NONE: - return verbose ? TextFormatting.RED + "Nothing is running" : ""; + case PLAYBACK: + return verbose ? TextFormatting.RED + "A playback is already running" : ""; + case RECORDING: + return verbose ? TextFormatting.RED + "A recording is already running" : ""; + case PAUSED: + return verbose ? TextFormatting.RED + "The game is already paused" : ""; + case NONE: + return verbose ? TextFormatting.RED + "Nothing is running" : ""; } } else if (state == TASstate.NONE) { // If the container is currently doing nothing switch (stateIn) { - case PLAYBACK: - TASmod.logger.debug(LoggerMarkers.Playback, "Starting playback"); - if (Minecraft.getMinecraft().player != null && !startLocation.isEmpty()) { - try { - tpPlayer(startLocation); - } catch (NumberFormatException e) { - state = TASstate.NONE; - e.printStackTrace(); - return verbose ? TextFormatting.RED + "An error occured while reading the start location of the TAS. The file might be broken" : ""; + case PLAYBACK: + LOGGER.debug(LoggerMarkers.Playback, "Starting playback"); + if (Minecraft.getMinecraft().player != null && !startLocation.isEmpty()) { + try { + tpPlayer(startLocation); + } catch (NumberFormatException e) { + state = TASstate.NONE; + e.printStackTrace(); + return verbose ? TextFormatting.RED + "An error occured while reading the start location of the TAS. The file might be broken" : ""; + } } - } - Minecraft.getMinecraft().gameSettings.chatLinks = false; // #119 - index = 0; - state = TASstate.PLAYBACK; - creditsPrinted=false; - TASmod.ktrngHandler.setInitialSeed(startSeed); - return verbose ? TextFormatting.GREEN + "Starting playback" : ""; - case RECORDING: - TASmod.logger.debug(LoggerMarkers.Playback, "Starting recording"); - if (Minecraft.getMinecraft().player != null && startLocation.isEmpty()) { - startLocation = getStartLocation(Minecraft.getMinecraft().player); - } - if(this.inputs.isEmpty()) { - inputs.add(new TickInputContainer(index)); - desyncMonitor.recordNull(index); - } - state = TASstate.RECORDING; - return verbose ? TextFormatting.GREEN + "Starting a recording" : ""; - case PAUSED: - return verbose ? TextFormatting.RED + "Can't pause anything because nothing is running" : ""; - case NONE: - return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: None)"; + Minecraft.getMinecraft().gameSettings.chatLinks = false; // #119 + index = 0; + state = TASstate.PLAYBACK; + creditsPrinted = false; + TASmod.ktrngHandler.setInitialSeed(startSeed); + return verbose ? TextFormatting.GREEN + "Starting playback" : ""; + case RECORDING: + LOGGER.debug(LoggerMarkers.Playback, "Starting recording"); + if (Minecraft.getMinecraft().player != null && startLocation.isEmpty()) { + startLocation = getStartLocation(Minecraft.getMinecraft().player); + } + if (this.inputs.isEmpty()) { + inputs.add(new TickInputContainer(index)); + desyncMonitor.recordNull(index); + } + state = TASstate.RECORDING; + return verbose ? TextFormatting.GREEN + "Starting a recording" : ""; + case PAUSED: + return verbose ? TextFormatting.RED + "Can't pause anything because nothing is running" : ""; + case NONE: + return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: None)"; } } else if (state == TASstate.RECORDING) { // If the container is currently recording switch (stateIn) { - case PLAYBACK: - return verbose ? TextFormatting.RED + "A recording is currently running. Please stop the recording first before starting a playback" : ""; - case RECORDING: - return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Recording)"; - case PAUSED: - TASmod.logger.debug(LoggerMarkers.Playback, "Pausing a recording"); - state = TASstate.PAUSED; - tempPause = TASstate.RECORDING; - return verbose ? TextFormatting.GREEN + "Pausing a recording" : ""; - case NONE: - TASmod.logger.debug(LoggerMarkers.Playback, "Stopping a recording"); - TASmodClient.virtual.unpressEverything(); - state = TASstate.NONE; - return verbose ? TextFormatting.GREEN + "Stopping the recording" : ""; + case PLAYBACK: + return verbose ? TextFormatting.RED + "A recording is currently running. Please stop the recording first before starting a playback" : ""; + case RECORDING: + return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Recording)"; + case PAUSED: + LOGGER.debug(LoggerMarkers.Playback, "Pausing a recording"); + state = TASstate.PAUSED; + tempPause = TASstate.RECORDING; + return verbose ? TextFormatting.GREEN + "Pausing a recording" : ""; + case NONE: + LOGGER.debug(LoggerMarkers.Playback, "Stopping a recording"); + TASmodClient.virtual.unpressEverything(); + state = TASstate.NONE; + return verbose ? TextFormatting.GREEN + "Stopping the recording" : ""; } } else if (state == TASstate.PLAYBACK) { // If the container is currently playing back switch (stateIn) { - case PLAYBACK: - return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Playback)"; - case RECORDING: - return verbose ? TextFormatting.RED + "A playback is currently running. Please stop the playback first before starting a recording" : ""; - case PAUSED: - TASmod.logger.debug(LoggerMarkers.Playback, "Pausing a playback"); - state = TASstate.PAUSED; - tempPause = TASstate.PLAYBACK; - TASmodClient.virtual.unpressEverything(); - return verbose ? TextFormatting.GREEN + "Pausing a playback" : ""; - case NONE: - TASmod.logger.debug(LoggerMarkers.Playback, "Stopping a playback"); - Minecraft.getMinecraft().gameSettings.chatLinks = true; - TASmodClient.virtual.unpressEverything(); - state = TASstate.NONE; - return verbose ? TextFormatting.GREEN + "Stopping the playback" : ""; + case PLAYBACK: + return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Playback)"; + case RECORDING: + return verbose ? TextFormatting.RED + "A playback is currently running. Please stop the playback first before starting a recording" : ""; + case PAUSED: + LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback"); + state = TASstate.PAUSED; + tempPause = TASstate.PLAYBACK; + TASmodClient.virtual.unpressEverything(); + return verbose ? TextFormatting.GREEN + "Pausing a playback" : ""; + case NONE: + LOGGER.debug(LoggerMarkers.Playback, "Stopping a playback"); + Minecraft.getMinecraft().gameSettings.chatLinks = true; + TASmodClient.virtual.unpressEverything(); + state = TASstate.NONE; + return verbose ? TextFormatting.GREEN + "Stopping the playback" : ""; } } else if (state == TASstate.PAUSED) { switch (stateIn) { - case PLAYBACK: - TASmod.logger.debug(LoggerMarkers.Playback, "Resuming a playback"); - state=TASstate.PLAYBACK; - tempPause=TASstate.NONE; - return verbose ? TextFormatting.GREEN + "Resuming a playback" : ""; - case RECORDING: - TASmod.logger.debug(LoggerMarkers.Playback, "Resuming a recording"); - state=TASstate.RECORDING; - tempPause=TASstate.NONE; - return verbose ? TextFormatting.GREEN + "Resuming a recording" : ""; - case PAUSED: - return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Paused)"; - case NONE: - TASmod.logger.debug(LoggerMarkers.Playback, "Aborting pausing"); - state=TASstate.NONE; - TASstate statey=tempPause; - tempPause=TASstate.NONE; - return TextFormatting.GREEN + "Aborting a "+statey.toString().toLowerCase()+" that was paused"; + case PLAYBACK: + LOGGER.debug(LoggerMarkers.Playback, "Resuming a playback"); + state = TASstate.PLAYBACK; + tempPause = TASstate.NONE; + return verbose ? TextFormatting.GREEN + "Resuming a playback" : ""; + case RECORDING: + LOGGER.debug(LoggerMarkers.Playback, "Resuming a recording"); + state = TASstate.RECORDING; + tempPause = TASstate.NONE; + return verbose ? TextFormatting.GREEN + "Resuming a recording" : ""; + case PAUSED: + return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Paused)"; + case NONE: + LOGGER.debug(LoggerMarkers.Playback, "Aborting pausing"); + state = TASstate.NONE; + TASstate statey = tempPause; + tempPause = TASstate.NONE; + return TextFormatting.GREEN + "Aborting a " + statey.toString().toLowerCase() + " that was paused"; } } return "Something went wrong ._."; @@ -248,34 +282,36 @@ public String setTASState(TASstate stateIn, boolean verbose) { /** * Switches between the paused state and the state it was in before the pause + * * @return The new state */ public TASstate togglePause() { - if(state!=TASstate.PAUSED) { - setTASState(TASstate.PAUSED); - }else { - setTASState(tempPause); + if (state != TASstate.PAUSED) { + setTASStateClient(TASstate.PAUSED); + } else { + setTASStateClient(tempPause); } return state; } /** * Forces the playback to pause or unpause + * * @param pause True, if it should be paused */ public void pause(boolean pause) { - TASmod.logger.trace(LoggerMarkers.Playback, "Pausing {}", pause); - if(pause) { - if(state!=TASstate.NONE) { - setTASState(TASstate.PAUSED, false); + LOGGER.trace(LoggerMarkers.Playback, "Pausing {}", pause); + if (pause) { + if (state != TASstate.NONE) { + setTASStateClient(TASstate.PAUSED, false); } - }else { - if(state == TASstate.PAUSED) { - setTASState(tempPause, false); + } else { + if (state == TASstate.PAUSED) { + setTASStateClient(tempPause, false); } } } - + public boolean isPlayingback() { return state == TASstate.PLAYBACK; } @@ -283,7 +319,7 @@ public boolean isPlayingback() { public boolean isRecording() { return state == TASstate.RECORDING; } - + public boolean isPaused() { return state == TASstate.PAUSED; } @@ -368,30 +404,30 @@ public VirtualSubticks addSubticksToContainer(VirtualSubticks subticks) { * the next inputs */ public void nextTick() { - /*Stop the playback while player is still loading*/ - EntityPlayerSP player=Minecraft.getMinecraft().player; - - if(player!=null && player.addedToChunk) { - if(isPaused() && tempPause != TASstate.NONE) { - TASstateClient.setOrSend(tempPause); // The recording is paused in LoadWorldEvents#startLaunchServer + /* Stop the playback while player is still loading */ + EntityPlayerSP player = Minecraft.getMinecraft().player; + + if (player != null && player.addedToChunk) { + if (isPaused() && tempPause != TASstate.NONE) { + setTASState(tempPause); // The recording is paused in LoadWorldEvents#startLaunchServer pause(false); printCredits(); } } - - /*Tick the next playback or recording*/ + + /* Tick the next playback or recording */ if (state == TASstate.RECORDING) { recordNextTick(); } else if (state == TASstate.PLAYBACK) { playbackNextTick(); } } - + private void recordNextTick() { index++; - if(inputs.size()<=index) { - if(inputs.size()= index; i--) { + setTASState(TASstate.NONE); + for (long i = inputs.size() - 1; i >= index; i--) { inputs.remove(i); } index--; - TASstateClient.setOrSend(TASstate.RECORDING); + setTASState(TASstate.RECORDING); return; } - - /*Stop condition*/ + + /* Stop condition */ if (index == inputs.size()) { unpressContainer(); - TASstateClient.setOrSend(TASstate.NONE); + setTASState(TASstate.NONE); } - /*Continue condition*/ + /* Continue condition */ else { - TickInputContainer tickcontainer = inputs.get(index); //Loads the new inputs from the container + TickInputContainer tickcontainer = inputs.get(index); // Loads the new inputs from the container this.keyboard = tickcontainer.getKeyboard().clone(); this.mouse = tickcontainer.getMouse().clone(); this.subticks = tickcontainer.getSubticks().clone(); @@ -460,13 +497,13 @@ public BigArrayList getInputs() { public Map>> getControlBytes() { return controlBytes; } - + public Map> getComments() { return comments; } - - public void setIndex(int index) throws IndexOutOfBoundsException{ - if(index<=size()) { + + public void setIndex(int index) throws IndexOutOfBoundsException { + if (index <= size()) { this.index = index; if (state == TASstate.PLAYBACK) { TickInputContainer tickcontainer = inputs.get(index); @@ -474,7 +511,7 @@ public void setIndex(int index) throws IndexOutOfBoundsException{ this.mouse = tickcontainer.getMouse(); this.subticks = tickcontainer.getSubticks(); } - }else { + } else { throw new IndexOutOfBoundsException("Index is bigger than the container"); } } @@ -488,7 +525,7 @@ public TickInputContainer get(int index) { } return tickcontainer; } - + /** * @return The {@link TickInputContainer} at the current index */ @@ -497,18 +534,18 @@ public TickInputContainer get() { } public void clear() { - TASmod.logger.debug(LoggerMarkers.Playback, "Clearing playback controller"); + LOGGER.debug(LoggerMarkers.Playback, "Clearing playback controller"); inputs = new BigArrayList(directory + File.separator + "temp"); controlBytes.clear(); comments.clear(); index = 0; - startLocation=""; + startLocation = ""; desyncMonitor.clear(); clearCredits(); } - + private void clearCredits() { - title="Insert Author here"; + title = "Insert Author here"; authors = "Insert author here"; playtime = "00:00.0"; rerecords = 0; @@ -573,7 +610,7 @@ public void fixTicks() { inputs.get(i).setTick(i + 1); } } - + public long getStartSeed() { return startSeed; } @@ -598,7 +635,7 @@ public String getStartLocation() { * @param startLocation The start location of the TAS */ public void setStartLocation(String startLocation) { - TASmod.logger.debug(LoggerMarkers.Playback, "Setting start location"); + LOGGER.debug(LoggerMarkers.Playback, "Setting start location"); this.startLocation = startLocation; } @@ -609,7 +646,7 @@ public void setStartLocation(String startLocation) { * @return The start location from the player */ private String getStartLocation(EntityPlayerSP player) { - TASmod.logger.debug(LoggerMarkers.Playback, "Retrieving player start location"); + LOGGER.debug(LoggerMarkers.Playback, "Retrieving player start location"); String pos = player.posX + "," + player.posY + "," + player.posZ; String pitch = Float.toString(player.rotationPitch); String yaw = Float.toString(player.rotationYaw); @@ -624,7 +661,7 @@ private String getStartLocation(EntityPlayerSP player) { * @throws NumberFormatException If the location can't be parsed */ private void tpPlayer(String startLocation) throws NumberFormatException { - TASmod.logger.debug(LoggerMarkers.Playback, "Teleporting the player to the start location"); + LOGGER.debug(LoggerMarkers.Playback, "Teleporting the player to the start location"); String[] section = startLocation.split(","); double x = Double.parseDouble(section[0]); double y = Double.parseDouble(section[1]); @@ -633,87 +670,30 @@ private void tpPlayer(String startLocation) throws NumberFormatException { float angleYaw = Float.parseFloat(section[3]); float anglePitch = Float.parseFloat(section[4]); - TASmodClient.packetClient.sendToServer(new TeleportPlayerPacket(x, y, z, angleYaw, anglePitch)); - } - - /** - * Permissionless player teleporting packet - * - * @author Scribble - * - */ - public static class TeleportPlayerPacket implements Packet { - - double x; - double y; - double z; - - float angleYaw; - float anglePitch; - - public TeleportPlayerPacket(double x, double y, double z, float angleYaw, float anglePitch) { - this.x = x; - this.y = y; - this.z = z; - this.angleYaw = angleYaw; - this.anglePitch = anglePitch; - } - - public TeleportPlayerPacket() { - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if (side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP) playerz; - player.getServerWorld().addScheduledTask(() -> { - player.rotationPitch = anglePitch; - player.rotationYaw = angleYaw; - - player.setPositionAndUpdate(x, y, z); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - - buf.writeFloat(angleYaw); - buf.writeFloat(anglePitch); - } - - @Override - public void deserialize(PacketBuffer buf) { - this.x = buf.readDouble(); - this.y = buf.readDouble(); - this.z = buf.readDouble(); - this.angleYaw = buf.readFloat(); - this.anglePitch = buf.readFloat(); + try { + TASmodClient.client.send(new TASmodBufferBuilder(PLAYBACK_TELEPORT).writeDouble(x).writeDouble(y).writeDouble(z).writeFloat(angleYaw).writeFloat(anglePitch)); + } catch (Exception e) { + e.printStackTrace(); } - } - // ============================================================== /** * Clears {@link #keyboard} and {@link #mouse} */ public void unpressContainer() { - TASmod.logger.trace(LoggerMarkers.Playback, "Unpressing container"); + LOGGER.trace(LoggerMarkers.Playback, "Unpressing container"); keyboard.clear(); mouse.clear(); } - + // ============================================================== public void printCredits() { - TASmod.logger.trace(LoggerMarkers.Playback, "Printing credits"); - if (state == TASstate.PLAYBACK&&!creditsPrinted) { - creditsPrinted=true; + LOGGER.trace(LoggerMarkers.Playback, "Printing credits"); + if (state == TASstate.PLAYBACK && !creditsPrinted) { + creditsPrinted = true; printMessage(title, ChatFormatting.GOLD); printMessage("", null); printMessage("by " + authors, ChatFormatting.AQUA); @@ -723,23 +703,24 @@ public void printCredits() { printMessage("Rerecords: " + rerecords, null); } } - + private void printMessage(String msg, ChatFormatting format) { - String formatString=""; - if(format!=null) - formatString=format.toString(); - + String formatString = ""; + if (format != null) + formatString = format.toString(); + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentString(formatString + msg)); } public void setPlayUntil(int until) { - this.playUntil = until; + this.playUntil = until; } - + // ============================================================== - + /** * Storage class which stores the keyboard, mouse and subticks of a given tick. + * * @author Scribble * */ @@ -793,87 +774,194 @@ public int getTick() { public void setTick(int tick) { this.tick = tick; } - + @Override public TickInputContainer clone() { return new TickInputContainer(tick, keyboard, mouse, subticks); } } - + /** * State of the input recorder + * * @author Scribble * */ public static enum TASstate { /** - * The game records inputs to the {@link InputContainer}. + * The game is neither recording, playing back or paused, is also set when + * aborting all mentioned states. */ - RECORDING, + NONE, /** - * The game plays back the inputs loaded in {@link InputContainer} and locks user interaction. + * The game plays back the inputs loaded in {@link InputContainer} and locks + * user interaction. */ PLAYBACK, /** - * The playback or recording is paused and may be resumed. Note that the game isn't paused, only the playback. Useful for debugging things. + * The game records inputs to the {@link InputContainer}. */ - PAUSED, // #124 + RECORDING, /** - * The game is neither recording, playing back or paused, is also set when aborting all mentioned states. + * The playback or recording is paused and may be resumed. Note that the game + * isn't paused, only the playback. Useful for debugging things. */ - NONE; - - public int getIndex() { - switch(this) { - case NONE: - return 0; - case PLAYBACK: - return 1; - case RECORDING: - return 2; - case PAUSED: - return 3; - default: - return 0; - } - } - - public static TASstate fromIndex(int state) { - switch (state) { - case 0: - return NONE; - case 1: - return PLAYBACK; - case 2: - return RECORDING; - case 3: - return PAUSED; - default: - return NONE; - } - } + PAUSED; // #124 } - private TASstate stateWhenOpened; - public void setStateWhenOpened(TASstate state) { - TASmod.logger.trace(LoggerMarkers.Playback, "Set state when opened to {}", state); - stateWhenOpened = state; + TASmodClient.openMainMenuScheduler.add(() -> { + PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + if (state == TASstate.RECORDING) { + long seed = TASmod.ktrngHandler.getGlobalSeedClient(); + container.setStartSeed(seed); + } + setTASState(state); + }); } - + + // ====================================== Networking + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { + PLAYBACK_SAVE, + PLAYBACK_LOAD, + PLAYBACK_FULLPLAY, + PLAYBACK_FULLRECORD, + PLAYBACK_RESTARTANDPLAY, + PLAYBACK_PLAYUNTIL, + PLAYBACK_TELEPORT, + PLAYBACK_CLEAR_INPUTS, + PLAYBACK_STATE + }; + } + @Override - public GuiScreen onOpenGui(GuiScreen gui) { - if(gui instanceof GuiMainMenu) { - if (stateWhenOpened != null) { - PlaybackController container = TASmodClient.virtual.getContainer(); - if(stateWhenOpened == TASstate.RECORDING) { - long seed = TASmod.ktrngHandler.getGlobalSeedClient(); - container.setStartSeed(seed); + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + String name = null; + Minecraft mc = Minecraft.getMinecraft(); + + switch (packet) { + + case PLAYBACK_SAVE: + name = TASmodBufferBuilder.readString(buf); + try { + TASmodClient.virtual.saveInputs(name); + } catch (IOException e) { + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); + else + e.printStackTrace(); + return; } - container.setTASState(stateWhenOpened); - stateWhenOpened = null; - } + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Saved inputs to " + name + ".mctas")); + else + LOGGER.debug(LoggerMarkers.Playback, "Saved inputs to " + name + ".mctas"); + break; + + case PLAYBACK_LOAD: + name = TASmodBufferBuilder.readString(buf); + try { + TASmodClient.virtual.loadInputs(name); + } catch (IOException e) { + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); + else + e.printStackTrace(); + return; + } + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Loaded inputs from " + name + ".mctas")); + else + LOGGER.debug(LoggerMarkers.Playback, "Loaded inputs from " + name + ".mctas"); + break; + + case PLAYBACK_FULLPLAY: + setStateWhenOpened(TASstate.PLAYBACK); // Set the state to PLAYBACK when the main menu is opened + + TASmodClient.tickSchedulerClient.add(() -> { // Schedule code to be executed on the next tick + // Exit the server if you are in one + if (mc.world != null) { + mc.world.sendQuittingDisconnectingPacket(); + mc.loadWorld((WorldClient) null); + } + mc.displayGuiScreen(new GuiMainMenu()); + }); + break; + + case PLAYBACK_FULLRECORD: + setStateWhenOpened(TASstate.RECORDING); // Set the state to RECORDING when the main menu is opened + + TASmodClient.virtual.getContainer().clear(); // Clear inputs + + // Schedule code to be executed on the next tick + TASmodClient.tickSchedulerClient.add(() -> { + if (mc.world != null) { // Exit the server if you are in one + mc.world.sendQuittingDisconnectingPacket(); + mc.loadWorld((WorldClient) null); + } + mc.displayGuiScreen(new GuiMainMenu()); + }); + break; + + case PLAYBACK_RESTARTANDPLAY: + final String finalname = ByteBufferBuilder.readString(buf); + + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Minecraft.getMinecraft().addScheduledTask(() -> { + TASmodClient.config.set(ConfigOptions.FileToOpen, finalname); + System.exit(0); + }); + break; + + case PLAYBACK_PLAYUNTIL: + int until = ByteBufferBuilder.readInt(buf); + TASmodClient.virtual.getContainer().setPlayUntil(until); + break; + + case PLAYBACK_CLEAR_INPUTS: + TASmodClient.virtual.getContainer().clear(); + break; + + case PLAYBACK_TELEPORT: + throw new WrongSideException(packet, Side.CLIENT); + + case PLAYBACK_STATE: + TASstate networkState = TASmodBufferBuilder.readTASState(buf); + boolean verbose = TASmodBufferBuilder.readBoolean(buf); + Task task = ()->{ + PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + if (networkState != container.getState()) { + + String message = container.setTASStateClient(networkState, verbose); + + if (!message.isEmpty()) { + if(Minecraft.getMinecraft().world != null) + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentString(message)); + else + LOGGER.debug(LoggerMarkers.Playback, message); + } + } + + }; + + + if((networkState == TASstate.RECORDING || networkState == TASstate.PLAYBACK) && TASmodClient.tickratechanger.ticksPerSecond != 0) { + TASmodClient.tickSchedulerClient.add(task); // Starts a recording in the next tick + } else { + TASmodClient.gameLoopSchedulerClient.add(task); // Starts a recording in the next frame + } + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); } - return gui; } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java new file mode 100644 index 00000000..98fd36f5 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java @@ -0,0 +1,126 @@ +package com.minecrafttas.tasmod.playback; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; +import static com.minecrafttas.tasmod.util.LoggerMarkers.*; +import static com.minecrafttas.tasmod.networking.TASmodPackets.*; + +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; +import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; + +import net.minecraft.entity.player.EntityPlayerMP; + +/** + * The playback controller on the server side.
+ * Currently used sync the {@link TASstate} with all clients + * + * @author Scribble + * + */ +public class PlaybackControllerServer implements ServerPacketHandler { + + private TASstate state; + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] + { + PLAYBACK_STATE, + PLAYBACK_TELEPORT, + PLAYBACK_CLEAR_INPUTS, + PLAYBACK_FULLPLAY, + PLAYBACK_FULLRECORD, + PLAYBACK_RESTARTANDPLAY, + PLAYBACK_PLAYUNTIL, + PLAYBACK_SAVE, + PLAYBACK_LOAD + }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + + case PLAYBACK_STATE: + TASstate networkState = TASmodBufferBuilder.readTASState(buf); + /* TODO Permissions */ + setState(networkState); + break; + + case PLAYBACK_TELEPORT: + double x = TASmodBufferBuilder.readDouble(buf); + double y = TASmodBufferBuilder.readDouble(buf); + double z = TASmodBufferBuilder.readDouble(buf); + float angleYaw = TASmodBufferBuilder.readFloat(buf); + float anglePitch = TASmodBufferBuilder.readFloat(buf); + + EntityPlayerMP player = TASmod.getServerInstance().getPlayerList().getPlayerByUsername(username); + player.getServerWorld().addScheduledTask(() -> { + player.rotationPitch = anglePitch; + player.rotationYaw = angleYaw; + + player.setPositionAndUpdate(x, y, z); + }); + break; + + case PLAYBACK_CLEAR_INPUTS: + TASmod.server.sendToAll(new TASmodBufferBuilder(PLAYBACK_CLEAR_INPUTS)); + break; + case PLAYBACK_FULLPLAY: + case PLAYBACK_FULLRECORD: + case PLAYBACK_RESTARTANDPLAY: + case PLAYBACK_PLAYUNTIL: + case PLAYBACK_SAVE: + case PLAYBACK_LOAD: + TASmod.server.sendToAll(new TASmodBufferBuilder(buf)); + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.SERVER); + } + } + + public void setState(TASstate stateIn) { + setServerState(stateIn); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_STATE).writeTASState(state).writeBoolean(true)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void setServerState(TASstate stateIn) { + if (state != stateIn) { + if (state == TASstate.RECORDING && stateIn == TASstate.PLAYBACK || state == TASstate.PLAYBACK && stateIn == TASstate.RECORDING) + return; + if (state == TASstate.NONE && state == TASstate.PAUSED) { + return; + } + this.state = stateIn; + LOGGER.info(Playback, "Set the server state to {}", stateIn.toString()); + } + } + + public void toggleRecording() { + setState(state == TASstate.RECORDING ? TASstate.NONE : TASstate.RECORDING); + } + + public void togglePlayback() { + setState(state == TASstate.PLAYBACK ? TASstate.NONE : TASstate.PLAYBACK); + } + + public TASstate getState() { + return state; + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java index 1ef35db8..4a9ad482 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java @@ -1,5 +1,7 @@ package com.minecrafttas.tasmod.playback; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -13,8 +15,7 @@ import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; -import com.minecrafttas.tasmod.playback.PlaybackController.TickInputContainer; -import com.minecrafttas.tasmod.playback.controlbytes.ControlByteHandler; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; import com.minecrafttas.tasmod.util.FileThread; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.virtual.VirtualKey; @@ -25,7 +26,7 @@ import com.mojang.realmsclient.util.Pair; /** - * Saves a given {@linkplain PlaybackController} to a file. Is also able to read an input container from a file.
+ * Saves a given {@linkplain PlaybackControllerClient} to a file. Is also able to read an input container from a file.
*
* I plan to be backwards compatible so all the save functions have a V1 in their name by the time of writing this
*
@@ -86,7 +87,7 @@ public static String getRegexString() { * @param container The container to save * @throws IOException When the input container is empty */ - public void saveToFileV1(File file, PlaybackController container) throws IOException { + public void saveToFileV1(File file, PlaybackControllerClient container) throws IOException { saveToFileV1Until(file, container, -1); } @@ -97,8 +98,8 @@ public void saveToFileV1(File file, PlaybackController container) throws IOExcep * @param index index until the inputs get saved * @throws IOException When the input container is empty */ - public void saveToFileV1Until(File file, PlaybackController container, int index) throws IOException{ - TASmod.logger.debug(LoggerMarkers.Playback, "Saving playback controller to file {}", file); + public void saveToFileV1Until(File file, PlaybackControllerClient container, int index) throws IOException{ + LOGGER.debug(LoggerMarkers.Playback, "Saving playback controller to file {}", file); if (container.size() == 0) { throw new IOException("There are no inputs to save to a file"); } @@ -164,7 +165,7 @@ public void saveToFileV1Until(File file, PlaybackController container, int index } public int getFileVersion(File file) throws IOException { - TASmod.logger.trace(LoggerMarkers.Playback, "Retrieving file version from {}", file); + LOGGER.trace(LoggerMarkers.Playback, "Retrieving file version from {}", file); List lines = FileUtils.readLines(file, Charset.defaultCharset()); for (String line : lines) { if (line.contains("Version")) { @@ -181,8 +182,8 @@ public int getFileVersion(File file) throws IOException { return 0; } - public PlaybackController fromEntireFileV1(File file) throws IOException { - TASmod.logger.debug(LoggerMarkers.Playback, "Loading playback controller to file {}", file); + public PlaybackControllerClient fromEntireFileV1(File file) throws IOException { + LOGGER.debug(LoggerMarkers.Playback, "Loading playback controller to file {}", file); List lines = FileUtils.readLines(file, StandardCharsets.UTF_8); File monitorFile=new File(file, "../"+file.getName().replace(".mctas", "")+".mon"); @@ -196,7 +197,7 @@ public PlaybackController fromEntireFileV1(File file) throws IOException { } boolean oldmonfileLoaded=!monitorLines.isEmpty(); - PlaybackController controller = new PlaybackController(); + PlaybackControllerClient controller = new PlaybackControllerClient(); String author = "Insert author here"; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/server/InitialSyncStatePacket.java b/src/main/java/com/minecrafttas/tasmod/playback/server/InitialSyncStatePacket.java deleted file mode 100644 index 7eb69aa1..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/server/InitialSyncStatePacket.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.minecrafttas.tasmod.playback.server; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; - -/** - * Sends the state of the client to the server on server join. Is only applied if you are the first player on the server and if you are operator - * - * @author Scribble - * - */ -public class InitialSyncStatePacket extends SyncStatePacket { - - public InitialSyncStatePacket() { - super(); - } - - public InitialSyncStatePacket(TASstate state) { - super(state); - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isServer()) - TASmod.containerStateServer.onInitialPacket((EntityPlayerMP)player, this.getState()); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/server/SyncStatePacket.java b/src/main/java/com/minecrafttas/tasmod/playback/server/SyncStatePacket.java deleted file mode 100644 index a6994856..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/server/SyncStatePacket.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.minecrafttas.tasmod.playback.server; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.playback.PlaybackController; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.util.TickScheduler.TickTask; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.text.TextComponentString; - -/** - * Syncs the current state of the input recorder with the state on the server side and with the state on all other clients - * - * @author Scribble - * - */ -public class SyncStatePacket implements Packet { - - - private short state; - private boolean verbose; - - public SyncStatePacket() { - state = 0; - } - - public SyncStatePacket(TASstate state) { - verbose = true; - this.state = (short) state.getIndex(); - } - - public SyncStatePacket(TASstate state, boolean verbose) { - this.verbose = verbose; - this.state = (short) state.getIndex(); - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeShort(state); - buf.writeBoolean(verbose); - } - - @Override - public void deserialize(PacketBuffer buf) { - state = buf.readShort(); - verbose = buf.readBoolean(); - } - - protected TASstate getState() { - return TASstate.fromIndex(state); - } - - public boolean isVerbose() { - return verbose; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isServer()) { - TASmod.containerStateServer.onPacket((EntityPlayerMP)player, getState()); - } - else { - - TASstate state = getState(); - - TickTask task = ()->{ - - PlaybackController container = TASmodClient.virtual.getContainer(); - if (state != container.getState()) { - String chatMessage = container.setTASState(state, verbose); - if (!chatMessage.isEmpty()) { - Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentString(chatMessage)); - } - } - - }; - - - if((state == TASstate.RECORDING || state == TASstate.PLAYBACK) && TASmodClient.tickratechanger.ticksPerSecond != 0) { - TASmodClient.tickSchedulerClient.add(task); // Starts a recording in the next tick - } else { - TASmodClient.gameLoopSchedulerClient.add(task); // Starts a recording in the next frame - } - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateClient.java b/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateClient.java deleted file mode 100644 index 0e6dc062..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateClient.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.minecrafttas.tasmod.playback.server; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; - -import net.minecraft.client.Minecraft; - -public class TASstateClient { - - public static void setStateClient(TASstate state) { - TASmodClient.virtual.getContainer().setTASState(state); - } - - public static void setOrSend(TASstate state) { - if(Minecraft.getMinecraft().player!=null) { - TASmodClient.packetClient.sendToServer(new SyncStatePacket(state)); - }else { - TASmodClient.virtual.getContainer().setTASState(state); - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateServer.java b/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateServer.java deleted file mode 100644 index 3721a3ae..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/server/TASstateServer.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.minecrafttas.tasmod.playback.server; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; - -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.server.MinecraftServer; - -/** - * Stores the state of the input container on the server side.
- *
- * Since the current state, whether it's recording playing back or nothing is - * stored on the client,
- * there needs to be some form of synchronization between all clients so all - * clients have the same state.
- *
- * Additionally the client can start recording before the server is even started - * and when multiple clients still have to connect to the server. - * - * @author Scribble - * - */ -public class TASstateServer { - - private TASstate state; - - private boolean shouldChange = true; - - public TASstateServer() { - state = TASstate.NONE; - shouldChange = true; - } - - public void onInitialPacket(EntityPlayerMP player, TASstate tasState) { - if(player.canUseCommand(2, "") && shouldChange) { - setState(tasState); - shouldChange = false; - }else { - TASmod.packetServer.sendTo(new SyncStatePacket(tasState), player); - } - } - - public void onPacket(EntityPlayerMP player, TASstate tasState) { - if(player.canUseCommand(2, "")) { - setState(tasState); - } - } - - public void leaveServer(EntityPlayerMP player) { - MinecraftServer server = TASmod.getServerInstance(); - if (server != null) { - if (server.getPlayerList().getPlayers().size() == 1) { - state = TASstate.NONE; - shouldChange = true; - } - } - } - - public void setState(TASstate stateIn) { - setServerState(stateIn); - TASmod.packetServer.sendToAll(new SyncStatePacket(state, true)); - } - - public void setServerState(TASstate stateIn) { - if (state != stateIn) { - if (state == TASstate.RECORDING && stateIn == TASstate.PLAYBACK || state == TASstate.PLAYBACK && stateIn == TASstate.RECORDING) - return; - if(state==TASstate.NONE&&state==TASstate.PAUSED) { - return; - } - this.state = stateIn; - TASmod.logger.info(String.format("Set the server state to %s", stateIn.toString())); - } - } - - public void toggleRecording() { - setState(state == TASstate.RECORDING ? TASstate.NONE : TASstate.RECORDING); - } - - public void togglePlayback() { - setState(state == TASstate.PLAYBACK ? TASstate.NONE : TASstate.PLAYBACK); - } - - public TASstate getState() { - return state; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java new file mode 100644 index 00000000..5b34a2d9 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java @@ -0,0 +1,328 @@ +package com.minecrafttas.tasmod.savestates; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; +import com.minecrafttas.tasmod.savestates.gui.GuiSavestateSavingScreen; +import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; +import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.mojang.realmsclient.gui.ChatFormatting; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.multiplayer.ChunkProviderClient; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.GameType; +import net.minecraft.world.chunk.Chunk; + +/** + * Various savestate steps and actions for the client side + * + * @author Scribble + */ +public class SavestateHandlerClient implements ClientPacketHandler { + + public final static File savestateDirectory = new File(TASmodClient.tasdirectory + File.separator + "savestates"); + + /** + * A bug occurs when unloading the client world. The client world has a + * "unloadedEntityList" which, as the name implies, stores all unloaded entities + *
+ *
+ * Strange things happen, when the client player is unloaded, which is what + * happens when we use + * {@linkplain SavestateHandlerClient#unloadAllClientChunks()}.
+ *
+ * This method ensures that the player is loaded by removing the player from the + * unloadedEntityList.
+ *
+ * TLDR:
+ * Makes sure that the player is not removed from the loaded entity list
+ *
+ * Side: Client + */ + @Environment(EnvType.CLIENT) + public static void keepPlayerInLoadedEntityList(net.minecraft.entity.player.EntityPlayer player) { + LOGGER.trace(LoggerMarkers.Savestate, "Keep player {} in loaded entity list", player.getName()); + Minecraft.getMinecraft().world.unloadedEntityList.remove(player); + } + + /** + * Similar to {@linkplain keepPlayerInLoadedEntityList}, the chunks themselves + * have a list with loaded entities
+ *
+ * Even after adding the player to the world, the chunks may not load the player + * correctly.
+ *
+ * Without this, no model is shown in third person
+ * This state is fixed, once the player moves into a different chunk, since the + * new chunk adds the player to it's list.
+ *
+ * + * TLDR:
+ * Adds the player to the chunk so the player is shown in third person
+ *
+ * Side: Client + */ + @Environment(EnvType.CLIENT) + public static void addPlayerToClientChunk(EntityPlayer player) { + LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to loaded entity list", player.getName()); + int i = MathHelper.floor(player.posX / 16.0D); + int j = MathHelper.floor(player.posZ / 16.0D); + Chunk chunk = Minecraft.getMinecraft().world.getChunkFromChunkCoords(i, j); + for (int k = 0; k < chunk.getEntityLists().length; k++) { + if (chunk.getEntityLists()[k].contains(player)) { + return; + } + } + chunk.addEntity(player); + } + + /** + * Makes a copy of the recording that is currently running. Gets triggered when + * a savestate is made on the server
+ * Side: Client + * + * @param nameOfSavestate coming from the server + * @throws SavestateException + * @throws IOException + */ + public static void savestate(String nameOfSavestate) throws SavestateException, IOException { + LOGGER.debug(LoggerMarkers.Savestate, "Saving client savestate {}", nameOfSavestate); + if (nameOfSavestate.isEmpty()) { + LOGGER.error(LoggerMarkers.Savestate, "No recording savestate loaded since the name of savestate is empty"); + return; + } + + SavestateHandlerClient.savestateDirectory.mkdir(); + + File targetfile = new File(SavestateHandlerClient.savestateDirectory, nameOfSavestate + ".mctas"); + + PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + if (container.isRecording()) { + TASmodClient.serialiser.saveToFileV1(targetfile, container); // If the container is recording, store it entirely + } else if (container.isPlayingback()) { + TASmodClient.serialiser.saveToFileV1Until(targetfile, container, container.index()); // If the container is playing, store it until the current index + } + } + + /** + * Replaces the current recording with the recording from the savestate. Gets + * triggered when a savestate is loaded on the server
+ * Side: Client + * + * @param nameOfSavestate coming from the server + * @throws IOException + */ + public static void loadstate(String nameOfSavestate) throws IOException { + LOGGER.debug(LoggerMarkers.Savestate, "Loading client savestate {}", nameOfSavestate); + if (nameOfSavestate.isEmpty()) { + LOGGER.error(LoggerMarkers.Savestate, "No recording savestate loaded since the name of savestate is empty"); + return; + } + + savestateDirectory.mkdir(); + + File targetfile = new File(savestateDirectory, nameOfSavestate + ".mctas"); + + PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + if (!container.isNothingPlaying()) { // If the file exists and the container is recording or playing, load the + // clientSavestate + if (targetfile.exists()) { + TASmodClient.virtual.loadClientSavestate(TASmodClient.serialiser.fromEntireFileV1(targetfile)); + } else { + TASmodClient.virtual.getContainer().setTASStateClient(TASstate.NONE, false); + Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "Inputs could not be loaded for this savestate,")); + Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "since the file doesn't exist. Stopping!")); + LOGGER.warn(LoggerMarkers.Savestate, "Inputs could not be loaded for this savestate, since the file doesn't exist."); + } + } + } + + public static void loadPlayer(NBTTagCompound compound) { + LOGGER.trace(LoggerMarkers.Savestate, "Loading client player from NBT"); + Minecraft mc = Minecraft.getMinecraft(); + EntityPlayerSP player = mc.player; + + player.readFromNBT(compound); + NBTTagCompound motion = compound.getCompoundTag("clientMotion"); + + if(motion.hasNoTags()) { + LOGGER.warn(LoggerMarkers.Savestate, "Could not load the motion from the savestate. Savestate seems to be created manually or by a different mod"); + } else { + LOGGER.trace(LoggerMarkers.Savestate, "Loading client motion from NBT"); + double x = motion.getDouble("x"); + double y = motion.getDouble("y"); + double z = motion.getDouble("z"); + player.motionX = x; + player.motionY = y; + player.motionZ = z; + + float rx = motion.getFloat("RelativeX"); + float ry = motion.getFloat("RelativeY"); + float rz = motion.getFloat("RelativeZ"); + player.moveForward = rx; + player.moveVertical = ry; + player.moveStrafing = rz; + + boolean sprinting = motion.getBoolean("Sprinting"); + float jumpVector = motion.getFloat("JumpFactor"); + player.setSprinting(sprinting); + player.jumpMovementFactor = jumpVector; + } + + LOGGER.trace(LoggerMarkers.Savestate, "Setting client gamemode"); + // #86 + int gamemode = compound.getInteger("playerGameType"); + GameType type = GameType.getByID(gamemode); + mc.playerController.setGameType(type); + + // #?? Player rotation does not change when loading a savestate +// CameraInterpolationEvents.rotationPitch = player.rotationPitch; +// CameraInterpolationEvents.rotationYaw = player.rotationYaw + 180f; + + SavestateHandlerClient.keepPlayerInLoadedEntityList(player); + } + + /** + * Unloads all chunks and reloads the renderer so no chunks will be visible + * throughout the unloading progress
+ *
+ * Side: Client + * + * @see MixinChunkProviderClient#unloadAllChunks() + */ + @Environment(EnvType.CLIENT) + public static void unloadAllClientChunks() { + LOGGER.trace(LoggerMarkers.Savestate, "Unloading All Client Chunks"); + Minecraft mc = Minecraft.getMinecraft(); + + ChunkProviderClient chunkProvider = mc.world.getChunkProvider(); + + ((ChunkProviderDuck) chunkProvider).unloadAllChunks(); + Minecraft.getMinecraft().renderGlobal.loadRenderers(); + } + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { + TASmodPackets.SAVESTATE_SAVE, + TASmodPackets.SAVESTATE_LOAD, + TASmodPackets.SAVESTATE_PLAYER, + TASmodPackets.SAVESTATE_REQUEST_MOTION, + TASmodPackets.SAVESTATE_SCREEN, + TASmodPackets.SAVESTATE_UNLOAD_CHUNKS + }; + } + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + String name = null; + Minecraft mc = Minecraft.getMinecraft(); + + switch (packet) { + case SAVESTATE_SAVE: + // Create client savestate + name = TASmodBufferBuilder.readString(buf); + try { + SavestateHandlerClient.savestate(name); + } catch (SavestateException e) { + LOGGER.error(e.getMessage()); + } catch (IOException e) { + e.printStackTrace(); + } + break; + case SAVESTATE_LOAD: + // Load client savestate + name = TASmodBufferBuilder.readString(buf); + try { + SavestateHandlerClient.loadstate(name); + } catch (IOException e) { + e.printStackTrace(); + } + break; + case SAVESTATE_PLAYER: + NBTTagCompound compound; + try { + compound = TASmodBufferBuilder.readNBTTagCompound(buf); + } catch (IOException e) { + e.printStackTrace(); + return; + } + /* + * Fair warning: Do NOT read the buffer inside an addScheduledTask. Read it + * before that. The buffer will have the wrong limit, when the task is executed. + * This is probably due to the buffers being reused. + */ + Minecraft.getMinecraft().addScheduledTask(() -> { + SavestateHandlerClient.loadPlayer(compound); + }); + break; + + case SAVESTATE_REQUEST_MOTION: + EntityPlayerSP player = Minecraft.getMinecraft().player; + if (player != null) { + if (!(Minecraft.getMinecraft().currentScreen instanceof GuiSavestateSavingScreen)) { + Minecraft.getMinecraft().displayGuiScreen(new GuiSavestateSavingScreen()); + } + TASmodClient.client.send( + new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION) + .writeMotionData( + new MotionData( + player.motionX, + player.motionY, + player.motionZ, + player.moveForward, + player.moveVertical, + player.moveStrafing, + player.isSprinting(), + player.jumpMovementFactor) + ) + ); + } + break; + case SAVESTATE_SCREEN: + // Open/Close Savestate screen + boolean open = TASmodBufferBuilder.readBoolean(buf); + if (open) { + mc.displayGuiScreen(new GuiSavestateSavingScreen()); + } else { + mc.displayGuiScreen(null); + } + break; + + case SAVESTATE_UNLOAD_CHUNKS: + Minecraft.getMinecraft().addScheduledTask(() -> { + SavestateHandlerClient.unloadAllClientChunks(); + }); + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); + } + + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateHandler.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java similarity index 54% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateHandler.java rename to src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java index 772148c9..c77c7ea4 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestateHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java @@ -1,47 +1,68 @@ -package com.minecrafttas.tasmod.savestates.server; +package com.minecrafttas.tasmod.savestates; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.Logger; +import com.google.common.collect.Maps; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.events.EventServer.EventCompleteLoadstate; import com.minecrafttas.tasmod.events.EventServer.EventLoadstate; import com.minecrafttas.tasmod.events.EventServer.EventSavestate; import com.minecrafttas.tasmod.mixin.savestates.AccessorAnvilChunkLoader; import com.minecrafttas.tasmod.mixin.savestates.AccessorChunkLoader; -import com.minecrafttas.tasmod.savestates.client.InputSavestatesPacket; -import com.minecrafttas.tasmod.savestates.server.chunkloading.SavestatesChunkControl; -import com.minecrafttas.tasmod.savestates.server.exceptions.LoadstateException; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateDeleteException; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; -import com.minecrafttas.tasmod.savestates.server.files.SavestateDataFile; -import com.minecrafttas.tasmod.savestates.server.files.SavestateDataFile.DataValues; -import com.minecrafttas.tasmod.savestates.server.files.SavestateTrackerFile; -import com.minecrafttas.tasmod.savestates.server.motion.ClientMotionServer; -import com.minecrafttas.tasmod.savestates.server.playerloading.SavestatePlayerLoading; +import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderServer; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; +import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateDeleteException; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; +import com.minecrafttas.tasmod.savestates.files.SavestateDataFile; +import com.minecrafttas.tasmod.savestates.files.SavestateDataFile.DataValues; +import com.minecrafttas.tasmod.savestates.files.SavestateTrackerFile; +import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.server.management.PlayerList; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.NextTickListEntry; +import net.minecraft.world.World; import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.storage.AnvilChunkLoader; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraft.world.storage.SaveHandler; +import net.minecraft.world.storage.WorldInfo; /** * Creates and loads savestates on both client and server without closing the @@ -51,10 +72,10 @@ * "https://www.curseforge.com/minecraft/mc-mods/worldstatecheckpoints">WorldStateCheckpoints, * but this new version is completely self written. * - * @author ScribbleLP + * @author Scribble * */ -public class SavestateHandler implements EventCompleteLoadstate{ +public class SavestateHandlerServer implements ServerPacketHandler { private MinecraftServer server; private File savestateDirectory; @@ -67,14 +88,15 @@ public class SavestateHandler implements EventCompleteLoadstate{ private int currentIndex; private final Logger logger; - + public static boolean wasLoading; + /** * Creates a savestate handler on the specified server * @param logger * * @param The server that should store the savestates */ - public SavestateHandler(MinecraftServer server, Logger logger) { + public SavestateHandlerServer(MinecraftServer server, Logger logger) { this.server = server; this.logger = logger; createSavestateDirectory(); @@ -126,6 +148,14 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex if (state == SavestateState.LOADING) { throw new SavestateException("A loadstate operation is being carried out"); } + + try { + // Open GuiSavestateScreen + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SCREEN).writeBoolean(true)); + } catch (Exception e) { + e.printStackTrace(); + } + // Lock savestating and loadstating state = SavestateState.SAVING; @@ -139,7 +169,7 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex server = TASmod.getServerInstance(); // Get the motion from the client - ClientMotionServer.requestMotionFromClient(); + PlayerHandler.requestMotionFromClient(); // Save the world! server.getPlayerList().saveAllPlayerData(); @@ -182,7 +212,12 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex * Send the name of the world to all players. This will make a savestate of the * recording on the client with that name */ - TASmod.packetServer.sendToAll(new InputSavestatesPacket(true, getSavestateName(indexToSave))); + try { + // savestate inputs client + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SAVE).writeString(getSavestateName(indexToSave))); + } catch (Exception e) { + e.printStackTrace(); + } } // Wait for the chunkloader to save the game @@ -206,8 +241,12 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex // Send a notification that the savestate has been loaded server.getPlayerList().sendMessage(new TextComponentString(TextFormatting.GREEN + "Savestate " + indexToSave + " saved")); - // Close the GuiSavestateScreen on the client - TASmod.packetServer.sendToAll(new SavestatePacket()); + try { + // close GuiSavestateScreen + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SCREEN).writeBoolean(false)); + } catch (Exception e) { + e.printStackTrace(); + } if (!tickrate0) { TASmod.tickratechanger.pauseGame(false); @@ -308,8 +347,12 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex * InputSavestate) */ if (savestateIndex != 0) { - // Load savestate on the client - TASmod.packetServer.sendToAll(new InputSavestatesPacket(false, getSavestateName(indexToLoad))); + try { + // loadstate inputs client + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeString(getSavestateName(indexToLoad))); + } catch (Exception e) { + e.printStackTrace(); + } } // Disabeling level saving for all worlds in case the auto save kicks in during @@ -318,13 +361,18 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex world.disableLevelSaving = true; } - // Unload chunks on the client - TASmod.packetServer.sendToAll(new LoadstatePacket()); + + try { + // unload chunks on client + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_UNLOAD_CHUNKS)); + } catch (Exception e) { + e.printStackTrace(); + } // Unload chunks on the server - SavestatesChunkControl.disconnectPlayersFromChunkMap(server); - SavestatesChunkControl.unloadAllServerChunks(server); - SavestatesChunkControl.flushSaveHandler(server); + ChunkHandler.disconnectPlayersFromChunkMap(server); + ChunkHandler.unloadAllServerChunks(server); + ChunkHandler.flushSaveHandler(server); // Delete and copy directories FileUtils.deleteDirectory(currentfolder); @@ -334,11 +382,11 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex loadSavestateDataFile(); // Update the player and the client - SavestatePlayerLoading.loadAndSendMotionToPlayer(server); + PlayerHandler.loadAndSendMotionToPlayer(server); // Update the session.lock file so minecraft behaves and saves the world - SavestatesChunkControl.updateSessionLock(server); + ChunkHandler.updateSessionLock(server); // Load the chunks and send them to the client - SavestatesChunkControl.addPlayersToChunkMap(server); + ChunkHandler.addPlayersToChunkMap(server); // Enable level saving again for (WorldServer world : server.worlds) { @@ -355,7 +403,7 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex // Add players to the chunk server.getPlayerList().getPlayers().forEach(player->{ - SavestatesChunkControl.addPlayerToServerChunk(player); + ChunkHandler.addPlayerToServerChunk(player); }); WorldServer[] worlds = server.worlds; @@ -368,9 +416,16 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex TASmod.tickratechanger.pauseGame(false); } - // Unlock loadstating - state = SavestateState.WASLOADING; + + TASmod.tickSchedulerServer.add(()->{ + EventCompleteLoadstate.fireLoadstateComplete(); + onLoadstateComplete(); + + // Unlock savestating + state = SavestateState.NONE; + }); } + /** * Creates the savestate directory in case the user deletes it between @@ -629,21 +684,20 @@ public int getCurrentIndex() { return currentIndex; } - @Override public void onLoadstateComplete() { - TASmod.logger.trace(LoggerMarkers.Event, "Running loadstate complete event"); + logger.trace(LoggerMarkers.Savestate, "Running loadstate complete event"); PlayerList playerList = TASmod.getServerInstance().getPlayerList(); for (EntityPlayerMP player : playerList.getPlayers()) { NBTTagCompound nbttagcompound = playerList.readPlayerDataFromFile(player); - SavestatePlayerLoading.reattachEntityToPlayer(nbttagcompound, player.getServerWorld(), player); + PlayerHandler.reattachEntityToPlayer(nbttagcompound, player.getServerWorld(), player); } // Updating redstone component timers to the new world time (#136) - SavestatesChunkControl.updateWorldServerTickListEntries(); + ChunkHandler.updateWorldServerTickListEntries(); } @Environment(EnvType.CLIENT) public static void playerLoadSavestateEventClient() { - SavestatesChunkControl.addPlayerToClientChunk(Minecraft.getMinecraft().player); + SavestateHandlerClient.addPlayerToClientChunk(Minecraft.getMinecraft().player); } private int legacyIndexFile(File savestateDat) { @@ -671,8 +725,404 @@ private int legacyIndexFile(File savestateDat) { public static enum SavestateState { SAVING, LOADING, - WASLOADING, NONE } -} \ No newline at end of file + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { + TASmodPackets.SAVESTATE_SAVE, + TASmodPackets.SAVESTATE_LOAD, + TASmodPackets.SAVESTATE_PLAYER, + TASmodPackets.SAVESTATE_REQUEST_MOTION, + TASmodPackets.SAVESTATE_SCREEN, + TASmodPackets.SAVESTATE_UNLOAD_CHUNKS + }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + // TODO Permissions + TASmodPackets packet = (TASmodPackets) id; + + EntityPlayerMP player = TASmod.getServerInstance().getPlayerList().getPlayerByUsername(username); + Integer index = null; + + switch (packet) { + case SAVESTATE_SAVE: + index = TASmodBufferBuilder.readInt(buf); + try { + TASmod.savestateHandlerServer.saveState(index, true); + } catch (SavestateException e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getMessage())); + + LOGGER.error(LoggerMarkers.Savestate, "Failed to create a savestate: " + e.getMessage()); + } catch (Exception e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getCause().toString())); + + LOGGER.error(e); + } finally { + TASmod.savestateHandlerServer.state = SavestateState.NONE; + } + break; + + case SAVESTATE_LOAD: + int indexing = TASmodBufferBuilder.readInt(buf); + player.getServerWorld().addScheduledTask(() -> { + try { + TASmod.savestateHandlerServer.loadState(indexing, true); + } catch (LoadstateException e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getMessage())); + + LOGGER.error(LoggerMarkers.Savestate, "Failed to create a savestate: " + e.getMessage()); + TASmod.savestateHandlerServer.state = SavestateState.NONE; + } catch (Exception e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getCause().toString())); + + LOGGER.error(e); + TASmod.savestateHandlerServer.state = SavestateState.NONE; + } + }); + break; + + case SAVESTATE_REQUEST_MOTION: + MotionData data = TASmodBufferBuilder.readMotionData(buf); + PlayerHandler.getMotion().put(player, data); + break; + case SAVESTATE_PLAYER: + case SAVESTATE_SCREEN: + case SAVESTATE_UNLOAD_CHUNKS: + throw new WrongSideException(id, Side.SERVER); + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.SERVER); + } + } + + /** + * Contains player related classes + */ + public static class PlayerHandler { + + public static class MotionData { + private double clientX; + private double clientY; + private double clientZ; + private float clientrX; + private float clientrY; + private float clientrZ; + private boolean sprinting; + private float jumpMovementVector; + + public MotionData(double x, double y, double z, float rx, float ry, float rz, boolean sprinting, float jumpMovementVector) { + clientX = x; + clientY = y; + clientZ = z; + clientrX = rx; + clientrY = ry; + clientrZ = rz; + this.sprinting = sprinting; + this.jumpMovementVector = jumpMovementVector; + } + + public double getClientX() { + return clientX; + } + + public double getClientY() { + return clientY; + } + + public double getClientZ() { + return clientZ; + } + + public float getClientrX() { + return clientrX; + } + + public float getClientrY() { + return clientrY; + } + + public float getClientrZ() { + return clientrZ; + } + + public boolean isSprinting() { + return sprinting; + } + + public float getJumpMovementVector() { + return jumpMovementVector; + } + } + + /** + * Tries to reattach the player to an entity, if the player was riding it it while savestating. + * + * Side: Server + * @param nbttagcompound where the ridden entity is saved + * @param worldserver that needs to spawn the entity + * @param playerIn that needs to ride the entity + */ + public static void reattachEntityToPlayer(NBTTagCompound nbttagcompound, World worldserver, Entity playerIn) { + if (nbttagcompound != null && nbttagcompound.hasKey("RootVehicle", 10)) + { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("RootVehicle"); + Entity entity1 = AnvilChunkLoader.readWorldEntity(nbttagcompound1.getCompoundTag("Entity"), worldserver, true); + + + if(entity1==null) { + for (Entity entity : worldserver.loadedEntityList) { + if(entity.getUniqueID().equals(nbttagcompound1.getUniqueId("Attach"))) entity1=entity; + } + } + + if (entity1 != null) + { + UUID uuid = nbttagcompound1.getUniqueId("Attach"); + + if (entity1.getUniqueID().equals(uuid)) + { + playerIn.startRiding(entity1, true); + } + else + { + for (Entity entity : entity1.getRecursivePassengers()) + { + if (entity.getUniqueID().equals(uuid)) + { + playerIn.startRiding(entity, true); + break; + } + } + } + + if (!playerIn.isRiding()) + { + LOGGER.warn("Couldn't reattach entity to player"); + worldserver.removeEntityDangerously(entity1); + + for (Entity entity2 : entity1.getRecursivePassengers()) + { + worldserver.removeEntityDangerously(entity2); + } + } + } + } + else { + if(playerIn.isRiding()) { + playerIn.dismountRidingEntity(); + } + } + } + + /** + * Loads all worlds and players from the disk. Also sends the playerdata to the client in {@linkplain SavestatePlayerLoadingPacketHandler} + * + * Side: Server + */ + public static void loadAndSendMotionToPlayer(MinecraftServer server) { + + List players=server.getPlayerList().getPlayers(); + PlayerList list=server.getPlayerList(); + + WorldServer[] worlds=server.worlds; + for (WorldServer world : worlds) { + WorldInfo info=world.getSaveHandler().loadWorldInfo(); + world.worldInfo = info; + } + for(EntityPlayerMP player : players) { + + int dimensionPrev=player.dimension; + + NBTTagCompound nbttagcompound = server.getPlayerList().readPlayerDataFromFile(player); + + int dimensionNow=0; + if (nbttagcompound.hasKey("Dimension")) + { + dimensionNow = nbttagcompound.getInteger("Dimension"); + } + + if(dimensionNow!=dimensionPrev) { + list.changePlayerDimension(player, dimensionNow); + } else { + player.getServerWorld().unloadedEntityList.remove(player); + } + + player.readFromNBT(nbttagcompound); + + LOGGER.debug(LoggerMarkers.Savestate, "Sending motion to {}", player.getName()); + + try { + TASmod.server.sendTo(player, new TASmodBufferBuilder(TASmodPackets.SAVESTATE_PLAYER).writeNBTTagCompound(nbttagcompound)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static void requestMotionFromClient() { + LOGGER.trace(LoggerMarkers.Savestate, "Request motion from client"); + PlayerHandler.motion.clear(); + try { + // request client motion + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static Map getMotion() { + return PlayerHandler.motion; + } + + public static Map motion = Maps.newHashMap(); + + } + + /** + * Contains static chunk actions, which can be triggered indiviadually for testing + */ + public static class ChunkHandler{ + + /** + * Updates ticklist entries to the current world time, allowing them to not be stuck in a pressed state #136 + */ + public static void updateWorldServerTickListEntries() { + LOGGER.trace(LoggerMarkers.Savestate, "Update server tick list entries"); + MinecraftServer server=TASmod.getServerInstance(); + for (WorldServer world : server.worlds) { + for (NextTickListEntry nextticklistentry : world.pendingTickListEntriesHashSet) { + nextticklistentry.setScheduledTime(world.getTotalWorldTime()); + } + } + } + + /** + * Just like {@link SavestateHandlerClient#addPlayerToClientChunk(EntityPlayer)}, adds the player to the chunk on the server. + * This prevents the player from being able to place block inside of him + * + * Side: Server + */ + public static void addPlayerToServerChunk(EntityPlayerMP player) { + LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to server chunk", player.getName()); + int i = MathHelper.floor(player.posX / 16.0D); + int j = MathHelper.floor(player.posZ / 16.0D); + WorldServer world = player.getServerWorld(); + Chunk chunk = world.getChunkFromChunkCoords(i, j); + for (int k = 0; k < chunk.getEntityLists().length; k++) { + if (chunk.getEntityLists()[k].contains(player)) { + return; + } + } + chunk.addEntity(player); + } + + /** + * The session lock is minecrafts failsafe system when it comes to saving. It prevents writing to the world folder from 2 different locations
+ *
+ * That works by storing system time to a session.lock file, when the server started. The integrated server also saves the time when it started in a variable.
+ *
+ * Those two times are then compared every time minecraft tries to save and fails if the times are different.
+ *
+ * Since we never close the integrated server, but copy an "old" session.lock file with the savestate, the session.lock will always mismatch.
+ * Thus we need to update the session lock once the loadstating is completed
+ *
+ * TLDR:
+ * Updates the session lock to allow for vanilla saving again
+ *
+ * Side: Server + */ + public static void updateSessionLock(MinecraftServer server) { + LOGGER.trace(LoggerMarkers.Savestate, "Update the session lock"); + WorldServer[] worlds=server.worlds; + for(WorldServer world : worlds) { + ((SaveHandler) world.getSaveHandler()).setSessionLock(); + } + } + + /** + * Tells the save handler to save all changes to disk and remove all references to the region files, making them editable on disc
+ *
+ * Side: Server + */ + public static void flushSaveHandler(MinecraftServer server) { + LOGGER.trace(LoggerMarkers.Savestate, "Flush the save handler"); + //Vanilla + WorldServer[] worlds=server.worlds; + for(WorldServer world : worlds) { + world.getSaveHandler().flush(); + } + } + + /** + * The player chunk map keeps track of which chunks need to be sent to the client.
+ * This adds the player to the chunk map so the server knows it can send the information to the client
+ *
+ * Side: Server + * @see disconnectPlayersFromChunkMap + */ + public static void addPlayersToChunkMap(MinecraftServer server) { + List players=server.getPlayerList().getPlayers(); + WorldServer[] worlds=server.worlds; + for (EntityPlayerMP player : players) { + LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to the chunk map", player.getName()); + switch (player.dimension) { + case -1: + worlds[1].getPlayerChunkMap().addPlayer(player); + worlds[1].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); + break; + case 0: + worlds[0].getPlayerChunkMap().addPlayer(player); + worlds[0].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); + break; + case 1: + worlds[2].getPlayerChunkMap().addPlayer(player); + worlds[2].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); + break; + } + } + } + + /** + * The player chunk map keeps track of which chunks need to be sent to the client.
+ * Removing the player stops the server from sending chunks to the client.
+ *
+ * Side: Server + * @see SavestatesChunkControl#addPlayersToChunkMap() + */ + public static void disconnectPlayersFromChunkMap(MinecraftServer server) { + List players=server.getPlayerList().getPlayers(); + WorldServer[] worlds=server.worlds; + for (WorldServer world : worlds) { + for (EntityPlayerMP player : players) { + LOGGER.trace(LoggerMarkers.Savestate, "Disconnect player {} from the chunk map", player.getName()); + world.getPlayerChunkMap().removePlayer(player); + } + } + } + + /** + * Unloads all chunks on the server
+ *
+ * Side: Server + * @see MixinChunkProviderServer#unloadAllChunks() + */ + public static void unloadAllServerChunks(MinecraftServer server) { + LOGGER.trace(LoggerMarkers.Savestate, "Unloading all server chunks"); + WorldServer[] worlds=server.worlds; + + for (WorldServer world:worlds) { + ChunkProviderServer chunkProvider=world.getChunkProvider(); + + ((ChunkProviderDuck)chunkProvider).unloadAllChunks(); + } + + } + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesHandler.java b/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesHandler.java deleted file mode 100644 index b34e066a..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesHandler.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.minecrafttas.tasmod.savestates.client; - -import java.io.File; -import java.io.IOException; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackController; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; -import com.minecrafttas.tasmod.util.LoggerMarkers; -import com.mojang.realmsclient.gui.ChatFormatting; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.util.text.TextComponentString; - -/** - * Creating savestates of the inputs on the client
- * Side: Client - * - * @author ScribbleLP - */ -@Environment(EnvType.CLIENT) -public class InputSavestatesHandler { - - private final static File savestateDirectory = new File(TASmodClient.tasdirectory + File.separator + "savestates"); - - /** - * Makes a copy of the recording that is currently running. Gets triggered when - * a savestate is made on the server
- * Side: Client - * - * @param nameOfSavestate coming from the server - * @throws SavestateException - * @throws IOException - */ - public static void savestate(String nameOfSavestate) throws SavestateException, IOException { - TASmod.logger.debug(LoggerMarkers.Savestate, "Saving client savestate {}", nameOfSavestate); - if (nameOfSavestate.isEmpty()) { - TASmod.logger.error(LoggerMarkers.Savestate, "No recording savestate loaded since the name of savestate is empty"); - return; - } - - savestateDirectory.mkdir(); - - File targetfile = new File(savestateDirectory, nameOfSavestate + ".mctas"); - - PlaybackController container = TASmodClient.virtual.getContainer(); - if (container.isRecording()) { - TASmodClient.serialiser.saveToFileV1(targetfile, container); //If the container is recording, store it entirely - } else if(container.isPlayingback()){ - TASmodClient.serialiser.saveToFileV1Until(targetfile, container, container.index()); //If the container is playing, store it until the current index - } - } - - /** - * Replaces the current recording with the recording from the savestate. - * Gets triggered when a savestate is loaded on the server
- * Side: Client - * - * @param nameOfSavestate coming from the server - * @throws IOException - */ - public static void loadstate(String nameOfSavestate) throws IOException { - TASmod.logger.debug(LoggerMarkers.Savestate, "Loading client savestate {}", nameOfSavestate); - if (nameOfSavestate.isEmpty()) { - TASmod.logger.error(LoggerMarkers.Savestate, "No recording savestate loaded since the name of savestate is empty"); - return; - } - - savestateDirectory.mkdir(); - - File targetfile = new File(savestateDirectory, nameOfSavestate + ".mctas"); - - PlaybackController container = TASmodClient.virtual.getContainer(); - if (!container.isNothingPlaying()) { // If the file exists and the container is recording or playing, load the clientSavestate - if (targetfile.exists()) { - TASmodClient.virtual.loadClientSavestate(TASmodClient.serialiser.fromEntireFileV1(targetfile)); - } else { - TASmodClient.virtual.getContainer().setTASState(TASstate.NONE, false); - Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW - + "Inputs could not be loaded for this savestate, since the file doesn't exist. Stopping!")); - TASmod.logger.warn(LoggerMarkers.Savestate, "Inputs could not be loaded for this savestate, since the file doesn't exist."); - } - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesPacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesPacket.java deleted file mode 100644 index 46ef6fdd..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/client/InputSavestatesPacket.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.minecrafttas.tasmod.savestates.client; - -import java.io.IOException; -import java.nio.charset.Charset; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -public class InputSavestatesPacket implements Packet{ - private boolean mode; - private String name; - - public InputSavestatesPacket() { - } - /** - * Makes a savestate of the recording on the Client - * @param mode If true: Make a savestate, else load the savestate - * @param name Name of the savestated file - */ - public InputSavestatesPacket(boolean mode,String name) { - this.mode=mode; - this.name=name; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isClient()) { - if (mode == true) { - try { - InputSavestatesHandler.savestate(name); - } catch (SavestateException e) { - TASmod.logger.error(e.getMessage()); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - try { - InputSavestatesHandler.loadstate(name); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - } - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(name.length()); - buf.writeCharSequence(name, Charset.defaultCharset()); - buf.writeBoolean(mode); - } - @Override - public void deserialize(PacketBuffer buf) { - int length=buf.readInt(); - name=(String) buf.readCharSequence(length, Charset.defaultCharset()); - mode=buf.readBoolean(); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/LoadstateException.java b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java similarity index 75% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/LoadstateException.java rename to src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java index 849e1e80..aa6419f6 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/LoadstateException.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.server.exceptions; +package com.minecrafttas.tasmod.savestates.exceptions; public class LoadstateException extends Exception{ /** diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateDeleteException.java b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateDeleteException.java similarity index 76% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateDeleteException.java rename to src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateDeleteException.java index df7d12ac..83764627 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateDeleteException.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateDeleteException.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.server.exceptions; +package com.minecrafttas.tasmod.savestates.exceptions; public class SavestateDeleteException extends Exception { /** diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateException.java b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java similarity index 75% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateException.java rename to src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java index a68c9610..bfe1ff33 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/exceptions/SavestateException.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.server.exceptions; +package com.minecrafttas.tasmod.savestates.exceptions; public class SavestateException extends Exception{ /** diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateDataFile.java b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java similarity index 94% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateDataFile.java rename to src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java index 90dad8bb..c41b4f8f 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateDataFile.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.server.files; +package com.minecrafttas.tasmod.savestates.files; import java.io.File; import java.io.FileNotFoundException; @@ -14,7 +14,7 @@ public class SavestateDataFile { public enum DataValues { INDEX("currentIndex"), - Name("savestateName"), + NAME("savestateName"), SEED("ktrngSeed"); diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateTrackerFile.java b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java similarity index 96% rename from src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateTrackerFile.java rename to src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java index 825ca960..95a64fdd 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/files/SavestateTrackerFile.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.server.files; +package com.minecrafttas.tasmod.savestates.files; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateLoadingScreen.java b/src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateLoadingScreen.java similarity index 85% rename from src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateLoadingScreen.java rename to src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateLoadingScreen.java index e07da96c..d0619aaa 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateLoadingScreen.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateLoadingScreen.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.client.gui; +package com.minecrafttas.tasmod.savestates.gui; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -7,6 +7,10 @@ public class GuiSavestateLoadingScreen extends GuiScreen { + public GuiSavestateLoadingScreen() { + this.mc=Minecraft.getMinecraft(); + } + @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateSavingScreen.java b/src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateSavingScreen.java similarity index 86% rename from src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateSavingScreen.java rename to src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateSavingScreen.java index db1d5ba1..92f16863 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/client/gui/GuiSavestateSavingScreen.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/gui/GuiSavestateSavingScreen.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.savestates.client.gui; +package com.minecrafttas.tasmod.savestates.gui; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -7,6 +7,10 @@ public class GuiSavestateSavingScreen extends GuiScreen { + public GuiSavestateSavingScreen() { + this.mc = Minecraft.getMinecraft(); + } + @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/LoadstatePacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/LoadstatePacket.java deleted file mode 100644 index 188b82e4..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/LoadstatePacket.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.savestates.server.chunkloading.SavestatesChunkControl; -import com.minecrafttas.tasmod.savestates.server.exceptions.LoadstateException; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; - -public class LoadstatePacket implements Packet { - - public int index; - - /** - * Load a savestate at the current index - */ - public LoadstatePacket() { - index = -1; - } - - /** - * Load the savestate at the specified index - * - * @param index The index to load the savestate - */ - public LoadstatePacket(int index) { - this.index = index; - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if (side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP) playerz; - player.getServerWorld().addScheduledTask(()->{ - if (!player.canUseCommand(2, "tickrate")) { - player.sendMessage(new TextComponentString(TextFormatting.RED + "You don't have permission to do that")); - return; - } - try { - TASmod.savestateHandler.loadState(index, true); - } catch (LoadstateException e) { - player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getMessage())); - } catch (Exception e) { - player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getCause().toString())); - e.printStackTrace(); - } finally { - TASmod.savestateHandler.state = SavestateState.NONE; - } - }); - } else { - Minecraft.getMinecraft().addScheduledTask(()->{ - SavestatesChunkControl.unloadAllClientChunks(); - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(index); - } - - @Override - public void deserialize(PacketBuffer buf) { - index = buf.readInt(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestatePacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestatePacket.java deleted file mode 100644 index 8d5cb81f..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/SavestatePacket.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.client.gui.GuiSavestateSavingScreen; -import com.minecrafttas.tasmod.savestates.server.SavestateHandler.SavestateState; -import com.minecrafttas.tasmod.savestates.server.exceptions.SavestateException; - -import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; - -public class SavestatePacket implements Packet { - - public int index; - - /** - * Make a savestate at the next index - */ - public SavestatePacket() { - index=-1; - } - - /** - * Make a savestate at the specified index - * - * @param index The index where to make a savestate - */ - public SavestatePacket(int index) { - this.index=index; - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if(side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP) playerz; - player.getServerWorld().addScheduledTask(()->{ - if (!player.canUseCommand(2, "savestate")) { - player.sendMessage(new TextComponentString(TextFormatting.RED+"You don't have permission to do that")); - return; - } - try { - TASmod.savestateHandler.saveState(index, true); - } catch (SavestateException e) { - player.sendMessage(new TextComponentString(TextFormatting.RED+"Failed to create a savestate: "+ e.getMessage())); - - } catch (Exception e) { - e.printStackTrace(); - player.sendMessage(new TextComponentString(TextFormatting.RED+"Failed to create a savestate: "+ e.getCause().toString())); - } finally { - TASmod.savestateHandler.state=SavestateState.NONE; - } - }); - - } - else { - Minecraft.getMinecraft().addScheduledTask(() -> { - Minecraft mc = Minecraft.getMinecraft(); - if (!(mc.currentScreen instanceof GuiSavestateSavingScreen)) { - mc.displayGuiScreen(new GuiSavestateSavingScreen()); - } else { - mc.displayGuiScreen(null); - } - }); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeInt(index); - } - - @Override - public void deserialize(PacketBuffer buf) { - index=buf.readInt(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/chunkloading/SavestatesChunkControl.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/chunkloading/SavestatesChunkControl.java deleted file mode 100644 index 130f5b06..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/chunkloading/SavestatesChunkControl.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.chunkloading; - -import java.util.List; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderClient; -import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderServer; -import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; -import com.minecrafttas.tasmod.util.LoggerMarkers; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.math.MathHelper; -import net.minecraft.world.NextTickListEntry; -import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.ChunkProviderServer; -import net.minecraft.world.storage.SaveHandler; -/** - * Various methods to unload/reload chunks and make loadless savestates possible - * @author Scribble - * - */ -public class SavestatesChunkControl { - /** - * Unloads all chunks and reloads the renderer so no chunks will be visible throughout the unloading progress
- *
- * Side: Client - * @see MixinChunkProviderClient#unloadAllChunks() - */ - @Environment(EnvType.CLIENT) - public static void unloadAllClientChunks() { - TASmod.logger.trace(LoggerMarkers.Savestate, "Unloading All Client Chunks"); - Minecraft mc = Minecraft.getMinecraft(); - - ChunkProviderClient chunkProvider=mc.world.getChunkProvider(); - - ((ChunkProviderDuck)chunkProvider).unloadAllChunks(); - Minecraft.getMinecraft().renderGlobal.loadRenderers(); - } - /** - * Unloads all chunks on the server
- *
- * Side: Server - * @see MixinChunkProviderServer#unloadAllChunks() - */ - public static void unloadAllServerChunks(MinecraftServer server) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Unloading all server chunks"); - //Vanilla - WorldServer[] worlds=server.worlds; - - for (WorldServer world:worlds) { - ChunkProviderServer chunkProvider=world.getChunkProvider(); - - ((ChunkProviderDuck)chunkProvider).unloadAllChunks(); - } - - } - /** - * The player chunk map keeps track of which chunks need to be sent to the client.
- * Removing the player stops the server from sending chunks to the client.
- *
- * Side: Server - * @see #addPlayersToChunkMap() - */ - public static void disconnectPlayersFromChunkMap(MinecraftServer server) { - List players=server.getPlayerList().getPlayers(); - //Vanilla - WorldServer[] worlds=server.worlds; - for (WorldServer world : worlds) { - for (EntityPlayerMP player : players) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Disconnect player {} from the chunk map", player.getName()); - world.getPlayerChunkMap().removePlayer(player); - } - } - } - /** - * The player chunk map keeps track of which chunks need to be sent to the client.
- * This adds the player to the chunk map so the server knows it can send the information to the client
- *
- * Side: Server - * @see #disconnectPlayersFromChunkMap() - */ - public static void addPlayersToChunkMap(MinecraftServer server) { - List players=server.getPlayerList().getPlayers(); - //Vanilla - WorldServer[] worlds=server.worlds; - for (EntityPlayerMP player : players) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Add player {} to the chunk map", player.getName()); - switch (player.dimension) { - case -1: - worlds[1].getPlayerChunkMap().addPlayer(player); - worlds[1].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); - break; - case 0: - worlds[0].getPlayerChunkMap().addPlayer(player); - worlds[0].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); - break; - case 1: - worlds[2].getPlayerChunkMap().addPlayer(player); - worlds[2].getChunkProvider().provideChunk((int)player.posX >> 4, (int)player.posZ >> 4); - break; - } - } - } - /** - * Tells the save handler to save all changes to disk and remove all references to the region files, making them editable on disc
- *
- * Side: Server - */ - public static void flushSaveHandler(MinecraftServer server) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Flush the save handler"); - //Vanilla - WorldServer[] worlds=server.worlds; - for(WorldServer world : worlds) { - world.getSaveHandler().flush(); - } - } - /** - * The session lock is minecrafts failsafe system when it comes to saving. It prevents writing to the world folder from 2 different locations
- *
- * That works by storing system time to a session.lock file, when the server started. The integrated server also saves the time when it started in a variable.
- *
- * Those two times are then compared every time minecraft tries to save and fails if the times are different.
- *
- * Since we never close the integrated server, but copy an "old" session.lock file with the savestate, the session.lock will always mismatch.
- * Thus we need to update the session lock once the loadstating is completed
- *
- * TLDR:
- * Updates the session lock to allow for vanilla saving again
- *
- * Side: Server - */ - public static void updateSessionLock(MinecraftServer server) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Update the session lock"); - WorldServer[] worlds=server.worlds; - for(WorldServer world : worlds) { - ((SaveHandler) world.getSaveHandler()).setSessionLock(); - } - } - /** - * A bug occurs when unloading the client world. The client world has a "unloadedEntityList" which, as the name implies, stores all unloaded entities
- *
- * Strange things happen, when the client player is unloaded, which is what happens when we use {@linkplain #unloadAllClientChunks()}.
- *
- * This method ensures that the player is loaded by removing the player from the unloadedEntityList.
- *
- * TLDR:
- * Makes sure that the player is not removed from the loaded entity list
- *
- * Side: Client - */ - @Environment(EnvType.CLIENT) - public static void keepPlayerInLoadedEntityList(net.minecraft.entity.player.EntityPlayer player) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Keep player {} in loaded entity list", player.getName()); - Minecraft.getMinecraft().world.unloadedEntityList.remove(player); - } - - /** - * Similar to {@linkplain #keepPlayerInLoadedEntityList(EntityPlayer)}, the chunks themselves have a list with loaded entities
- *
- * Even after adding the player to the world, the chunks may not load the player correctly.
- *
- * Without this, no model is shown in third person
- * This state is fixed, once the player moves into a different chunk, since the new chunk adds the player to it's list.
- *
- * - * TLDR:
- * Adds the player to the chunk so the player is shown in third person
- *
- * Side: Client - */ - @Environment(EnvType.CLIENT) - public static void addPlayerToClientChunk(EntityPlayer player) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Add player {} to loaded entity list", player.getName()); - int i = MathHelper.floor(player.posX / 16.0D); - int j = MathHelper.floor(player.posZ / 16.0D); - Chunk chunk = Minecraft.getMinecraft().world.getChunkFromChunkCoords(i, j); - for (int k = 0; k < chunk.getEntityLists().length; k++) { - if (chunk.getEntityLists()[k].contains(player)) { - return; - } - } - chunk.addEntity(player); - } - - /** - * Just like {@link #addPlayerToClientChunk(EntityPlayer)}, adds the player to the chunk on the server. - * This prevents the player from being able to place block inside of him - * - * Side: Server - */ - public static void addPlayerToServerChunk(EntityPlayerMP player) { - TASmod.logger.trace(LoggerMarkers.Savestate, "Add player {} to server chunk", player.getName()); - int i = MathHelper.floor(player.posX / 16.0D); - int j = MathHelper.floor(player.posZ / 16.0D); - WorldServer world = player.getServerWorld(); - Chunk chunk = world.getChunkFromChunkCoords(i, j); - for (int k = 0; k < chunk.getEntityLists().length; k++) { - if (chunk.getEntityLists()[k].contains(player)) { - return; - } - } - chunk.addEntity(player); - } - - /** - * Updates ticklist entries to the current world time, allowing them to not be stuck in a pressed state #136 - */ - public static void updateWorldServerTickListEntries() { - TASmod.logger.trace(LoggerMarkers.Savestate, "Update server tick list entries"); - MinecraftServer server=TASmod.getServerInstance(); - for (WorldServer world : server.worlds) { - for (NextTickListEntry nextticklistentry : world.pendingTickListEntriesHashSet) { - nextticklistentry.setScheduledTime(world.getTotalWorldTime()); - } - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/ClientMotionServer.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/ClientMotionServer.java deleted file mode 100644 index dbfe299a..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/ClientMotionServer.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.motion; - -import java.util.Map; - -import com.google.common.collect.Maps; -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.util.LoggerMarkers; - -import net.minecraft.entity.player.EntityPlayerMP; - -public class ClientMotionServer { - - private static Map motion = Maps.newHashMap(); - - public static Map getMotion() { - return motion; - } - - public static void requestMotionFromClient() { - TASmod.logger.trace(LoggerMarkers.Savestate, "Request motion from client"); - motion.clear(); - TASmod.packetServer.sendToAll(new RequestMotionPacket()); - - int i = 1; - while (motion.size() != TASmod.getServerInstance().getPlayerList().getCurrentPlayerCount()) { - i++; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if(i % 30 == 1) { - TASmod.logger.debug(LoggerMarkers.Savestate, "Resending motion packet"); - TASmod.packetServer.sendToAll(new RequestMotionPacket()); - } - if (i == 1000) { - TASmod.logger.warn(LoggerMarkers.Savestate, "Client motion timed out!"); - break; - } - - } - } - - // =========================================================== - - public static class Saver { - private double clientX; - private double clientY; - private double clientZ; - private float clientrX; - private float clientrY; - private float clientrZ; - private boolean sprinting; - private float jumpMovementVector; - - public Saver(double x, double y, double z, float rx, float ry, float rz, boolean sprinting, float jumpMovementVector) { - clientX = x; - clientY = y; - clientZ = z; - clientrX = rx; - clientrY = ry; - clientrZ = rz; - this.sprinting = sprinting; - this.jumpMovementVector = jumpMovementVector; - } - - public double getClientX() { - return clientX; - } - - public double getClientY() { - return clientY; - } - - public double getClientZ() { - return clientZ; - } - - public float getClientrX() { - return clientrX; - } - - public float getClientrY() { - return clientrY; - } - - public float getClientrZ() { - return clientrZ; - } - - public boolean isSprinting() { - return sprinting; - } - - public float getJumpMovementVector() { - return jumpMovementVector; - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/MotionPacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/MotionPacket.java deleted file mode 100644 index 4968cd2b..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/MotionPacket.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.motion; - -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.server.motion.ClientMotionServer.Saver; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.PacketBuffer; - -/** - * A packet containing the motion data of the player - * @author Scribble - * - */ -public class MotionPacket implements Packet { - private double x = 0; - private double y = 0; - private double z = 0; - private float rx = 0; - private float ry = 0; - private float rz = 0; - private boolean sprinting; - private float jumpMovementVector = 0.2F; - - public MotionPacket() { - } - - /** - * Sends the players motion data to the server - * @param x - * @param y - * @param z - * @param moveForward - * @param moveVertical - * @param moveStrafe - * @param isSprinting - * @param jumpMovementVector - */ - public MotionPacket(double x, double y, double z, float moveForward, float moveVertical, float moveStrafe, boolean isSprinting, float jumpMovementVector) { - this.x = x; - this.y = y; - this.z = z; - this.rx = moveForward; - this.ry = moveVertical; - this.rz = moveStrafe; - sprinting = isSprinting; - this.jumpMovementVector = jumpMovementVector; - } - - @Override - public void handle(PacketSide side, EntityPlayer playercommon) { - if (side.isServer()) { - EntityPlayerMP player = (EntityPlayerMP) playercommon; - ClientMotionServer.getMotion().put(player, new Saver(x, y, z, rx, ry, rz, sprinting, jumpMovementVector)); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - buf.writeFloat(rx); - buf.writeFloat(ry); - buf.writeFloat(rz); - buf.writeBoolean(sprinting); - buf.writeFloat(jumpMovementVector); - } - - @Override - public void deserialize(PacketBuffer buf) { - x = buf.readDouble(); - y = buf.readDouble(); - z = buf.readDouble(); - rx = buf.readFloat(); - ry = buf.readFloat(); - rz = buf.readFloat(); - sprinting = buf.readBoolean(); - jumpMovementVector = buf.readFloat(); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/RequestMotionPacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/RequestMotionPacket.java deleted file mode 100644 index 975db5ae..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/motion/RequestMotionPacket.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.motion; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.client.gui.GuiSavestateSavingScreen; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Requests the motion from the client - * @author Scribble - * - */ -public class RequestMotionPacket implements Packet { - - /** - * Requests the motion from the client - */ - public RequestMotionPacket() { - } - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if(side.isClient()) { - EntityPlayerSP player = (EntityPlayerSP) playerz; - if (player != null) { - if (!(Minecraft.getMinecraft().currentScreen instanceof GuiSavestateSavingScreen)) { - Minecraft.getMinecraft().displayGuiScreen(new GuiSavestateSavingScreen()); - } - TASmodClient.packetClient.sendToServer(new MotionPacket(player.motionX, player.motionY, player.motionZ, player.moveForward, player.moveVertical, player.moveStrafing, player.isSprinting(), player.jumpMovementFactor)); - } - } - - } - - @Override - public void serialize(PacketBuffer buf) { - - } - - @Override - public void deserialize(PacketBuffer buf) { - - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoading.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoading.java deleted file mode 100644 index 9f8a5821..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoading.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.playerloading; - -import java.util.List; -import java.util.UUID; - -import com.minecrafttas.tasmod.TASmod; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.management.PlayerList; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.storage.AnvilChunkLoader; -import net.minecraft.world.storage.WorldInfo; - -public class SavestatePlayerLoading { - - public static boolean wasLoading; - - /** - * Loads all worlds and players from the disk. Also sends the playerdata to the client in {@linkplain SavestatePlayerLoadingPacketHandler} - * - * Side: Server - */ - public static void loadAndSendMotionToPlayer(MinecraftServer server) { - - List players=server.getPlayerList().getPlayers(); - PlayerList list=server.getPlayerList(); - - WorldServer[] worlds=server.worlds; - for (WorldServer world : worlds) { - WorldInfo info=world.getSaveHandler().loadWorldInfo(); - world.worldInfo = info; - } - for(EntityPlayerMP player : players) { - - int dimensionPrev=player.dimension; - - NBTTagCompound nbttagcompound = server.getPlayerList().readPlayerDataFromFile(player); - - int dimensionNow=0; - if (nbttagcompound.hasKey("Dimension")) - { - dimensionNow = nbttagcompound.getInteger("Dimension"); - } - - if(dimensionNow!=dimensionPrev) { - list.changePlayerDimension(player, dimensionNow); - }else { - player.getServerWorld().unloadedEntityList.remove(player); - } - - player.readFromNBT(nbttagcompound); - - TASmod.packetServer.sendTo(new SavestatePlayerLoadingPacket(nbttagcompound), player); - } - } - - /** - * Tries to reattach the player to an entity, if the player was riding it it while savestating. - * - * Side: Server - * @param nbttagcompound where the ridden entity is saved - * @param worldserver that needs to spawn the entity - * @param playerIn that needs to ride the entity - */ - public static void reattachEntityToPlayer(NBTTagCompound nbttagcompound, World worldserver, Entity playerIn) { - if (nbttagcompound != null && nbttagcompound.hasKey("RootVehicle", 10)) - { - NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("RootVehicle"); - Entity entity1 = AnvilChunkLoader.readWorldEntity(nbttagcompound1.getCompoundTag("Entity"), worldserver, true); - - - if(entity1==null) { - for (Entity entity : worldserver.loadedEntityList) { - if(entity.getUniqueID().equals(nbttagcompound1.getUniqueId("Attach"))) entity1=entity; - } - } - - if (entity1 != null) - { - UUID uuid = nbttagcompound1.getUniqueId("Attach"); - - if (entity1.getUniqueID().equals(uuid)) - { - playerIn.startRiding(entity1, true); - } - else - { - for (Entity entity : entity1.getRecursivePassengers()) - { - if (entity.getUniqueID().equals(uuid)) - { - playerIn.startRiding(entity, true); - break; - } - } - } - - if (!playerIn.isRiding()) - { - TASmod.logger.warn("Couldn't reattach entity to player"); - worldserver.removeEntityDangerously(entity1); - - for (Entity entity2 : entity1.getRecursivePassengers()) - { - worldserver.removeEntityDangerously(entity2); - } - } - } - } - else { - if(playerIn.isRiding()) { - playerIn.dismountRidingEntity(); - } - } - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoadingPacket.java b/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoadingPacket.java deleted file mode 100644 index e48e5406..00000000 --- a/src/main/java/com/minecrafttas/tasmod/savestates/server/playerloading/SavestatePlayerLoadingPacket.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.minecrafttas.tasmod.savestates.server.playerloading; - -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; -import com.minecrafttas.tasmod.savestates.server.chunkloading.SavestatesChunkControl; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.PacketBuffer; -import net.minecraft.world.GameType; - -/** - * Reads the playerdata coming from the server and also applies motion, relative - * motion and other things from the player - * - * @author Scribble - * - */ -public class SavestatePlayerLoadingPacket implements Packet { - - private NBTTagCompound compound; - - public SavestatePlayerLoadingPacket() { - } - - public SavestatePlayerLoadingPacket(NBTTagCompound nbttagcompound) { - compound = nbttagcompound; - }; - - - @Override - public void handle(PacketSide side, EntityPlayer playerz) { - if(side.isClient()) { - EntityPlayerSP player = (EntityPlayerSP)playerz; - - player.readFromNBT(compound); - NBTTagCompound motion = compound.getCompoundTag("clientMotion"); - - double x = motion.getDouble("x"); - double y = motion.getDouble("y"); - double z = motion.getDouble("z"); - player.motionX = x; - player.motionY = y; - player.motionZ = z; - - float rx = motion.getFloat("RelativeX"); - float ry = motion.getFloat("RelativeY"); - float rz = motion.getFloat("RelativeZ"); - player.moveForward = rx; - player.moveVertical = ry; - player.moveStrafing = rz; - - boolean sprinting = motion.getBoolean("Sprinting"); - float jumpVector = motion.getFloat("JumpFactor"); - player.setSprinting(sprinting); - player.jumpMovementFactor = jumpVector; - - // #86 - int gamemode = compound.getInteger("playerGameType"); - GameType type = GameType.getByID(gamemode); - Minecraft.getMinecraft().playerController.setGameType(type); - - // #?? Player rotation does not change when loading a savestate -// CameraInterpolationEvents.rotationPitch = player.rotationPitch; -// CameraInterpolationEvents.rotationYaw = player.rotationYaw + 180f; - - SavestatesChunkControl.keepPlayerInLoadedEntityList(player); - SavestatePlayerLoading.wasLoading = true; - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeCompoundTag(compound); - } - - @Override - public void deserialize(PacketBuffer buf) { - compound = buf.readCompoundTag(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/AdvanceTickratePacket.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/AdvanceTickratePacket.java deleted file mode 100644 index 168725dd..00000000 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/AdvanceTickratePacket.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.minecrafttas.tasmod.tickratechanger; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Advanced game by 1 tick - * - * @author Scribble - * - */ -public class AdvanceTickratePacket implements Packet { - /** - * Advanced game by 1 tick - */ - public AdvanceTickratePacket() { - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if (side.isServer()) { - if (player.canUseCommand(2, "tickrate")) { - if (TASmod.tickratechanger.ticksPerSecond == 0) { - TASmod.tickratechanger.advanceTick(); - } - } - } else { - TASmodClient.tickratechanger.advanceClientTick(); // Using advanceTick() would create an endless loop - } - } - - @Override - public void serialize(PacketBuffer buf) { - - } - - @Override - public void deserialize(PacketBuffer buf) { - - } - - -} diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/ChangeTickratePacket.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/ChangeTickratePacket.java deleted file mode 100644 index a5987a41..00000000 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/ChangeTickratePacket.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.minecrafttas.tasmod.tickratechanger; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Changes the tickrate on the other side - * - * @author Scribble - * - */ -public class ChangeTickratePacket implements Packet { - - float tickrate; - - public ChangeTickratePacket() { - } - - /** - * Changes the tickrate on the other side - * - * @param tickrate The new tickrate - */ - public ChangeTickratePacket(float tickrate) { - this.tickrate = tickrate; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if (side.isServer()) { - if (player.canUseCommand(2, "tickrate")) { - TASmod.tickratechanger.changeTickrate(tickrate); - } - } else { - TASmodClient.tickratechanger.changeClientTickrate(tickrate); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeFloat(tickrate); - } - - @Override - public void deserialize(PacketBuffer buf) { - tickrate = buf.readFloat(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/PauseTickratePacket.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/PauseTickratePacket.java deleted file mode 100644 index 041f9437..00000000 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/PauseTickratePacket.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.minecrafttas.tasmod.tickratechanger; - -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Sets the game to tickrate 0 and back - * - * @author Scribble - * - */ -public class PauseTickratePacket implements Packet { - - private short status; - - /** - * Toggles the tickrate between 0 and tickrate > 0 - */ - public PauseTickratePacket() { - status = 0; - } - - /** - * Changes the state to either PAUSE UNPAUSE or TOGGLED - * - * @param state The state - */ - public PauseTickratePacket(State state) { - this.status = state.toShort(); - } - - public State getState() { - return State.fromShort(status); - } - - - /** - * Can be {@link State#PAUSE}, {@link State#UNPAUSE} or {@link State#TOGGLE} - * - * @author Scribble - * - */ - public enum State { - /** - * Set's the game to tickrate 0 - */ - PAUSE((short) 1), - /** - * Set's the game to "tickrate saved" - */ - UNPAUSE((short) 2), - /** - * Toggles between {@link #PAUSE} and {@link #UNPAUSE} - */ - TOGGLE((short) 0); - - private short id; - - State(short i) { - id = i; - } - - public short toShort() { - return id; - } - - public static State fromShort(short i) { - switch (i) { - case 1: - return PAUSE; - case 2: - return UNPAUSE; - default: - return TOGGLE; - } - } - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if (side.isServer()) { - if (player.canUseCommand(2, "tickrate")) { - State state = getState(); - if (state == State.PAUSE) - TASmod.tickratechanger.pauseGame(true); - else if (state == State.UNPAUSE) - TASmod.tickratechanger.pauseGame(false); - else if (state == State.TOGGLE) - TASmod.tickratechanger.togglePause(); - } - } else if (side.isClient()) { - State state = getState(); - if (state == State.PAUSE) - TASmodClient.tickratechanger.pauseClientGame(true); - else if (state == State.UNPAUSE) - TASmodClient.tickratechanger.pauseClientGame(false); - else if (state == State.TOGGLE) - TASmodClient.tickratechanger.togglePauseClient(); - } - } - - @Override - public void serialize(PacketBuffer buf) { - buf.writeShort(status); - } - - @Override - public void deserialize(PacketBuffer buf) { - status = buf.readShort(); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java index f85e1fc9..b42ab8b1 100644 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java @@ -1,19 +1,30 @@ package com.minecrafttas.tasmod.tickratechanger; -import com.minecrafttas.common.events.EventClient.EventClientGameLoop; -import com.minecrafttas.tasmod.TASmod; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.nio.ByteBuffer; + +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.ticksync.TickSyncClient; +import com.minecrafttas.tasmod.events.EventClient.EventClientTickrateChange; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.minecraft.client.Minecraft; /** * Changes the {@link Minecraft#timer} variable + * * @author Scribble * */ -public class TickrateChangerClient implements EventClientGameLoop{ +public class TickrateChangerClient implements ClientPacketHandler { /** * The current tickrate of the client */ @@ -24,22 +35,22 @@ public class TickrateChangerClient implements EventClientGameLoop{ * pausing */ public float tickrateSaved = 20F; - + /** * True if the tickrate is 20 and the client should advance 1 tick */ public boolean advanceTick = false; - + public long millisecondsPerTick = 50L; public TickrateChangerClient() { this(20f); } - + public TickrateChangerClient(float initialTickrate) { ticksPerSecond = initialTickrate; } - + /** * Changes both client and server tickrates * @@ -49,7 +60,7 @@ public void changeTickrate(float tickrate) { changeClientTickrate(tickrate); changeServerTickrate(tickrate); } - + public void changeClientTickrate(float tickrate) { changeClientTickrate(tickrate, true); } @@ -69,7 +80,7 @@ public void changeClientTickrate(float tickrate, boolean log) { if (tickrate > 0) { millisecondsPerTick = (long) (1000F / tickrate); mc.timer.tickLength = millisecondsPerTick; - + } else if (tickrate == 0F) { if (ticksPerSecond != 0) { tickrateSaved = ticksPerSecond; @@ -77,8 +88,9 @@ public void changeClientTickrate(float tickrate, boolean log) { mc.timer.tickLength = Float.MAX_VALUE; } ticksPerSecond = tickrate; - if(log) - log("Setting the client tickrate to "+ ticksPerSecond); + EventClientTickrateChange.fireOnClientTickrateChange(tickrate); + if (log) + log("Setting the client tickrate to " + ticksPerSecond); } /** @@ -91,17 +103,24 @@ public void changeServerTickrate(float tickrate) { if (tickrate < 0) { return; } - TASmodClient.packetClient.sendToServer(new ChangeTickratePacket(tickrate)); + + try { + // request tickrate change + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.TICKRATE_CHANGE).writeFloat(tickrate)); + } catch (Exception e) { + e.printStackTrace(); + } } /** * Toggles between tickrate 0 and tickrate > 0 */ public void togglePause() { - if (Minecraft.getMinecraft().world != null) { - TASmodClient.packetClient.sendToServer(new PauseTickratePacket()); - } else { - togglePauseClient(); + try { + // request tickrate change + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.TICKRATE_ZERO).writeTickratePauseState(TickratePauseState.TOGGLE)); + } catch (Exception e) { + e.printStackTrace(); } } @@ -126,25 +145,27 @@ public void pauseGame(boolean pause) { if (pause) { changeTickrate(0F); } else { - advanceTick=false; + advanceTick = false; changeTickrate(tickrateSaved); } } /** * Pauses the game without sending a command to the server + * * @param pause The state of the client */ public void pauseClientGame(boolean pause) { - if(pause) { + if (pause) { changeClientTickrate(0F); - }else { + } else { changeClientTickrate(tickrateSaved); } } - + /** - * Advances the game by 1 tick. Sends a {@link AdvanceTickratePacket} to the server or calls {@link #advanceClientTick()} if the world is null + * Advances the game by 1 tick. Sends a {@link AdvanceTickratePacket} to the + * server or calls {@link #advanceClientTick()} if the world is null */ public void advanceTick() { if (Minecraft.getMinecraft().world != null) { @@ -158,9 +179,13 @@ public void advanceTick() { * Sends a {@link AdvanceTickratePacket} to the server to advance the server */ public void advanceServerTick() { - TASmodClient.packetClient.sendToServer(new AdvanceTickratePacket()); + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.TICKRATE_ADVANCE)); + } catch (Exception e) { + e.printStackTrace(); + } } - + /** * Advances the game by 1 tick. Doesn't send a packet to the server */ @@ -170,19 +195,51 @@ public void advanceClientTick() { changeClientTickrate(tickrateSaved); } } - + public void joinServer() { changeServerTickrate(ticksPerSecond); } - + private static void log(String msg) { - TASmod.logger.debug(LoggerMarkers.Tickrate, msg); + LOGGER.debug(LoggerMarkers.Tickrate, msg); } @Override - public void onRunClientGameLoop(Minecraft mc) { - if (TASmodClient.packetClient != null && TASmodClient.packetClient.isClosed()) { // If the server died, but the client has not left the world - TickSyncClient.shouldTick.set(true); + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { TASmodPackets.TICKRATE_CHANGE, TASmodPackets.TICKRATE_ADVANCE, TASmodPackets.TICKRATE_ZERO, }; + } + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case TICKRATE_CHANGE: + float tickrate = TASmodBufferBuilder.readFloat(buf); + changeClientTickrate(tickrate); + break; + case TICKRATE_ADVANCE: + advanceClientTick(); + break; + case TICKRATE_ZERO: + TickratePauseState state = TASmodBufferBuilder.readTickratePauseState(buf); + + switch (state) { + case PAUSE: + pauseClientGame(true); + break; + case UNPAUSE: + pauseClientGame(false); + break; + case TOGGLE: + togglePauseClient(); + default: + break; + } + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); } } diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java index a6d400df..eb0686a9 100644 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java @@ -1,10 +1,20 @@ package com.minecrafttas.tasmod.tickratechanger; +import java.nio.ByteBuffer; + import org.apache.logging.log4j.Logger; import com.minecrafttas.common.events.EventServer.EventPlayerJoinedServerSide; import com.minecrafttas.common.events.EventServer.EventServerStop; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.events.EventServer.EventServerTickrateChange; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.minecraft.entity.player.EntityPlayerMP; @@ -13,50 +23,53 @@ /** * Controls the tickrate on the server side * - * The tickrate is controlled in MinecraftServer.run() where the server is halted for 50 milliseconds minus the time the tick took to execute. + * The tickrate is controlled in MinecraftServer.run() where the server is + * halted for 50 milliseconds minus the time the tick took to execute. *

- * To change the tickrate on server and all clients use {@link #changeTickrate(float)}. + * To change the tickrate on server and all clients use + * {@link #changeTickrate(float)}. *

- * You can individually set the tickrate with {@link #changeClientTickrate(float)} and {@link #changeServerTickrate(float)}. + * You can individually set the tickrate with + * {@link #changeClientTickrate(float)} and + * {@link #changeServerTickrate(float)}. *

* * * @author Scribble * */ -public class TickrateChangerServer implements EventServerStop, EventPlayerJoinedServerSide{ - +public class TickrateChangerServer implements EventServerStop, EventPlayerJoinedServerSide, ServerPacketHandler { + /** * The current tickrate of the client */ - public float ticksPerSecond=20F; - + public float ticksPerSecond = 20F; + /** * How long the server should sleep */ - public long millisecondsPerTick=50L; - + public long millisecondsPerTick = 50L; + /** * The tickrate before {@link #ticksPerSecond} was changed to 0, used to toggle * pausing */ - public float tickrateSaved=20F; - + public float tickrateSaved = 20F; + /** * True if the tickrate is 20 and the server should advance 1 tick */ - public boolean advanceTick=false; - + public boolean advanceTick = false; + /** * The logger used for logging. Has to be set seperately */ public Logger logger; - - + public TickrateChangerServer(Logger logger) { this.logger = logger; } - + /** * Changes both client and server tickrates. *

@@ -68,21 +81,26 @@ public void changeTickrate(float tickrate) { changeClientTickrate(tickrate); changeServerTickrate(tickrate); } - + public void changeClientTickrate(float tickrate) { changeClientTickrate(tickrate, false); } - + /** * Changes the tickrate of all clients. Sends a {@link ChangeTickratePacket} * * @param tickrate The new tickrate of the client - * @param log If a message should logged + * @param log If a message should logged */ public void changeClientTickrate(float tickrate, boolean log) { - if(log) - log("Changing the tickrate "+ tickrate + " to all clients"); - TASmod.packetServer.sendToAll(new ChangeTickratePacket(tickrate)); + if (log) + log("Changing the tickrate " + tickrate + " to all clients"); + + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.TICKRATE_CHANGE).writeFloat(tickrate)); + } catch (Exception e) { + e.printStackTrace(); + } } /** @@ -93,106 +111,118 @@ public void changeClientTickrate(float tickrate, boolean log) { public void changeServerTickrate(float tickrate) { changeServerTickrate(tickrate, true); } - + /** * Changes the tickrate of the server * * @param tickrate The new tickrate of the server - * @param log If a message should logged + * @param log If a message should logged */ public void changeServerTickrate(float tickrate, boolean log) { - if(tickrate>0) { - millisecondsPerTick = (long)(1000L / tickrate); - }else if(tickrate==0) { - if(ticksPerSecond!=0) { - tickrateSaved=ticksPerSecond; - } - } - ticksPerSecond = tickrate; - if(log) { - log("Setting the server tickrate to "+ ticksPerSecond); - } - } - + if (tickrate > 0) { + millisecondsPerTick = (long) (1000L / tickrate); + } else if (tickrate == 0) { + if (ticksPerSecond != 0) { + tickrateSaved = ticksPerSecond; + } + } + ticksPerSecond = tickrate; + EventServerTickrateChange.fireOnServerTickrateChange(tickrate); + if (log) { + log("Setting the server tickrate to " + ticksPerSecond); + } + } + /** * Toggles between tickrate 0 and tickrate > 0 */ public void togglePause() { - if(ticksPerSecond>0) { + if (ticksPerSecond > 0) { changeTickrate(0); - } - else if (ticksPerSecond==0) { - changeTickrate(tickrateSaved); - } - } - + } else if (ticksPerSecond == 0) { + changeTickrate(tickrateSaved); + } + } + /** * Enables tickrate 0 + * * @param pause True if the game should be paused, false if unpause */ public void pauseGame(boolean pause) { - if(pause) { + if (pause) { changeTickrate(0); - } - else { - advanceTick=false; - changeTickrate(tickrateSaved); - } + } else { + advanceTick = false; + changeTickrate(tickrateSaved); + } } - + /** * Pauses the game without sending a command to the clients + * * @param pause The state of the server */ public void pauseServerGame(boolean pause) { - if(pause) { + if (pause) { changeServerTickrate(0F); - }else { + } else { changeServerTickrate(tickrateSaved); } } - - + /** * Advances the game by 1 tick. */ - public void advanceTick() { - advanceServerTick(); - advanceClientTick(); - } - - /** - * Sends a {@link AdvanceTickratePacket} to all clients - */ - private static void advanceClientTick() { - TASmod.packetServer.sendToAll(new AdvanceTickratePacket()); - } - - /** - * Advances the server by 1 tick - */ + public void advanceTick() { + advanceServerTick(); + advanceClientTick(); + } + + /** + * Sends a {@link AdvanceTickratePacket} to all clients + */ + private void advanceClientTick() { + // Do not check for ticksPerSecond==0 here, because at this point, ticksPerSecond is 20 for one tick! + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.TICKRATE_ADVANCE)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Advances the server by 1 tick + */ private void advanceServerTick() { - if(ticksPerSecond==0) { - advanceTick=true; - changeServerTickrate(tickrateSaved); - } + if (ticksPerSecond == 0) { + advanceTick = true; + changeServerTickrate(tickrateSaved); + } } /** - * Fired when a player joined the server - * @param player The player that joins the server - */ + * Fired when a player joined the server + * + * @param player The player that joins the server + */ @Override public void onPlayerJoinedServerSide(EntityPlayerMP player) { - if(TASmod.getServerInstance().isDedicatedServer()) { - log("Sending the current tickrate ("+ticksPerSecond+") to " +player.getName()); - TASmod.packetServer.sendTo(new ChangeTickratePacket(ticksPerSecond), player); + if (TASmod.getServerInstance().isDedicatedServer()) { + log("Sending the current tickrate (" + ticksPerSecond + ") to " + player.getName()); + + try { + TASmod.server.sendTo(player, new TASmodBufferBuilder(TASmodPackets.TICKRATE_CHANGE).writeFloat(ticksPerSecond)); + } catch (Exception e) { + e.printStackTrace(); + } } } - + /** * The message to log - * @param msg + * + * @param msg */ private void log(String msg) { logger.debug(LoggerMarkers.Tickrate, msg); @@ -204,4 +234,62 @@ public void onServerStop(MinecraftServer server) { pauseGame(false); } } + + /** + * Enum for sending paused states for the tickratechanger + */ + public static enum TickratePauseState { + /** + * Set's the game to tickrate 0 + */ + PAUSE, + /** + * Set's the game to "tickrate saved" + */ + UNPAUSE, + /** + * Toggles between {@link #PAUSE} and {@link #UNPAUSE} + */ + TOGGLE; + } + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] { TASmodPackets.TICKRATE_CHANGE, TASmodPackets.TICKRATE_ADVANCE, TASmodPackets.TICKRATE_ZERO }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case TICKRATE_CHANGE: + float tickrate = TASmodBufferBuilder.readFloat(buf); + changeTickrate(tickrate); + break; + case TICKRATE_ADVANCE: + advanceTick(); + break; + case TICKRATE_ZERO: + TickratePauseState state = TASmodBufferBuilder.readTickratePauseState(buf); + + switch (state) { + case PAUSE: + pauseGame(true); + break; + case UNPAUSE: + pauseGame(false); + break; + case TOGGLE: + togglePause(); + default: + break; + } + break; + + default: + throw new PacketNotImplementedException(packet, this.getClass(), Side.SERVER); + } + } + } diff --git a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java index 184e91a2..1a88da35 100644 --- a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java +++ b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java @@ -1,8 +1,16 @@ package com.minecrafttas.tasmod.ticksync; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; import net.minecraft.client.Minecraft; @@ -13,10 +21,18 @@ * * @author Pancake */ -public class TickSyncClient { +public class TickSyncClient implements ClientPacketHandler, EventClientTickPost{ public static final AtomicBoolean shouldTick = new AtomicBoolean(true); + private boolean enabled = false; + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[] {TASmodPackets.TICKSYNC}; + } + + /** * Handles incoming tick packets from the server to the client * This will simply tick the client as long as the tick is correct @@ -24,21 +40,36 @@ public class TickSyncClient { * @param uuid Server UUID, null * @param tick Current tick of the server */ - public static void onPacket() { + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) { shouldTick.set(true); } + /** * Called after a client tick. This will send a packet * to the server making it tick * * @param mc Instance of Minecraft */ - public static void clientPostTick(Minecraft mc) { - if (mc.player == null && TASmodClient.packetClient==null) { + @Override + public void onClientTickPost(Minecraft mc) { + if (TASmodClient.client == null || TASmodClient.client.isClosed() || !enabled) { return; } - TASmodClient.packetClient.sendToServer(new TickSyncPacket(mc.player.getGameProfile().getId())); + + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.TICKSYNC)); + } catch (Exception e) { + LOGGER.error("Unable to send packet to server:", e); + } } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncPacket.java b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncPacket.java deleted file mode 100644 index 27347e51..00000000 --- a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncPacket.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.minecrafttas.tasmod.ticksync; - -import java.util.UUID; - -import com.minecrafttas.tasmod.networking.Packet; -import com.minecrafttas.tasmod.networking.PacketSide; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.network.PacketBuffer; - -/** - * Packet for {@linkplain TickSyncServer} - * @author Scribble - * - */ -public class TickSyncPacket implements Packet { - - protected UUID uuid; - - public TickSyncPacket() { - - } - - public TickSyncPacket(UUID id) { - this.uuid = id; - } - - @Override - public void handle(PacketSide side, EntityPlayer player) { - if(side.isServer()) { - TickSyncServer.onPacket(this.uuid); - }else { - TickSyncClient.onPacket(); - } - } - - @Override - public void serialize(PacketBuffer buf) { - if(uuid!=null) - buf.writeUniqueId(uuid); - } - - @Override - public void deserialize(PacketBuffer buf) { - uuid = buf.readUniqueId(); - } - -} diff --git a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java index fc5337db..b206492e 100644 --- a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java +++ b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java @@ -1,11 +1,22 @@ package com.minecrafttas.tasmod.ticksync; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.UUID; +import com.minecrafttas.common.events.EventServer.EventClientCompleteAuthentication; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.events.EventServer.EventServerTickPost; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.networking.TASmodPackets; + +import net.minecraft.server.MinecraftServer; /** * This class manages tick sync @@ -14,9 +25,16 @@ * * @author Pancake */ -public class TickSyncServer { +public class TickSyncServer implements ServerPacketHandler, EventServerTickPost, EventClientCompleteAuthentication { - private static List synchronizedList = Collections.synchronizedList(new ArrayList<>()); + private static List synchronizedList = Collections.synchronizedList(new ArrayList<>()); + + private boolean enabled = true; + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TASmodPackets[]{TASmodPackets.TICKSYNC}; + } /** * Handles incoming tick packets from the client to the server @@ -26,18 +44,22 @@ public class TickSyncServer { * @param uuid Player UUID * @param tick Current tick of the player */ - public static void onPacket(UUID uuid) { + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) { synchronized (synchronizedList) { - if(!synchronizedList.contains(uuid)) { - synchronizedList.add(uuid); + if(!synchronizedList.contains(username)) { + synchronizedList.add(username); } } + if(TASmod.getServerInstance()==null) { // If the server is null, keep the clients ticking + sendToClients(); + } } - public static boolean shouldTick() { + public boolean shouldTick() { synchronized (synchronizedList) { int acknowledged = synchronizedList.size(); - int totalConnections = TASmod.packetServer.getConnections(); + int totalConnections = TASmod.server.getClients().size(); if(acknowledged >= totalConnections) { return true; }else { @@ -46,17 +68,35 @@ public static boolean shouldTick() { } } - /** - * Called after a server tick. This will send a packet - * to all clients making them tick - */ - public static void serverPostTick() { - TASmod.packetServer.sendToAll(new TickSyncPacket()); + public static void clearList() { + synchronizedList.clear(); + } + + @Override + public void onServerTickPost(MinecraftServer server) { + sendToClients(); + } + + @Override + public void onClientCompleteAuthentication(String username) { + sendToClients(); + } + + private void sendToClients() { + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.TICKSYNC)); + } catch (Exception e) { + LOGGER.error("Unable to send packet to all clients:", e); + } if(synchronizedList.size()>0) synchronizedList.clear(); } - public static void clearList() { - synchronizedList.clear(); + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; } } diff --git a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java index 5d06536f..c0fb439f 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java +++ b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java @@ -7,7 +7,7 @@ *

A list of Log4J markers which can be added to logging statements. * *

Simply add the marker as the first argument: - *

TASmod.logger.info({@linkplain LoggerMarkers}.{@link #Event}, "Message");
+ *
LOGGER.info({@linkplain LoggerMarkers}.{@link #Event}, "Message");
* *

You can then turn off log messages by adding a VM option to your run configuration: *

-Dtasmod.marker.event=DENY
diff --git a/src/main/java/com/minecrafttas/tasmod/util/TickScheduler.java b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java similarity index 60% rename from src/main/java/com/minecrafttas/tasmod/util/TickScheduler.java rename to src/main/java/com/minecrafttas/tasmod/util/Scheduler.java index 1df52a54..fd13454b 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/TickScheduler.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java @@ -4,28 +4,28 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** - * Schedules a lambda to be run in the next tick + * A simple scheduling interface * * @author Scribble * */ -public class TickScheduler { +public class Scheduler { - Queue queue = new ConcurrentLinkedQueue<>(); + Queue queue = new ConcurrentLinkedQueue<>(); public void runAllTasks() { - TickTask task; + Task task; while((task = queue.poll()) != null) { task.runTask(); } } - public void add(TickTask task) { + public void add(Task task) { queue.add(task); } @FunctionalInterface - public interface TickTask { + public interface Task { public void runTask(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index 108fa55c..58faea1a 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -1,5 +1,7 @@ package com.minecrafttas.tasmod.virtual; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -7,12 +9,10 @@ import java.util.List; import com.minecrafttas.common.events.EventClient.EventPlayerJoinedClientSide; -import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.events.OpenGuiEvents; -import com.minecrafttas.tasmod.playback.PlaybackController; -import com.minecrafttas.tasmod.playback.PlaybackController.TASstate; -import com.minecrafttas.tasmod.playback.PlaybackController.TickInputContainer; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; import com.minecrafttas.tasmod.util.PointerNormalizer; import net.minecraft.client.Minecraft; @@ -90,7 +90,7 @@ public class VirtualInput implements EventPlayerJoinedClientSide{ * The container where all inputs get stored during recording or stored and * ready to be played back */ - private PlaybackController container = new PlaybackController(); + private PlaybackControllerClient container = new PlaybackControllerClient(); // ===========================Keyboard================================= @@ -130,9 +130,9 @@ public VirtualInput(String fileToLoad) { if (fileToLoad != null) { try { loadInputs(fileToLoad); - OpenGuiEvents.stateWhenOpened = TASstate.PLAYBACK; + container.setStateWhenOpened(TASstate.PLAYBACK); } catch (IOException e) { - TASmod.logger.error("Cannot load inputs from the start of the TAS: {}", e.getMessage()); + LOGGER.error("Cannot load inputs from the start of the TAS: {}", e.getMessage()); } } } @@ -420,7 +420,7 @@ public float getSubtickYaw() { // =====================================Container=========================================== - public PlaybackController getContainer() { + public PlaybackControllerClient getContainer() { return container; } @@ -439,7 +439,7 @@ public void updateContainer() { * * @param container to replace the current one */ - public void setContainer(PlaybackController container) { + public void setContainer(PlaybackControllerClient container) { this.container = container; } @@ -449,11 +449,11 @@ public void setContainer(PlaybackController container) { * Loads and preloads the inputs from the new InputContainer to * {@link #container} * - * Saving a savestate is done via {@linkplain com.minecrafttas.tasmod.playback.PlaybackSerialiser#saveToFileV1(File, PlaybackController)} in {@linkplain com.minecrafttas.tasmod.savestates.client.InputSavestatesHandler#savestate(String)} + * Saving a savestate is done via {@linkplain com.minecrafttas.tasmod.playback.PlaybackSerialiser#saveToFileV1(File, PlaybackControllerClient)} in {@linkplain com.minecrafttas.tasmod.savestates.SavestateHandlerClient#savestate(String)} * * @param savestatecontainer The container that should be loaded. */ - public void loadClientSavestate(PlaybackController savestatecontainer) { + public void loadClientSavestate(PlaybackControllerClient savestatecontainer) { if (container.isPlayingback()) { preloadInput(container, savestatecontainer.size() - 1); // Preloading from the current container and @@ -486,7 +486,7 @@ public void loadClientSavestate(PlaybackController savestatecontainer) { } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } - savestatecontainer.setTASState(TASstate.PLAYBACK); + savestatecontainer.setTASStateClient(TASstate.PLAYBACK); savestatecontainer.setStartLocation(start); container = savestatecontainer; } @@ -506,7 +506,7 @@ public void loadClientSavestate(PlaybackController savestatecontainer) { e.printStackTrace(); } - savestatecontainer.setTASState(TASstate.RECORDING); + savestatecontainer.setTASStateClient(TASstate.RECORDING); savestatecontainer.setStartLocation(start); container = savestatecontainer; // Replace the current container with the savestated container } @@ -520,7 +520,7 @@ public void loadClientSavestate(PlaybackController savestatecontainer) { * @param index The index of the container from which the inputs should be * loaded */ - private void preloadInput(PlaybackController container, int index) { + private void preloadInput(PlaybackControllerClient container, int index) { TickInputContainer tickcontainer = container.get(index); if (tickcontainer != null) { @@ -532,7 +532,7 @@ private void preloadInput(PlaybackController container, int index) { // "currentKeyboard" Minecraft.getMinecraft().runTickMouse(); } else { - TASmod.logger.warn("Can't preload inputs, specified inputs are null!"); + LOGGER.warn("Can't preload inputs, specified inputs are null!"); } } // ================================Load/Save Inputs===================================== diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml index d59c5699..36586568 100644 --- a/src/main/resources/log4j.xml +++ b/src/main/resources/log4j.xml @@ -18,13 +18,29 @@ %style{[%d{HH:mm:ss}]}{blue} %highlight{[%t/%level]}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=green, TRACE=blue} %style{(%logger{1}%notEmpty{/%marker})}{cyan} %highlight{%msg%n}{FATAL=red, ERROR=red, WARN=normal, INFO=normal, DEBUG=normal, TRACE=normal} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/tasmod.mixin.json b/src/main/resources/tasmod.mixin.json index b0e76918..b91afa5c 100644 --- a/src/main/resources/tasmod.mixin.json +++ b/src/main/resources/tasmod.mixin.json @@ -20,8 +20,7 @@ //Events //Fixing forge and vanilla stuff - "fixes.MixinDragonFightManager", - "fixes.MixinNetworkManager" + "fixes.MixinDragonFightManager" ], "client": [ @@ -45,7 +44,6 @@ //Join and leave game event on the client "events.MixinGuiMainMenu", - "events.MixinGuiControls", "events.MixinGuiIngame", //Playbackhooks @@ -61,6 +59,9 @@ //Shields "shields.MixinRenderItem", - "shields.MixinTileEntityItemStackRenderer" + "shields.MixinTileEntityItemStackRenderer", + + //Fixes + "fixes.MixinNetworkManager" ] } \ No newline at end of file diff --git a/src/test/java/common/TestConfiguration.java b/src/test/java/common/TestConfiguration.java index 8e01df55..893fcfe6 100644 --- a/src/test/java/common/TestConfiguration.java +++ b/src/test/java/common/TestConfiguration.java @@ -35,11 +35,17 @@ static void tearDownAfterClass() throws Exception { configPath.delete(); } + /** + * Test if the config is successfully initialized + */ @Test void testIfInitialized() { assertNotNull(config); } + /** + * Test if the default option is correctly set + */ @Test void testDefault() { configPath.delete(); @@ -47,6 +53,9 @@ void testDefault() { assertEquals("", config.get(ConfigOptions.FileToOpen)); } + /** + * Setting a value and recreating the config should result in the value still being set + */ @Test void testSavingAndLoading() { config.set(ConfigOptions.FileToOpen, "Test"); @@ -54,24 +63,36 @@ void testSavingAndLoading() { assertEquals("Test", config.get(ConfigOptions.FileToOpen)); } + /** + * Test if integers can be set + */ @Test void testIntegers() { config.set(ConfigOptions.FileToOpen, 3); assertEquals(3, config.getInt(ConfigOptions.FileToOpen)); } + /** + * Test if booleans can be set + */ @Test void testBooleans() { config.set(ConfigOptions.FileToOpen, true); assertEquals(true, config.getBoolean(ConfigOptions.FileToOpen)); } + /** + * Test if deleting and unsetting a config value works + */ @Test void testDeleteAndContains() { config.delete(ConfigOptions.FileToOpen); assertFalse(config.has(ConfigOptions.FileToOpen)); } + /** + * Test if resetting to default works + */ @Test void resetToDefault() { config.reset(ConfigOptions.FileToOpen); diff --git a/src/test/java/common/server/ByteBufferBuilderTest.java b/src/test/java/common/server/ByteBufferBuilderTest.java new file mode 100644 index 00000000..f602cb7a --- /dev/null +++ b/src/test/java/common/server/ByteBufferBuilderTest.java @@ -0,0 +1,323 @@ +package common.server; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.nio.ByteBuffer; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +import com.minecrafttas.common.events.CompactPacketHandler; +import com.minecrafttas.common.server.ByteBufferBuilder; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.interfaces.PacketID; + +class ByteBufferBuilderTest { + + + private enum TestPacketIDs implements PacketID { + TESTID_1, + TESTID_2, + TESTID_3; + + private Side side; + private CompactPacketHandler lambda; + + private TestPacketIDs() { + } + + private TestPacketIDs(Side side, CompactPacketHandler lambda) { + this.side = side; + this.lambda = lambda; + } + + @Override + public int getID() { + return this.ordinal(); + } + + @Override + public CompactPacketHandler getLambda() { + return this.lambda; + } + + @Override + public Side getSide() { + return this.side; + } + + @Override + public String getName() { + return this.name(); + } + + @Override + public boolean shouldTrace() { + return false; + } + + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting the packetid + */ + @Test + void testId() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + ByteBuffer buf = builder.build(); + buf.position(0); + + assertEquals(0, buf.getInt()); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting the packetid + */ + @Test + void testId2() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_2); + ByteBuffer buf = builder.build(); + buf.position(0); + + assertEquals(1, buf.getInt()); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting the packetid + */ + @Test + void testId3() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_3); + ByteBuffer buf = builder.build(); + buf.position(0); + + assertEquals(2, buf.getInt()); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting an integer + */ + @Test + void testInt() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeInt(1234); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(1234, ByteBufferBuilder.readInt(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a float + */ + @Test + void testFloat() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeFloat(12.2F); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(12.2F, ByteBufferBuilder.readFloat(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a double + */ + @Test + void testDouble() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeDouble(60.9D); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(60.9D, ByteBufferBuilder.readDouble(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a long + */ + @Test + void testLong() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeLong(800815L); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(800815L, ByteBufferBuilder.readLong(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a short + */ + @Test + void testShort() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeShort((short)12); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(12, ByteBufferBuilder.readShort(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a boolean + */ + @Test + void testBoolean() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeBoolean(true); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(true, ByteBufferBuilder.readBoolean(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a boolean + */ + @Test + void testBoolean2() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeBoolean(false); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals(false, ByteBufferBuilder.readBoolean(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a string + */ + @Test + void testString() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeString("Test"); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals("Test", ByteBufferBuilder.readString(buf)); + } + + /** + * Test + */ + @Test + void testByteArray() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeByteArray(new byte[] {1,1,0,0,1,1,0}); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertArrayEquals(new byte[] {1,1,0,0,1,1,0}, ByteBufferBuilder.readByteArray(buf)); + } + + /** + * Test creating a new ByteBuffer from a ByteBufferbuilder and getting a uuid + */ + @Test + void testUUID() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + + builder.writeUUID(UUID.fromString("b8abdafc-5002-40df-ab68-63206ea4c7e8")); + + ByteBuffer buf = builder.build(); + buf.position(4); + + assertEquals("b8abdafc-5002-40df-ab68-63206ea4c7e8", ByteBufferBuilder.readUUID(buf).toString()); + } + + // ==================================== + + /** + * Test creating a clone from an existing ByteBufferBuilder + */ + @Test + void testClone() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + builder.writeInt(1234); + + ByteBufferBuilder clone; + try { + clone = builder.clone(); + } catch (CloneNotSupportedException e) { + fail(e); + return; + } + + ByteBuffer buf = clone.build(); + buf.position(4); + + assertEquals(1234, ByteBufferBuilder.readInt(buf)); + } + + // ===================================== + + /** + * Test an exception for all types if a ByteBufferBuilder is already closed + */ + @Test + void testClosed() { + ByteBufferBuilder builder = new ByteBufferBuilder(TestPacketIDs.TESTID_1); + builder.close(); + builder.close(); + + Exception exception; + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeInt(0); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeDouble(0D); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeFloat(0F); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeLong(0L); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeShort((short)0); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeBoolean(true); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeString(""); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.writeUUID(UUID.randomUUID()); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + exception = assertThrows(IllegalStateException.class, () -> { + builder.build(); + }); + assertEquals("This buffer is already closed", exception.getMessage()); + } +} diff --git a/src/test/java/common/server/ServerTest.java b/src/test/java/common/server/ServerTest.java new file mode 100644 index 00000000..def5dc43 --- /dev/null +++ b/src/test/java/common/server/ServerTest.java @@ -0,0 +1,330 @@ +package common.server; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.nio.ByteBuffer; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import com.minecrafttas.common.events.CompactPacketHandler; +import com.minecrafttas.common.server.ByteBufferBuilder; +import com.minecrafttas.common.server.Client; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.PacketHandlerRegistry; +import com.minecrafttas.common.server.Server; +import com.minecrafttas.common.server.exception.PacketNotImplementedException; +import com.minecrafttas.common.server.exception.WrongSideException; +import com.minecrafttas.common.server.interfaces.ClientPacketHandler; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.common.server.interfaces.ServerPacketHandler; + +/** + * An integration test for the {@link Server} class by setting up a connection. + * Disabled due to gihub actions failing to execute the tests + */ +@Disabled +class ServerTest { + + // The time to live for how long the tests should wait for the asynchronous server + private static int ttl = 1; + + private enum TestPacketIDs implements PacketID { + TEST_INTERFACE_INT, + TEST_INTERFACE_STRING, + TEST_LAMBDA_CLIENT(Side.CLIENT, (buf, clientID) -> { + result = ByteBufferBuilder.readInt(buf); + ServerTest.side = Side.CLIENT; + latch.countDown(); + }), + TEST_LAMBDA_SERVER(Side.SERVER, (buf, clientID) -> { + result = ByteBufferBuilder.readInt(buf); + ServerTest.side = Side.SERVER; + latch.countDown(); + }); + + private Side side; + private CompactPacketHandler lambda; + + private TestPacketIDs() { + } + + private TestPacketIDs(Side side, CompactPacketHandler lambda) { + this.side = side; + this.lambda = lambda; + } + + @Override + public int getID() { + return this.ordinal(); + } + + @Override + public CompactPacketHandler getLambda() { + return this.lambda; + } + + @Override + public Side getSide() { + return this.side; + } + + @Override + public String getName() { + return this.name(); + } + + @Override + public boolean shouldTrace() { + return false; + } + + } + + private static Client.Side side = null; + + private static class TestingClass implements ClientPacketHandler, ServerPacketHandler { + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new TestPacketIDs[] { TestPacketIDs.TEST_INTERFACE_INT, TestPacketIDs.TEST_INTERFACE_STRING }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TestPacketIDs packet = (TestPacketIDs) id; + switch (packet) { + case TEST_INTERFACE_INT: + result = ByteBufferBuilder.readInt(buf); + side = Side.SERVER; + latch.countDown(); + break; + case TEST_INTERFACE_STRING: + result2 = ByteBufferBuilder.readString(buf); + side = Side.SERVER; + latch.countDown(); + break; + default: + throw new PacketNotImplementedException(id, this.getClass(), Side.SERVER); + } + } + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TestPacketIDs packet = (TestPacketIDs) id; + switch (packet) { + case TEST_INTERFACE_INT: + result = ByteBufferBuilder.readInt(buf); + side = Side.CLIENT; + latch.countDown(); + break; + default: + throw new PacketNotImplementedException(id, this.getClass(), Side.CLIENT); + } + } + + } + + private static Server server; + private static Client client; + + private static Integer result = null; + private static String result2 = null; + + private static TestingClass clazz = new TestingClass(); + + /** + * Setting up a local connection between client and server + * @throws Exception + */ + @BeforeAll + static void setUpBeforeClass() throws Exception { + try { + server = new Server(25566, TestPacketIDs.values()); + } catch (Exception e) { + e.printStackTrace(); + } + + try { + client = new Client("127.0.0.1", 25566, TestPacketIDs.values(), "TASBot", true); + } catch (Exception e) { + e.printStackTrace(); + } + PacketHandlerRegistry.register(clazz); + } + + @AfterAll + static void tearDownAfterClass() throws Exception { + PacketHandlerRegistry.unregister(clazz); + } + + private static CountDownLatch latch; + + @BeforeEach + void setUp() throws Exception { + latch = new CountDownLatch(1); + } + + @AfterEach + void tearDown() throws Exception { + side = null; + result = null; + } + + /** + * Test sending an int packet to {@link TestingClass#onServerPacket(PacketID, ByteBuffer, UUID)} + */ + @Test + void testSendToServerInterface() { + try { + client.send(new ByteBufferBuilder(TestPacketIDs.TEST_INTERFACE_INT).writeInt(1)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(ttl, result); + assertEquals(Client.Side.SERVER, side); + } + + /** + * Test sending an int packet to {@link TestingClass#onClientPacket(PacketID, ByteBuffer, UUID)} to all clients currently connected + */ + @Test + void testSendToAllClientsInterface() { + try { + server.sendToAll(new ByteBufferBuilder(TestPacketIDs.TEST_INTERFACE_INT).writeInt(2)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(2, result); + assertEquals(Client.Side.CLIENT, side); + } + + /** + * Test sending an int packet to {@link TestingClass#onClientPacket(PacketID, ByteBuffer, UUID)} to only one client + */ + @Test + void testSendToClientInterface() { + try { + server.sendTo("TASBot", new ByteBufferBuilder(TestPacketIDs.TEST_INTERFACE_INT).writeInt(3)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(3, result); + assertEquals(Client.Side.CLIENT, side); + } + + /** + * Test sending an string packet to {@link TestingClass#onClientPacket(PacketID, ByteBuffer, UUID)} to only one client + */ + @Test + void testSendToServerInterface2() { + try { + client.send(new ByteBufferBuilder(TestPacketIDs.TEST_INTERFACE_STRING).writeString("TEST")); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals("TEST", result2); + assertEquals(Client.Side.SERVER, side); + } + + // ============================ Lambda + + /** + * Test sending an int packet to {@link TestPacketIDs#TEST_LAMBDA_SERVER} + */ + @Test + void testSendToServerLambda() { + try { + client.send(new ByteBufferBuilder(TestPacketIDs.TEST_LAMBDA_SERVER).writeInt(4)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(4, result); + assertEquals(Client.Side.SERVER, side); + } + + /** + * Test sending an int packet to {@link TestPacketIDs#TEST_LAMBDA_CLIENT} to all clients + */ + @Test + void testSendToAllClientsLambda() { + try { + server.sendToAll(new ByteBufferBuilder(TestPacketIDs.TEST_LAMBDA_CLIENT).writeInt(5)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(5, result); + assertEquals(Client.Side.CLIENT, side); + } + + /** + * Test sending an int packet to {@link TestPacketIDs#TEST_LAMBDA_CLIENT} to one client + */ + @Test + void testSendToClientLambda() { + try { + server.sendTo("TASBot", new ByteBufferBuilder(TestPacketIDs.TEST_LAMBDA_CLIENT).writeInt(6)); + } catch (Exception e) { + fail(e); + return; + } + try { + latch.await(ttl, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e); + return; + } + assertEquals(6, result); + assertEquals(Client.Side.CLIENT, side); + } +} diff --git a/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java b/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java new file mode 100644 index 00000000..273bcfc0 --- /dev/null +++ b/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java @@ -0,0 +1,115 @@ +package tasmod.networking; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +import com.minecrafttas.common.events.CompactPacketHandler; +import com.minecrafttas.common.server.Client.Side; +import com.minecrafttas.common.server.interfaces.PacketID; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; + +import net.minecraft.nbt.NBTTagCompound; + +class TASmodByteBufferBuilderTest { + + private enum TestPacketIDs implements PacketID { + TESTID_1; + + private Side side; + private CompactPacketHandler lambda; + + private TestPacketIDs() { + } + + private TestPacketIDs(Side side, CompactPacketHandler lambda) { + this.side = side; + this.lambda = lambda; + } + + @Override + public int getID() { + return this.ordinal(); + } + + @Override + public CompactPacketHandler getLambda() { + return this.lambda; + } + + @Override + public Side getSide() { + return this.side; + } + + @Override + public String getName() { + return this.name(); + } + + @Override + public boolean shouldTrace() { + return false; + } + + } + + /** + * Test if NBTTagCompounds get correctly stored in a ByteBuffer + */ + @Test + void testNBT() { + + NBTTagCompound tag = new NBTTagCompound(); + NBTTagCompound tag2 = new NBTTagCompound(); + + tag.setString("String", "What"); + tag.setShort("Short", (short) 3); + tag.setLong("Long", 8008132L); + tag.setInteger("Int", -5); + tag.setIntArray("IntArray", new int[] {1, 2, 3}); + tag.setDouble("Double", 1.2D); + tag.setByte("Byte", (byte) 1); + tag.setUniqueId("UUID", UUID.fromString("b8abdafc-5002-40df-ab68-63206ea4c7e8")); + tag.setFloat("Float", 1.0F); + tag.setBoolean("Boolean", true); + tag.setByteArray("ByteArray", new byte[] {1, 0, 0}); + + tag2.setTag("Data", tag); + + TASmodBufferBuilder bufferBuilder = new TASmodBufferBuilder(TestPacketIDs.TESTID_1).writeNBTTagCompound(tag2); + + ByteBuffer buf = bufferBuilder.build(); + + buf.position(4); + + NBTTagCompound tag3 = null; + try { + tag3 = TASmodBufferBuilder.readNBTTagCompound(buf); + } catch (IOException e) { + fail(e); + return; + } + + NBTTagCompound tag4 = tag3.getCompoundTag("Data"); + + assertEquals("What", tag4.getString("String")); + assertEquals((short)3, tag4.getShort("Short")); + assertEquals(8008132L, tag4.getLong("Long")); + assertEquals(-5, tag4.getInteger("Int")); + assertArrayEquals(new int[] {1, 2, 3}, tag4.getIntArray("IntArray")); + assertEquals(1.2D, tag4.getDouble("Double")); + assertEquals((byte) 1, tag4.getByte("Byte")); + assertEquals(UUID.fromString("b8abdafc-5002-40df-ab68-63206ea4c7e8"), tag4.getUniqueId("UUID")); + assertEquals(1.0F, tag4.getFloat("Float")); + assertEquals(true, tag4.getBoolean("Boolean")); + assertArrayEquals(new byte[] {1, 0, 0}, tag4.getByteArray("ByteArray")); + } + +}