using System; using System.Collections.Generic; using System.Text; using D2Sniffer; using System.Diagnostics; using System.Net.Sockets; namespace data { public class Bot { //fields BotConfig myBotConfig; //configuration for current bot instance static PacketSniffer myPacketSniffer; //d2Smell sniffer instance TcpClient myBnetServerClient, myRealmServerClient,myGameServerClient;//connections to battle.net servers static System.Net.IPAddress RealmServerIp; //Used as storage to connect to realmserver after the response from BNLS is received ushort CurrentGameNum = 0; //used in CreateGame packet, increment by 0x02 each game uint ClientToken, ServerToken; //Tokens used to Hash data at run-time //TODO change this when implementing the cdkey files and store the damn index cuz appsettings are so retarded... int KeyListIndex = 0; //keep track of the current used cdkey string RealmServerString, BNCSUniqueName, CurrentGameName, CurrentGamePassword = "sadsapog"; //game related + internal stuff bool LoginDone = false, InGame = false, FailedToJoin = false; //used in the Creat/Join game proc byte[] RSLogon; //a packet that is built in a function but sent in another one. int RunCreationWaitDelay = 5000; //the delay between each game creation attemps public int TotalRuns = 0, TotalCompletedRuns = 0; //runs state: TotalCompletedRuns/TotalRuns public RealmServer.JoinGameResult LastJoinResult = RealmServer.JoinGameResult.Success; public bool NeedIPReset = false; //this is not yet set private bool FatalDebugError = false; //replace this eventually; (or I might keep it) /// /// Ctor /// /// a BotConfig object containing all revelant information to run the bot public Bot(BotConfig botConfig) { this.myBotConfig = botConfig; //keep this } int TotalTimeOutGame = 0; /// /// Will perform a hard-coded number of runs. Then return to change cdkey /// Caution: Some runs use 2 games but are considered being only 1run (when the bot use ComeBack if 2000 received packets is reached) /// So be carefull since if you set it to swap cdkey every 20runs it can create up to 40 games /// public void doRuns() { this.NeedIPReset = false; //myPacketSniffer.OnConnectionChange += new ConnectionChangeHandler(myPacketSniffer_OnConnectionChange); CurrentGameNum = 0; //increment by 2 at each game creation Game.PacketDispatcher myPacketDispatcher = new data.Game.PacketDispatcher(); myPacketSniffer.AddListener(D2Packets.PacketOrigin.GameServer, new PacketEventHandler(myPacketDispatcher.onPacketArrival)); int InitialTicks = 0; Test myTest = new Test(myBotConfig, false); //for (int i = 0; i < 10; i++) int i = 0; while (true) { if (i > 0) { //Disconnect_BnetServer(); //Disconnect_RealmServer(); onRunDone(); } i++; //if (this.NeedIPReset) // return; Game.GameInfo.RST = false; CurrentGameNum += 2; this.FailedToJoin = false; this.InGame = false; Random rnd = new Random(); Sleep(this.RunCreationWaitDelay); Game.Belt.Init(); CurrentGameName = rnd.Next(1000000, 99999999).ToString(); CreateGame(CurrentGameNum, CurrentGameName, CurrentGamePassword, DifficultyType.Nightmare, ""); //JoinGame(1, "Dkk3", "Q"); int Timeout2 = 0; //while (!this.FatalDebugError && !this.InGame) while (!this.InGame) { System.Threading.Thread.Sleep(1); Timeout2++; if (Timeout2 > 5000) { if (TotalTimeOutGame > 20) return; Sleep(5000); TotalTimeOutGame++; Logger.Log("Bot(): Create game timed out"); break; ; } if (this.FailedToJoin) { //Sleep(30000); Sleep(5000); this.FailedToJoin = false; break; } } if (!this.InGame) { //onRunDone(); continue; } TotalTimeOutGame = 0; InitialTicks = Environment.TickCount; //Sleep(100); //if (Game.GameInfo.RST) //{ // Logger.Log("RST, create new game"); // continue; //} bool RunSuccess = false; try { RunSuccess = myTest.DoRun(ref myGameServerClient); } catch { this.TotalRuns += 1; myTest.onRunDone(); continue; } if (RunSuccess) this.TotalCompletedRuns += 1; this.TotalRuns += 1; myTest.onRunDone(); //go back to the same game in case we did not finish the run under 2000 rcv packets if (!RunSuccess && Game.GameInfo.Catacomb2Path.Count > 0 && Game.GameInfo.Catacomb3Path.Count == 0) { onRunDone(); Game.Belt.Init(); Game.GameInfo.RST = false; CurrentGameNum += 2; this.FailedToJoin = false; this.InGame = false; LastJoinResult = RealmServer.JoinGameResult.Success; Sleep(2000); for (int ia = 0; ia < 3; ia++) { if (ia > 0) Sleep(this.RunCreationWaitDelay); JoinGame(this.CurrentGameNum, this.CurrentGameName, this.CurrentGamePassword); for (int tt = 0; tt < 1000; tt++) { Sleep(1); if (this.InGame) break; if (this.FailedToJoin) continue; } if (this.InGame) break; if (LastJoinResult == RealmServer.JoinGameResult.GameDoesNotExist) break; } //Sleep(1000); if (!this.InGame) continue; try { if (myTest.ComeBack(ref myGameServerClient)) this.TotalCompletedRuns += 1; } catch { } } //go back to the same game in case we did not finish the run under 2000 rcv packets if (!RunSuccess && Game.GameInfo.Catacomb2Path.Count > 0 && Game.GameInfo.Catacomb3Path.Count > 0) { onRunDone(); Game.Belt.Init(); Game.GameInfo.RST = false; CurrentGameNum += 2; this.FailedToJoin = false; this.InGame = false; LastJoinResult = RealmServer.JoinGameResult.Success; Sleep(2000); for (int ia = 0; ia < 3; ia++) { if (ia > 0) Sleep(this.RunCreationWaitDelay); JoinGame(this.CurrentGameNum, this.CurrentGameName, this.CurrentGamePassword); for (int tt = 0; tt < 1000; tt++) { Sleep(1); if (this.InGame) break ; //break; if (this.FailedToJoin) continue; } if (this.InGame) break; if (LastJoinResult == RealmServer.JoinGameResult.GameDoesNotExist) break; } //Sleep(1000); if (!this.InGame) continue; try { if (myTest.ComeBack(ref myGameServerClient)) this.TotalCompletedRuns += 1; } catch { } } Logger.Log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); Logger.Log("Total completed runs: " + this.TotalCompletedRuns + " on " + this.TotalRuns); Logger.Log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); if (FatalDebugError) { Logger.Log("Fatal error, trying to reconnect to bnet"); onRunDone(); //continue; } } } public void CheckCdKeys() { if (!System.IO.File.Exists(Environment.CurrentDirectory + "\\system\\State\\" + this.myBotConfig._CharacterName)) System.IO.File.Create(Environment.CurrentDirectory + "\\system\\State\\" + this.myBotConfig._CharacterName).Close(); System.IO.StreamReader sr = new System.IO.StreamReader(Environment.CurrentDirectory + "\\system\\State\\" + this.myBotConfig._CharacterName); string line = sr.ReadLine(); Logger.Log("CHECK CD KEYS: LINE = " + line); if (line == null || line == "") { this.KeyListIndex = 0; } else { this.KeyListIndex = Convert.ToInt32(line) + 1; } sr.Close(); sr.Dispose(); if (this.KeyListIndex > this.myBotConfig._ClassicKeyList.Length - 1) this.KeyListIndex = 0; System.IO.StreamWriter sw = new System.IO.StreamWriter(Environment.CurrentDirectory + "\\system\\State\\" + this.myBotConfig._CharacterName, false); sw.Write(this.KeyListIndex); sw.Close(); sw.Dispose(); } /// /// will simply try to disconnect from the gameserver /// public void onRunDone() { Sleep(100); byte[] GCExitGame = GameClient.ExitGame.Build(); try { myGameServerClient.GetStream().Write(GCExitGame, 0, GCExitGame.Length); } catch {} Sleep(100); try { myGameServerClient.Client.Shutdown(SocketShutdown.Both); } catch { } Sleep(100); try { myGameServerClient.Client.Close(); } catch { } Sleep(100); myGameServerClient = null; Sleep(100); } /// /// Used to connect to battle.net /// /// Somewhat redundant I can probably remove this (?) /// success or not public bool ConnectToBattleNet(BotConfig botConfig) { CheckCdKeys(); Logger.Log("Connecting to battle.net..."); this.FatalDebugError = false; this.LoginDone = false; this.myBotConfig = botConfig; myBnetServerClient = new TcpClient(this.GetServerString(this.myBotConfig._Server), 6112); myBnetServerClient.LingerState = new LingerOption(true, 0); if (myPacketSniffer != null) { myPacketSniffer.Dispose(); myPacketSniffer = null; } myPacketSniffer = new PacketSniffer(); myPacketSniffer.AddListener(D2Packets.PacketOrigin.BattleNetServer, new PacketEventHandler(this.onBnetPacketReceived)); myPacketSniffer.AddListener(D2Packets.PacketOrigin.RealmServer, new PacketEventHandler(this.onRealmPacketReceived)); myPacketSniffer.StartSniffing(Process.GetCurrentProcess().Id, myBotConfig._PcapName); try { myBnetServerClient.GetStream().WriteByte(0x01); myBnetServerClient.GetStream().Write(new BC.ConnectionRequest().Build(), 0, new BC.ConnectionRequest().Build().Length); } catch { this.NeedIPReset = true; return false; } int Timeout = 0; while (!this.FatalDebugError && !this.LoginDone) { System.Threading.Thread.Sleep(1); if (Timeout > 5000) { Logger.Log("Failed to connect to battle.net: Timed out"); this.NeedIPReset = true; return false; } Timeout++; } if (this.FatalDebugError) { Logger.Log("Failed to connect to battle.net"); return false; } this.NeedIPReset = false; Logger.Log("We are now logged onto battle.net"); return true; } /// /// Disconnect from battle.net /// this function should maby check gameserver (?) /// public void Disconnect() { Logger.Log("Disconnecting from battle.net..."); this.onRunDone(); if (myRealmServerClient != null) { try { myRealmServerClient.Client.Shutdown(SocketShutdown.Both); } catch { } Sleep(100); try { myRealmServerClient.Client.Close(); } catch { } Sleep(100); } if (myBnetServerClient != null) { try { myBnetServerClient.Client.Shutdown(SocketShutdown.Both); } catch { } Sleep(100); try { myBnetServerClient.Client.Close(); } catch { } Sleep(100); } myRealmServerClient = null; myBnetServerClient = null; myGameServerClient = null; } void Disconnect_BnetServer() { if (myBnetServerClient != null) { try { myBnetServerClient.Client.Shutdown(SocketShutdown.Both); } catch { } Sleep(100); try { myBnetServerClient.Client.Close(); } catch { } Sleep(100); } } void Disconnect_RealmServer() { this.onRunDone(); if (myRealmServerClient != null) { try { myRealmServerClient.Client.Shutdown(SocketShutdown.Both); } catch { } Sleep(100); try { myRealmServerClient.Client.Close(); } catch { } Sleep(100); } } /// /// Join a game. I need to change the return value to bool, it would be alot easier to use. /// /// /// /// void JoinGame(ushort GameNumber, string GameName, string GamePassword) { Logger.Log("Joining game: " + GameName); byte[] myJoinGame = new data.RC.JoinGame(GameNumber, GameName, GamePassword).GetData(); Sleep(100); myRealmServerClient.GetStream().Write(myJoinGame, 0, myJoinGame.Length); } /// /// Create a game. I need to change the return value to bool, it would be alot easier to use. /// /// /// /// /// /// void CreateGame(ushort GameNumber, string GameName, string GamePassword,DifficultyType type,string GameDescription) { Logger.Log("Creating game: " + GameName); byte[] myCreateGame = new data.RC.CreateGame(GameNumber, type, GameName, GamePassword, GameDescription).GetData(); Sleep(100); myRealmServerClient.GetStream().Write(myCreateGame, 0, myCreateGame.Length); } /// /// BNCS recv packets handler (event) /// /// /// void onBnetPacketReceived(object sender, PacketEventArgs args) { switch (args.Packet.Data[0]) { case (int) D2Packets.BnetServerPacket.BnetPing: BnetServer.BnetPing ping = args.Packet as BnetServer.BnetPing; //Logger.Log("Bnet: Ping? " + ping.Timestamp); uint Timestamp = ping.Timestamp; BC.BasePacket pong = new data.BC.BasePacket(0x25); pong.InsertUInt32(Timestamp); byte[] raw = pong.GetData(); try { myBnetServerClient.GetStream().Write(raw, 0, raw.Length); } catch { } //Logger.Log("Bnet: Pong!"); break; case (int)D2Packets.BnetServerPacket.BnetConnectionResponse: //0x50 //this.KeyListIndex += 1; if (this.KeyListIndex >= myBotConfig._ClassicKeyList.Length) this.KeyListIndex = 0; Logger.Log("BNetConnectionResponse received"); BnetServer.BnetConnectionResponse packet = args.Packet as BnetServer.BnetConnectionResponse; this.ClientToken = (uint)Environment.TickCount; MBNCSUtil.DataReader myReader = new MBNCSUtil.DataReader(packet.Data); myReader.Seek(7); this.ServerToken = myReader.ReadUInt32(); //send 0x51 BC.AuthorizationCheck myAUTH_CHECK = new data.BC.AuthorizationCheck(ClientToken, packet.VersionFileName , packet.VersionFormulae, myBotConfig._GameFiles, myBotConfig._ClassicKeyList[KeyListIndex] , myBotConfig._LodKeyList[KeyListIndex], ServerToken, myBotConfig._OwnerName); Logger.Log("Sending AuthorizationCheck for cdkey #" + KeyListIndex); Sleep(50); myBnetServerClient.GetStream().Write(myAUTH_CHECK.Build(), 0, myAUTH_CHECK.Build().Length); break; case (int)D2Packets.BnetServerPacket.BnetAuthResponse: //0x51 BnetServer.BnetAuthResponse packet2 = args.Packet as BnetServer.BnetAuthResponse; if (packet2.Result == BnetServer.BnetAuthResult.Success) { Logger.Log("Battle.net authorization success"); Logger.Log("Logging onto account: " + myBotConfig._AccountName); Sleep(10); byte[] accinfo = new BC.AccountInfo(myBotConfig._AccountName,myBotConfig._AccountPassword,(int)this.ClientToken,(int)this.ServerToken).Build(); myBnetServerClient.GetStream().Write(accinfo, 0, accinfo.Length); } else { Logger.Log("Could not pass battle.net authorization check. Result: " + packet2.Result.ToString()); if (packet2.Result != BnetServer.BnetAuthResult.CDKeyInUse) { this.NeedIPReset = true; Logger.Log("BAD CDKEY DETECTED AT INDEX " + this.KeyListIndex); }; this.FatalDebugError = true; } break; case (int) D2Packets.BnetServerPacket.BnetLogonResponse: //0x3a BnetServer.BnetLogonResponse packet3 = args.Packet as BnetServer.BnetLogonResponse; if (packet3.Result == BnetServer.BnetLogonResult.Success) { Logger.Log("Account logon success"); //query a list of realms MBNCSUtil.BncsPacket myQueryPacket = new MBNCSUtil.BncsPacket(0x40); myBnetServerClient.GetStream().Write(myQueryPacket.GetData(), 0, myQueryPacket.GetData().Length); } else { Logger.Log("Could not log onto account. Result: " + packet3.Result); this.FatalDebugError=true; } break; case (int) D2Packets.BnetServerPacket.QueryRealmsResponse: //0x40 BnetServer.QueryRealmsResponse packet4 = args.Packet as BnetServer.QueryRealmsResponse; this.RealmServerString = packet4.Realms[0].Name; //send a request for realm server logon byte[] myRealmLogon = new data.BC.RealmLogon(this.RealmServerString,(int) this.ClientToken,(int) this.ServerToken).Build(); Sleep(50); myBnetServerClient.GetStream().Write(myRealmLogon, 0, myRealmLogon.Length); break; case (int) D2Packets.BnetServerPacket.RealmLogonResponse : //0x3e BnetServer.RealmLogonResponse packet5 = args.Packet as BnetServer.RealmLogonResponse; if (packet5.Result == BnetServer.RealmLogonResult.Success) { Logger.Log("Realm logon data received successfully"); RealmServerIp = packet5.RealmServerIP; this.BNCSUniqueName = packet5.Username; myRealmServerClient = new TcpClient(); myRealmServerClient.LingerState = new LingerOption(true, 0); myRealmServerClient.Connect(RealmServerIp, 6112); System.Threading.Thread.Sleep(10); myRealmServerClient.GetStream().WriteByte(0x01); System.Threading.Thread.Sleep(10); byte[] tmpdata = new byte[packet5.Data.Length+1]; tmpdata[0] = 0x01; for (int i = 0; i < packet5.Data.Length; i++) tmpdata[i + 1] = packet5.Data[i]; RS.Logon alogon = new data.RS.Logon(tmpdata); RSLogon = new RC.Logon(alogon).Build(); Sleep(50); myRealmServerClient.GetStream().Write(RSLogon, 0, RSLogon.Length); } else { Logger.Log("Failed to log onto realm server"); this.FatalDebugError = true; } break; default: break; } } /// /// Realmserver recv packets handler (event) /// /// /// void onRealmPacketReceived(object sender, PacketEventArgs args) { switch (args.Packet.Data[0]) { case (int) D2Packets.RealmServerPacket.RealmStartupResponse: //0x01 RealmServer.RealmStartupResponse rsesponce = args.Packet as RealmServer.RealmStartupResponse; if (rsesponce.Result == RealmServer.RealmStartupResult.Success) { Logger.Log("Realm logon success"); byte[] CharLogonPacket = new RC.CharacterLogon(myBotConfig._CharacterName).GetData(); System.Threading.Thread.Sleep(100); myRealmServerClient.GetStream().Write(CharLogonPacket, 0, CharLogonPacket.Length); this.LoginDone = true; } else { Logger.Log("Failed to log onto realm server. result: " + rsesponce.Result.ToString()); if (rsesponce.Result == RealmServer.RealmStartupResult.TemporaryIPBan) this.NeedIPReset = true; this.FatalDebugError = true; } break; case (int)D2Packets.RealmServerPacket.JoinGameResponse: //0x04 RealmServer.JoinGameResponse JoinResponse = args.Packet as RealmServer.JoinGameResponse; Logger.Log("JoinGameResponse received. result: " + JoinResponse.Result.ToString()); if (JoinResponse == null) { Logger.Log("Failed to join game, Packet is null (WTF?)"); this.FailedToJoin = true; return; } LastJoinResult = JoinResponse.Result; if (JoinResponse.Result != RealmServer.JoinGameResult.Success) { Logger.Log("Failed to join game. Reason: " + JoinResponse.Result); this.FailedToJoin = true; if (JoinResponse.Result == RealmServer.JoinGameResult.GameDoesNotExist) this.NeedIPReset = true; return; } Logger.Log("Connecting to GameServer..."); myGameServerClient = new TcpClient(); myGameServerClient.LingerState = new LingerOption(true, 0); if (myGameServerClient.Connected) { return; } myGameServerClient.Connect(JoinResponse.GameServerIP, 4000); for (int c = 0; c < 1000 && !myGameServerClient.Connected; c++) System.Threading.Thread.Sleep(1); if (!myGameServerClient.Connected) { this.FailedToJoin = true; Logger.Log("Failed to connect to gameserver"); return; } BC.NotifyJoin myNotifyJoin = new data.BC.NotifyJoin(CurrentGameName,CurrentGamePassword); byte[] BSNotifyJoin = myNotifyJoin.GetData();//bug here retrieve like packet x10 try { myBnetServerClient.GetStream().Write(BSNotifyJoin, 0, BSNotifyJoin.Length); } catch { this.FailedToJoin = true; return; } Sleep(50); byte[] GCLogon = new GCp.GameLogon(JoinResponse.GameHash, JoinResponse.GameToken , myBotConfig._CharacterClass, myBotConfig._CharacterName).GetData(); try { myGameServerClient.GetStream().Write(GCLogon, 0, GCLogon.Length); } catch { this.FailedToJoin = true; return; } Sleep(50); try { myGameServerClient.GetStream().WriteByte(0x6b); } catch { this.FailedToJoin = true; return; } Sleep(50); try { byte[] TestConnectionPacket = GameClient.SelectSkill.Build(D2Data.SkillType.Telekinesis,D2Data.SkillHand.Right); myGameServerClient.GetStream().Write(TestConnectionPacket,0,TestConnectionPacket.Length); } catch { this.FailedToJoin = true; return; } this.InGame = true; break; case (int)D2Packets.RealmServerPacket.CreateGameResponse: //0x03 RealmServer.CreateGameResponse CreateResponse = args.Packet as RealmServer.CreateGameResponse; if (CreateResponse.Result == RealmServer.CreateGameResult.Success) { JoinGame(CurrentGameNum, CurrentGameName, CurrentGamePassword); } else { Logger.Log("Failed to create game: " + CreateResponse.Result); this.FatalDebugError = true; } break; default: break; } } /// /// Convert a Server enum used in botconfig to a server string used in TcpClient.Connect() /// /// /// string GetServerString(Server server) { switch (server) { case Server.Asia: return "asia.battle.net"; case Server.Europe: return "europe.battle.net"; case Server.UsEast: return "useast.battle.net"; case Server.UsWest: return "uswest.battle.net"; default: throw new ApplicationException("GetServerString() unknown server passed to function"); } } /// /// Non-Blocking sleep method /// /// void Sleep(int delay) { int StartTime = Environment.TickCount; while (true) { System.Threading.Thread.Sleep(1); if (Environment.TickCount - StartTime >= delay) return; } } /// /// This is more for debugging purpose. What is state IdentifyConnection? /// /// /// void myPacketSniffer_OnConnectionChange(object sender, D2Packets.ConnectionChangeEventArgs args) { Logger.Log("----------- CONNECTION CHANGED -----------"); Logger.Log("CONNECTION STATE: " + args.Type); Game.GameInfo.RST = true; } } }