diff --git a/AmongUsCapture/GameMemReader.cs b/AmongUsCapture/GameMemReader.cs index 8658ee54..f19a9297 100644 --- a/AmongUsCapture/GameMemReader.cs +++ b/AmongUsCapture/GameMemReader.cs @@ -29,6 +29,9 @@ public static GameMemReader getInstance() public event EventHandler PlayerChanged; + public event EventHandler ChatMessageAdded; + + public Dictionary oldPlayerInfos = new Dictionary(10); // Important: this is making the assumption that player names are unique. They are, but for better tracking of players and to eliminate any ambiguity the keys of this probably need to be the players' network IDs instead public Dictionary newPlayerInfos = new Dictionary(10); // container for new player infos. Also has capacity 10 already assigned so no internal resizing of the data structure is needed @@ -36,6 +39,8 @@ public static GameMemReader getInstance() private GameState oldState = GameState.LOBBY; private bool exileCausesEnd = false; + private int prevChatBubsVersion; + public void RunLoop() { while (true) @@ -77,6 +82,7 @@ public void RunLoop() Console.WriteLine($"({GameAssemblyPtr})"); + prevChatBubsVersion = ProcessMemory.Read(GameAssemblyPtr, 0xD0B25C, 0x5C, 0, 0x28, 0xC, 0x14, 0x10); } } @@ -155,7 +161,7 @@ public void RunLoop() if (this.shouldTransmitState) { shouldTransmitState = false; - GameStateChanged.Invoke(this, new GameStateChangedEventArgs() { NewState = state }); + GameStateChanged?.Invoke(this, new GameStateChangedEventArgs() { NewState = state }); } else if (state != oldState) { GameStateChanged?.Invoke(this, new GameStateChangedEventArgs() { NewState = state }); @@ -190,7 +196,7 @@ public void RunLoop() PlayerInfo oldPlayerInfo = oldPlayerInfos[playerName]; if (!oldPlayerInfo.GetIsDead() && pi.GetIsDead()) // player just died { - PlayerChanged.Invoke(this, new PlayerChangedEventArgs() + PlayerChanged?.Invoke(this, new PlayerChangedEventArgs() { Action = PlayerAction.Died, Name = playerName, @@ -204,7 +210,7 @@ public void RunLoop() { PlayerChanged?.Invoke(this, new PlayerChangedEventArgs() { - Action = PlayerAction.Disconnected, + Action = PlayerAction.ChangedColor, Name = playerName, IsDead = pi.GetIsDead(), Disconnected = pi.GetIsDisconnected(), @@ -212,7 +218,7 @@ public void RunLoop() }); } - if(oldPlayerInfo.GetIsDisconnected() != pi.GetIsDisconnected()) + if(!oldPlayerInfo.GetIsDisconnected() && pi.GetIsDisconnected()) { PlayerChanged?.Invoke(this, new PlayerChangedEventArgs() { @@ -269,11 +275,58 @@ public void RunLoop() } } - //foreach (KeyValuePair kvp in oldPlayerInfos) - //{ - // PlayerInfo pi = kvp.Value; - // Console.WriteLine($"Player ID {pi.PlayerId}; Name: {ProcessMemory.ReadString((IntPtr)pi.PlayerName)}; Color: {pi.ColorId}; Dead: " + ((pi.IsDead > 0) ? "yes" : "no")); - //} + IntPtr chatBubblesPtr = ProcessMemory.Read(GameAssemblyPtr, 0xD0B25C, 0x5C, 0, 0x28, 0xC, 0x14); + int poolSize = 20; // = ProcessMemory.Read(GameAssemblyPtr, 0xD0B25C, 0x5C, 0, 0x28, 0xC, 0xC) + int numChatBubbles = ProcessMemory.Read(chatBubblesPtr, 0xC); + int chatBubsVersion = ProcessMemory.Read(chatBubblesPtr, 0x10); + IntPtr chatBubblesAddr = ProcessMemory.Read(chatBubblesPtr, 0x8) + 0x10; + IntPtr[] chatBubblePtrs = ProcessMemory.ReadArray(chatBubblesAddr, numChatBubbles); + + int newMsgs = 0; + + if (chatBubsVersion > prevChatBubsVersion) // new message has been sent + { + if (chatBubsVersion > poolSize) // increments are twofold (push to and pop from pool) + { + if (prevChatBubsVersion > poolSize) + { + newMsgs = (chatBubsVersion - prevChatBubsVersion) >> 1; + } + else + { + newMsgs = (poolSize - prevChatBubsVersion) + ((chatBubsVersion - poolSize) >> 1); + } + } + else // single increments + { + newMsgs = chatBubsVersion - prevChatBubsVersion; + } + } + else if (chatBubsVersion < prevChatBubsVersion) // reset + { + if (chatBubsVersion > poolSize) // increments are twofold (push to and pop from pool) + { + newMsgs = poolSize + ((chatBubsVersion - poolSize) >> 1); + } + else // single increments + { + newMsgs = chatBubsVersion; + } + } + + prevChatBubsVersion = chatBubsVersion; + + for (int i = numChatBubbles - newMsgs; i < numChatBubbles; i++) + { + string msgText = ProcessMemory.ReadString(ProcessMemory.Read(chatBubblePtrs[i], 0x20, 0x28)); + if (msgText.Length == 0) continue; + string msgSender = ProcessMemory.ReadString(ProcessMemory.Read(chatBubblePtrs[i], 0x1C, 0x28)); + ChatMessageAdded?.Invoke(this, new ChatMessageEventArgs() + { + Sender = msgSender, + Message = msgText + }); + } Thread.Sleep(250); } @@ -330,4 +383,10 @@ public class PlayerChangedEventArgs : EventArgs public bool Disconnected { get; set; } public PlayerColor Color { get; set; } } + + public class ChatMessageEventArgs : EventArgs + { + public string Sender { get; set; } + public string Message { get; set; } + } } diff --git a/AmongUsCapture/ProcessMemory.cs b/AmongUsCapture/ProcessMemory.cs index 19aacd43..47a46f40 100644 --- a/AmongUsCapture/ProcessMemory.cs +++ b/AmongUsCapture/ProcessMemory.cs @@ -99,6 +99,17 @@ public static string ReadString(IntPtr address) return System.Text.Encoding.Unicode.GetString(rawString); } + public static IntPtr[] ReadArray(IntPtr address, int size) + { + byte[] bytes = Read(address, size * 4); + IntPtr[] ints = new IntPtr[size]; + for (int i = 0; i < size; i++) + { + ints[i] = (IntPtr) BitConverter.ToUInt32(bytes, i * 4); + } + return ints; + } + private static byte[] Read(IntPtr address, int numBytes) { byte[] buffer = new byte[numBytes]; diff --git a/AmongUsCapture/Program.cs b/AmongUsCapture/Program.cs index 79fd5584..a987412a 100644 --- a/AmongUsCapture/Program.cs +++ b/AmongUsCapture/Program.cs @@ -11,7 +11,7 @@ namespace AmongUsCapture { static class Program { - private static bool debugGui = true; + private static bool debugGui = false; public static ConsoleInterface conInterface = null; /// /// The main entry point for the application. diff --git a/AmongUsCapture/UserForm.cs b/AmongUsCapture/UserForm.cs index 05887fd6..43a95726 100644 --- a/AmongUsCapture/UserForm.cs +++ b/AmongUsCapture/UserForm.cs @@ -19,12 +19,19 @@ public UserForm(ClientSocket sock) InitializeComponent(); GameMemReader.getInstance().GameStateChanged += GameStateChangedHandler; GameMemReader.getInstance().PlayerChanged += UserForm_PlayerChanged; + GameMemReader.getInstance().ChatMessageAdded += OnChatMessageAdded; if(DarkTheme()) { EnableDarkTheme(); } } + + private void OnChatMessageAdded(object sender, ChatMessageEventArgs e) + { + WriteLineToConsole($"[CHAT] {e.Sender}: {e.Message}"); + } + private bool DarkTheme() { bool is_dark_mode = false;