using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Text; using Tamir.IPLib; using D2Sniffer; using D2Data; using D2Packets; namespace Chief { public delegate bool PacketConfirmation(object packet); public class Game { #region Win32 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] private static extern uint MapVirtualKey(uint uCode, uint uMapType); [DllImport("user32.dll")] private static extern bool FlashWindow(IntPtr hwnd, bool bInvert); /* [DllImport("user32.dll")] private static extern bool TranslateMessage([In] ref MSG lpMsg); private struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam; public IntPtr lParam; public uint time; public POINT pt; } [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X; public int Y; public POINT(int x, int y) { this.X = x; this.Y = y; } public static implicit operator System.Drawing.Point(POINT p) { return new System.Drawing.Point(p.X, p.Y); } public static implicit operator POINT(System.Drawing.Point p) { return new POINT(p.X, p.Y); } }*/ #endregion //private OutOfGameState State = OutOfGameState.None; #region Collections public Games Games = new Games(); public Items Items = new Items(); public Objects Objects = new Objects(); public Units Units = new Units(); #endregion public D2Toolkit.D2Instance Diablo; public Hero Hero = new Hero(); private PacketSniffer Sniffer = new PacketSniffer(); public Game() { } public void Attach(Process proc) { this.Diablo = new D2Toolkit.D2Instance(proc.MainWindowHandle); if (!this.Sniffer.StartSniffing(this.Diablo.ProcessID)) throw new Exception("Couldn't start sniffing."); // Realm Server Packets this.Sniffer.AddAsyncListener(RealmServerPacket.GameList, new PacketEventHandler(this.GameList)); // Game Server Packets this.Sniffer.AddAsyncListener(GameServerPacket.AssignPlayer, new PacketEventHandler(this.AssignPlayer)); this.Sniffer.AddAsyncListener(GameServerPacket.AssignNPC, new PacketEventHandler(this.AssignNPC)); this.Sniffer.AddAsyncListener(GameServerPacket.AssignGameObject, new PacketEventHandler(this.AssignGameObject)); this.Sniffer.AddAsyncListener(GameServerPacket.AssignSkill, new PacketEventHandler(this.AssignSkill)); this.Sniffer.AddAsyncListener(GameServerPacket.SetNPCMode, new PacketEventHandler(this.SetNPCMode)); this.Sniffer.AddAsyncListener(GameServerPacket.NPCMove, new PacketEventHandler(this.NPCMove)); this.Sniffer.AddAsyncListener(GameServerPacket.WalkVerify, new PacketEventHandler(this.WalkVerify)); this.Sniffer.AddAsyncListener(GameServerPacket.PlayerReassign, new PacketEventHandler(this.PlayerReassign)); //this.Sniffer.AddAsyncListener(GameServerPacket.WorldItemAction, new PacketEventHandler(this.WorldItemAction)); this.Sniffer.AddAsyncListener(GameServerPacket.OwnedItemAction, new PacketEventHandler(this.OwnedItemAction)); // Game Client Packets this.Sniffer.AddAsyncListener(GameClientPacket.SelectSkill, new PacketEventHandler(this.SelectSkill)); this.Sniffer.AddAsyncListener(GameClientPacket.RequestReassign, new PacketEventHandler(this.RequestReassign)); //this.Sniffer.AddAsyncListener(GameClientPacket.WaypointInteract, new PacketEventHandler(this.WaypointInteract)); } public void Clear() { this.Units.Clear(); this.Objects.Clear(); this.Items.Clear(); this.Games.Clear(); } #region Packet interpreting #region Realm Server private void GameList(object sender, PacketEventArgs args) { RealmServer.GameList gameList = (args.Packet as RealmServer.GameList); this.Games.Add(gameList); } #endregion #region Game Server private void AssignPlayer(object sender, PacketEventArgs args) { GameServer.AssignPlayer packet = args.Packet as GameServer.AssignPlayer; // Grab our bot's character UID, if it's not already found if (this.Hero.UID == 0) { this.Hero.Initialize(packet); } } private void AssignNPC(object sender, PacketEventArgs args) { NPC npc = new NPC(args.Packet as GameServer.AssignNPC); // Add NPC if it doesn't already exist, otherwise just update NPC npcInList = this.Units.Find(npc.UID); if (npcInList != null) npcInList = npc; else this.Units.Add(npc); } private void AssignGameObject(object sender, PacketEventArgs args) { Object theObject = new Object(args.Packet as GameServer.AssignGameObject); // Add Object if it doesn't already exist, otherwise just update Object objectInList = this.Objects.Find(theObject.UID); if (objectInList != null) objectInList = theObject; else this.Objects.Add(theObject); } private void AssignSkill(object sender, PacketEventArgs args) { GameServer.AssignSkill packet = args.Packet as GameServer.AssignSkill; // Grab our bot's character UID, if it's not already found if (this.Hero.UID == packet.UID) { this.Hero.SetSkillSelected(packet.Hand, packet.Skill); } } private void SetNPCMode(object sender, PacketEventArgs args) { uint uid = (args.Packet as GameServer.SetNPCMode).UID; NPC npc = this.Units.Find(uid); if (npc != null) npc.UpdateMode(args.Packet as GameServer.SetNPCMode); } private void WalkVerify(object sender, PacketEventArgs args) { GameServer.WalkVerify packet = args.Packet as GameServer.WalkVerify; this.Hero.X = (uint)packet.X; this.Hero.Y = (uint)packet.Y; } private void PlayerReassign(object sender, PacketEventArgs args) { GameServer.PlayerReassign packet = args.Packet as GameServer.PlayerReassign; this.Hero.X = (uint)packet.X; this.Hero.Y = (uint)packet.Y; } private void NPCMove(object sender, PacketEventArgs args) { NPC npc = this.Units.Find((args.Packet as GameServer.NPCMove).UID); if (npc != null) { npc.UpdatePosition(args.Packet as GameServer.NPCMove); } } private void WorldItemAction(object sender, PacketEventArgs args) { //GameServer.WorldItemAction item = (args.Packet as GameServer.WorldItemAction); Item item = new Item(args.Packet as GameServer.WorldItemAction); if (!this.Items.ContainsKey(item.UID) && item != null) try { this.Items.Add(item.UID, item); } catch (NullReferenceException) { /* TODO: Why do I get this exception? Do some items not have UID's?*/ } } private void OwnedItemAction(object sender, PacketEventArgs args) { Item item = new Item(args.Packet as GameServer.OwnedItemAction); if (!this.Hero.Items.ContainsKey(item.UID)) try { this.Hero.Items.Add(item.UID, item); } catch (NullReferenceException) { /* TODO: Why do I get this exception? Do some items not have UID's?*/ } } #endregion #region Game Client private void SelectSkill(object sender, PacketEventArgs args) { GameClient.SelectSkill packet = args.Packet as GameClient.SelectSkill; this.Hero.SetSkillSelected(packet.Hand, packet.Skill); } private void RequestReassign(object sender, PacketEventArgs args) { GameClient.RequestReassign packet = args.Packet as GameClient.RequestReassign; // Grab our bot's character UID, if it's not already found if (this.Hero.UID == 0) { this.Hero.UID = packet.MeUID; } } private void WaypointInteract(object sender, PacketEventArgs args) { // When we go through a waypoint, we should remove all objects/npcs/etc. so we don't // get any mix ups this.Clear(); } #endregion #endregion #region Packet actions public void Move(int x, int y) { this.Diablo.Sender.SendToGameServer(new GameClient.RunToLocation((ushort)x, (ushort)y).Data); uint myUID = this.Hero.UID; uint toX = (uint)x; uint toY = (uint)y; uint fromX = this.Hero.X; uint fromY = this.Hero.Y; byte[] PlayerMove = new byte[] { 0x0F, // PlayerMove 0x00, // Client Type (always 0x00) (byte)(myUID & 0xFF), (byte)((myUID >> 8) & 0xFF), (byte)((myUID >> 16) & 0xFF), (byte)(myUID >> 24), // Client ID 0x17, // Run/Walk (0x01 for walk, 0x17 for run) (byte)(toX & 0xFF), (byte)((toX >> 8) & 0xFF), (byte)(toY & 0xFF), (byte)((toY >> 8) & 0xFF), // Location to walk to 0x00, // Unknown, always 0x00 (byte)(fromX & 0xFF), (byte)((fromX >> 8) & 0xFF), (byte)(fromY & 0xFF), (byte)((fromY >> 8) & 0xFF) // Current location of the client }; this.Diablo.Sender.SendToGameClient(new GameServer.PlayerMove(PlayerMove).Data); } public void Move(Unit unit) { this.Diablo.Sender.SendToGameServer(new GameClient.RunToLocation((ushort)unit.X, (ushort)unit.Y).Data); this.Diablo.Sender.SendToGameServer(new GameClient.RunToTarget(UnitType.NPC, unit.UID).Data); uint myUID = this.Hero.UID; uint toX = (uint)unit.X; uint toY = (uint)unit.Y; uint fromX = this.Hero.X; uint fromY = this.Hero.Y; byte[] PlayerMove = new byte[] { 0x0F, // PlayerMove 0x00, // Client Type (always 0x00) (byte)(myUID & 0xFF), (byte)((myUID >> 8) & 0xFF), (byte)((myUID >> 16) & 0xFF), (byte)(myUID >> 24), // Client ID 0x17, // Run/Walk (0x01 for walk, 0x17 for run) (byte)(toX & 0xFF), (byte)((toX >> 8) & 0xFF), (byte)(toY & 0xFF), (byte)((toY >> 8) & 0xFF), // Location to walk to 0x00, // Unknown, always 0x00 (byte)(fromX & 0xFF), (byte)((fromX >> 8) & 0xFF), (byte)(fromY & 0xFF), (byte)((fromY >> 8) & 0xFF) // Current location of the client }; this.Diablo.Sender.SendToGameClient(new GameServer.PlayerMove(PlayerMove).Data); } public bool MoveWait(int x, int y) { Move(x, y); PacketConfirmation myDel = delegate(object packet) { if ((packet as GameServer.WalkVerify).X == x && (packet as GameServer.WalkVerify).Y == y) return true; else return false; }; if (!WaitForPacket(GameServerPacket.WalkVerify, myDel, 5000)) return false; return true; } public bool MoveWait(Unit unit) { Move(unit); PacketConfirmation myDel = delegate(object packet) { if ((packet as GameServer.WalkVerify).X == unit.X && (packet as GameServer.WalkVerify).Y == unit.Y) return true; else return false; }; if (!WaitForPacket(GameServerPacket.WalkVerify, myDel, 5000)) return false; return true; } public bool PortalInteract(Object unit) { // If we didn't move on the first try, try 1 more time //if (!MoveWait(unit)) Move(unit); this.Diablo.Sender.SendToGameServer(new GameClient.UnitInteract(UnitType.GameObject, unit.UID).Data); //if (!WaitForPacket(GameServerPacket.PortalInfo, 5000)) // return false; this.Diablo.Sender.SendToGameServer(new GameClient.RequestReassign(UnitType.NPC, this.Hero.UID).Data); return true; } public bool WaypointInteract(Object waypoint, WaypointDestination destination) { Move(waypoint); this.Diablo.Sender.SendToGameServer(new GameClient.UnitInteract(UnitType.GameObject, waypoint.UID).Data); this.Diablo.Sender.SendToGameServer(new GameClient.WaypointInteract(waypoint.UID, destination).Data); this.Diablo.Sender.SendToGameServer(new GameClient.RequestReassign(UnitType.NPC, this.Hero.UID).Data); return false; } public bool TeleportWait(int x, int y) { SelectSkill(SkillHand.Right, SkillType.Teleport); this.CastRightSkill(x, y); if (!WaitForPacket(GameServerPacket.PlayerReassign, 5000)) return false; // Delay so we don't keep teleporting System.Threading.Thread.Sleep(200); return true; } public void Attack(NPC npc, SkillType skill) { SelectSkill(SkillHand.Left, skill); this.Diablo.Sender.SendToGameServer(new GameClient.CastLeftSkill((ushort)npc.X, (ushort)npc.Y).Data); } public bool SelectSkill(SkillHand hand, SkillType type) { if (!this.Hero.IsSkillSelected(hand, type)) { this.Diablo.Sender.SendToGameServer(new GameClient.SelectSkill(type, hand).Data); if (!WaitForPacket(GameServerPacket.AssignSkill, 5000)) return false; } return true; } public void CastLeftSkill(int x, int y) { this.Diablo.Sender.SendToGameServer(new GameClient.CastLeftSkill((ushort)x, (ushort)y).Data); } public void CastRightSkill(int x, int y) { this.Diablo.Sender.SendToGameServer(new GameClient.CastRightSkill((ushort)x, (ushort)y).Data); } public bool SwitchWeaponSlotWait() { this.Diablo.Sender.SendToGameServer(new GameClient.SwitchWeapons().Data); PacketConfirmation myDel = delegate(object packet) { if ((packet as GameServer.OwnedItemAction).Destination == ItemDestination.Equipment) return true; else return false; }; if (!WaitForPacket(GameServerPacket.OwnedItemAction, myDel, 5000)) return false; return true; } public void SendChatMessage(string text) { this.Diablo.Sender.SendToGameServer(new GameClient.SendMessage(GameMessageType.GameMessage, text).Data); } public bool WaitForPacket(RealmServerPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(RealmServerPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } public bool WaitForPacket(RealmClientPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(RealmClientPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } public bool WaitForPacket(GameServerPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(GameServerPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } public bool WaitForPacket(GameClientPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(GameClientPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } public bool WaitForPacket(BnetServerPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(BnetServerPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } public bool WaitForPacket(BnetClientPacket packet, int timeOut) { return WaitForPacket(packet, delegate(object pckt) { return true; }, timeOut); } public bool WaitForPacket(BnetClientPacket packet, PacketConfirmation confirmation, int timeOut) { bool isBusy = true; this.Sniffer.AddAsyncListener(packet, delegate(object sender, PacketEventArgs args) { if (confirmation(args.Packet)) isBusy = false; }); DateTime start = DateTime.Now; while (isBusy) { if (((TimeSpan)DateTime.Now.Subtract(start)).TotalMilliseconds > timeOut) return false; System.Threading.Thread.Sleep(10); } return true; } #endregion #region Out of game /// /// Creates and attaches to Diablo II instance /// /// /// /// /// public bool Launch(string workingPath, string exe, string parameters) { Process tempProcess = new Process(); tempProcess.StartInfo.WorkingDirectory = workingPath; tempProcess.StartInfo.FileName = exe; tempProcess.StartInfo.Arguments = parameters; if (!tempProcess.Start()) return false; // DEBUG -- non-static delay here, maybe? System.Threading.Thread.Sleep(1000); this.Attach(tempProcess); // Assume -skiptobnet return this.WaitForPacket(BnetClientPacket.ExtraWorkResponse, 15000); } public bool Login(string username, string password) { // Type in name/pass this.Diablo.SendKey(System.Windows.Forms.Keys.Tab); this.SendKeys(username); this.Diablo.SendKey(System.Windows.Forms.Keys.Tab); this.SendKeys(password); this.Diablo.SendKey(System.Windows.Forms.Keys.Enter); return this.WaitForPacket(RealmServerPacket.CharacterList, 15000); } public bool CharacterSelect(int slot) { if(slot % 2 == 0) { this.Diablo.SendKey(System.Windows.Forms.Keys.Right); } for (int i = 2; i < slot; i+=2) { this.Diablo.SendKey(System.Windows.Forms.Keys.Down); } this.Diablo.SendKey(System.Windows.Forms.Keys.Enter); return this.WaitForPacket(RealmServerPacket.MessageOfTheDay, 15000); } public bool CreateGame(string name, string password, Difficulty difficulty) { // Create Game this.Diablo.Click(592, 460, Poseur32.ClickType.Left); // Type in name/pass this.SendKeys(name); this.Diablo.SendKey(System.Windows.Forms.Keys.Tab); this.SendKeys(password); // Difficulty Point difficultyRadio = new Point(); switch(difficulty) { case Difficulty.Normal: difficultyRadio = new Point(440, 374); break; case Difficulty.Nightmare: difficultyRadio = new Point(565, 375); break; case Difficulty.Hell: difficultyRadio = new Point(707, 375); break; } this.Diablo.Click(difficultyRadio.X, difficultyRadio.Y, Poseur32.ClickType.Left); // Create the game this.Diablo.Click(681, 419, Poseur32.ClickType.Left); return this.WaitForPacket(D2Packets.GameServerPacket.PlayerReassign, 15000); } public bool JoinGame(string name, string password) { // Type in name/pass this.SendKeys(name); this.Diablo.SendKey(System.Windows.Forms.Keys.Tab); this.SendKeys(password); // Join this.Diablo.Click(678, 417, Poseur32.ClickType.Left); this.Games.Clear(); return this.WaitForPacket(D2Packets.GameServerPacket.PlayerReassign, 15000); } public bool JoinGame(string name) { return this.JoinGame(name, ""); } /// /// Joins a random game, with the most people /// public bool JoinGame() { // Join game this.Diablo.Click(713, 461, Poseur32.ClickType.Left); // Wait for list of games this.WaitForPacket(D2Packets.RealmServerPacket.GameList, 5000); System.Threading.Thread.Sleep(2000); // Select the best game string bestGame = ""; for (int i = 7; i > 0; i--) { foreach (RealmServer.GameList gameList in this.Games) { if (gameList.PlayerCount >= i) { bestGame = gameList.Name; break; } } if (bestGame != "") break; } return this.JoinGame(bestGame); } public void SendKeys(string text) { foreach (char c in text) { this.Diablo.SendKey(c); //System.Threading.Thread.Sleep(5); } } #endregion public void Kill() { this.Diablo.Exit(true); } public void FlashWindow() { FlashWindow(this.Diablo.Process.MainWindowHandle, true); } } /* public enum OutOfGameState { None, Login, CharacterSelect, Chat, CreateGame, JoinGame, InGame } */ public enum Difficulty { Normal, Nightmare, Hell } }