From 7e20e409a890d15d18ff3bb05b1310107241ec6a Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Mon, 7 Sep 2020 03:51:42 +0800 Subject: [PATCH] Add support for creating replay mod capture files (#1246) * Add test replay handler * Fix incorrect built raw packet * Fix incorrect built raw packet * Add filter * Add not working zip lib * Add dotNetZip lib and complete basic function * Update ReplayHandler.cs * Complete Replay handler Without client player handling * Complete replay mod - New ChatBot OnNetworkPacket event * Add auto-backup and command for Replay Mod * Add ReplayMod description to readme * Small naming changes, fix compile error on .NET4.0 * ReplayHandler slight optimizations Use Path.Combine to automatically use Windows '\' or Linux '/' Move re-usable common parts outside the Replay handler Small optimizations in building JSON strings Co-authored-by: ORelio --- MinecraftClient/ChatBot.cs | 33 + MinecraftClient/ChatBots/ReplayCapture.cs | 91 + MinecraftClient/McClient.cs | 33 + MinecraftClient/MinecraftClient.csproj | 37 + MinecraftClient/Protocol/GuidExtensions.cs | 79 + .../Handlers/Compression/DeflateStream.cs | 740 ++++ .../ParallelDeflateOutputStream.cs | 1386 ++++++ .../Handlers/Compression/Zip/ComHelper.cs | 116 + .../Compression/Zip/EncryptionAlgorithm.cs | 135 + .../Handlers/Compression/Zip/Events.cs | 684 +++ .../Handlers/Compression/Zip/Exceptions.cs | 300 ++ .../Zip/ExtractExistingFileAction.cs | 85 + .../Handlers/Compression/Zip/FileSelector.cs | 1608 +++++++ .../Handlers/Compression/Zip/OffsetStream.cs | 114 + .../Handlers/Compression/Zip/Shared.cs | 901 ++++ .../Handlers/Compression/Zip/WinZipAes.cs | 941 ++++ .../Handlers/Compression/Zip/ZipConstants.cs | 51 + .../Handlers/Compression/Zip/ZipCrypto.cs | 455 ++ .../Handlers/Compression/Zip/ZipDirEntry.cs | 381 ++ .../Compression/Zip/ZipEntry.Extract.cs | 1456 ++++++ .../Handlers/Compression/Zip/ZipEntry.Read.cs | 798 ++++ .../Compression/Zip/ZipEntry.Write.cs | 2582 +++++++++++ .../Handlers/Compression/Zip/ZipEntry.cs | 2968 +++++++++++++ .../Compression/Zip/ZipEntrySource.cs | 69 + .../Compression/Zip/ZipErrorAction.cs | 97 + .../Compression/Zip/ZipFile.AddUpdate.cs | 2182 +++++++++ .../Handlers/Compression/Zip/ZipFile.Check.cs | 352 ++ .../Compression/Zip/ZipFile.Events.cs | 1219 +++++ .../Compression/Zip/ZipFile.Extract.cs | 298 ++ .../Handlers/Compression/Zip/ZipFile.Read.cs | 1110 +++++ .../Handlers/Compression/Zip/ZipFile.Save.cs | 964 ++++ .../Zip/ZipFile.SaveSelfExtractor.cs | 1101 +++++ .../Compression/Zip/ZipFile.Selector.cs | 1464 ++++++ .../Handlers/Compression/Zip/ZipFile.cs | 3910 +++++++++++++++++ .../Compression/Zip/ZipFile.x-IEnumerable.cs | 154 + .../Compression/Zip/ZipInputStream.cs | 827 ++++ .../Compression/Zip/ZipOutputStream.cs | 1817 ++++++++ .../Compression/Zip/ZipSegmentedStream.cs | 571 +++ .../Protocol/Handlers/DataTypes.cs | 22 +- .../Protocol/Handlers/Protocol16.cs | 5 + .../Protocol/Handlers/Protocol18.cs | 24 + MinecraftClient/Protocol/IMinecraftCom.cs | 9 + .../Protocol/IMinecraftComHandler.cs | 15 + MinecraftClient/Protocol/ProtocolHandler.cs | 46 + MinecraftClient/Protocol/ReplayHandler.cs | 497 +++ MinecraftClient/Settings.cs | 20 +- MinecraftClient/config/README.md | 6 + 47 files changed, 32732 insertions(+), 21 deletions(-) create mode 100644 MinecraftClient/ChatBots/ReplayCapture.cs create mode 100644 MinecraftClient/Protocol/GuidExtensions.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/DeflateStream.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/ParallelDeflateOutputStream.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ComHelper.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/EncryptionAlgorithm.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/Events.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/Exceptions.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ExtractExistingFileAction.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/FileSelector.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/OffsetStream.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/Shared.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/WinZipAes.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipConstants.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipCrypto.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipDirEntry.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.Extract.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.Read.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.Write.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntrySource.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipErrorAction.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.AddUpdate.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Check.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Events.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Extract.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Read.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Save.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.SaveSelfExtractor.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Selector.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.x-IEnumerable.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipInputStream.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipOutputStream.cs create mode 100644 MinecraftClient/Protocol/Handlers/Compression/Zip/ZipSegmentedStream.cs create mode 100644 MinecraftClient/Protocol/ReplayHandler.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index d4604310..75b22f3a 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -354,6 +354,18 @@ namespace MinecraftClient /// Ptotocol version public virtual void OnEntityMetadata(Entity entity, Dictionary metadata) { } + /// + /// Called when a network packet received or sent + /// + /// + /// You need to enable this event by calling with True before you can use this event + /// + /// Packet ID + /// A copy of Packet Data + /// The packet is login phase or playing phase + /// The packet is received from server or sent by client + public virtual void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) { } + /* =================================================================== */ /* ToolBox - Methods below might be useful while creating your bot. */ /* You should not need to interact with other classes of the program. */ @@ -1286,6 +1298,27 @@ namespace MinecraftClient else return false; } + /// + /// Enable or disable network packet event calling. If you want to capture every packet including login phase, please enable this in + /// + /// + /// Enable this may increase memory usage. + /// + /// + protected void SetNetworkPacketEventEnabled(bool enabled) + { + Handler.SetNetworkPacketCaptureEnabled(enabled); + } + + /// + /// Get the minecraft protcol number currently in use + /// + /// Protcol number + protected int GetProtocolVersion() + { + return Handler.GetProtocolVersion(); + } + /// /// Command runner definition. /// Returned string will be the output of the command diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs new file mode 100644 index 00000000..b8014546 --- /dev/null +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MinecraftClient.Protocol; + +namespace MinecraftClient.ChatBots +{ + /// + /// Record and save replay file that can be used by the Replay mod (https://www.replaymod.com/) + /// + public class ReplayCapture : ChatBot + { + private ReplayHandler replay; + private int backupInterval = 3000; // Unit: second * 10 + private int backupCounter = -1; + + public ReplayCapture(int backupInterval) + { + if (backupInterval != -1) + this.backupInterval = backupInterval * 10; + else this.backupInterval = -1; + } + + public override void Initialize() + { + SetNetworkPacketEventEnabled(true); + replay = new ReplayHandler(GetProtocolVersion()); + replay.MetaData.serverName = GetServerHost() + GetServerPort(); + backupCounter = backupInterval; + + RegisterChatBotCommand("replay", "replay command", Command); + } + + public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) + { + replay.AddPacket(packetID, packetData, isLogin, isInbound); + } + + public override void Update() + { + if (backupInterval > 0 && replay.RecordRunning) + { + if (backupCounter <= 0) + { + replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr"); + backupCounter = backupInterval; + } + else backupCounter--; + } + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + replay.OnShutDown(); + return base.OnDisconnect(reason, message); + } + + public string Command(string cmd, string[] args) + { + try + { + if (replay.RecordRunning) + { + if (args.Length > 0) + { + switch (args[0].ToLower()) + { + case "save": + { + replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); + return "Replay file created."; + } + case "stop": + { + replay.OnShutDown(); + return "Record stopped."; + } + } + } + return "Available commands: save, stop"; + } + else return "Record was stopped. Restart the program to start another record."; + } + catch (Exception e) + { + return e.Message; + } + } + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 06e5bcb6..fc675489 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -84,6 +84,9 @@ namespace MinecraftClient // players latency private Dictionary playersLatency = new Dictionary(); + // ChatBot OnNetworkPacket event + private bool networkPacketCaptureEnabled = false; + public int GetServerPort() { return port; } public string GetServerHost() { return host; } public string GetUsername() { return username; } @@ -98,6 +101,8 @@ namespace MinecraftClient public int GetTotalExperience() { return playerTotalExperience; } public byte GetCurrentSlot() { return CurrentSlot; } public int GetGamemode() { return gamemode; } + public bool GetNetworkPacketCaptureEnabled() { return networkPacketCaptureEnabled; } + public int GetProtocolVersion() { return handler.GetProtocolVersion(); } // get bots list for unloading them by commands public List GetLoadedChatBots() @@ -185,6 +190,7 @@ namespace MinecraftClient if (Settings.Mailer_Enabled) { BotLoad(new ChatBots.Mailer()); } if (Settings.AutoCraft_Enabled) { BotLoad(new AutoCraft(Settings.AutoCraft_configFile)); } if (Settings.AutoDrop_Enabled) { BotLoad(new AutoDrop(Settings.AutoDrop_Mode, Settings.AutoDrop_items)); } + if (Settings.ReplayMod_Enabled) { BotLoad(new ReplayCapture(Settings.ReplayMod_BackupInterval)); } //Add your ChatBot here by uncommenting and adapting //BotLoad(new ChatBots.YourBot()); @@ -757,6 +763,18 @@ namespace MinecraftClient } } + /// + /// Enable or disable network packet event calling. + /// + /// + /// Enable this may increase memory usage. + /// + /// + public void SetNetworkPacketCaptureEnabled(bool enabled) + { + networkPacketCaptureEnabled = enabled; + } + #endregion #region Getters: Retrieve data for use in other methods or ChatBots @@ -1492,6 +1510,21 @@ namespace MinecraftClient } } + /// + /// Called when a network packet received or sent + /// + /// + /// Only called if is set to True + /// + /// Packet ID + /// A copy of Packet Data + /// The packet is login phase or playing phase + /// The packet is received from server or sent by client + public void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) + { + DispatchBotEvent(bot => bot.OnNetworkPacket(packetID, packetData, isLogin, isInbound)); + } + /// /// Called when a server was successfully joined /// diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 930e27ac..4a470974 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -75,6 +75,7 @@ + @@ -151,6 +152,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -172,6 +207,7 @@ + @@ -339,6 +375,7 @@ +