using System; using System.Net; using System.Net.Sockets; using Chief; using ChiefD2.Net; using ChiefD2.Net.Packet; using D2PacketsNew.D2Packets; using MBNCSUtil; using ETUtils; namespace ChiefD2 { public class Client : IClient { #region IClient properties public PacketHandler PacketHandler { get { return this.Socket.PacketHandler; } } public event ClientStateChangeEventHandler LobbyEntered; public event ClientStateChangeEventHandler GameEntered; public event ClientStateChangeEventHandler GameExited; public event ClientStateChangeEventHandler Killed; #endregion #region Properties private D2Socket Socket; private Log DebugLog; private int ClientToken; private int ServerToken; private ushort GameCounter = 0; public BotProfile Profile; #endregion public Client(BotProfile profile) { this.DebugLog = new Log("clientless.log"); this.Profile = profile; if (this.Profile.ClassicKey == null || this.Profile.LordOfDestructionKey == null) throw new Exception("You must enter the CD Keys to use a clientless connection!"); this.Socket = new D2Socket(); //this.Socket.PacketReceived += new UnknownPacketEventHandler(Socket_PacketReceived); //this.Socket.PacketSent += new UnknownPacketEventHandler(Socket_PacketSent); #region Listeners // Logging into bnet //this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.BnetPing, new PacketEventHandler(BnetPing)); this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.BnetConnectionResponse, new PacketEventHandler(BnetConnectionResponse)); this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.BnetAuthResponse, new PacketEventHandler(BnetAuthResponse)); this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.BnetLogonResponse, new PacketEventHandler(BnetLogonResponse)); this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.QueryRealmsResponse, new PacketEventHandler(QueryRealmsResponse)); this.Socket.PacketHandler.AddAsyncListener(BnetServerPacket.RealmLogonResponse, new PacketEventHandler(RealmLogonResponse)); // Realms this.Socket.PacketHandler.AddAsyncListener(RealmServerPacket.RealmStartupResponse, new PacketEventHandler(RealmStartupResponse)); this.Socket.PacketHandler.AddAsyncListener(RealmServerPacket.CharacterLogonResponse, new PacketEventHandler(CharacterLogonResponse)); // Game Server this.Socket.PacketHandler.AddAsyncListener(GameServerPacket.GameLogonSuccess, new PacketEventHandler(GameLogonSuccess)); #endregion this.DebugLog.Append("Debugging started ---------------------------------------------------------"); } #region packet events void Socket_PacketSent(PacketOrigin origin, byte[] data, int bytesTransferred) { string buffer = origin.ToString() + " Packet Sent"; for (int i = 0; i < bytesTransferred; i++) { if (i % 10 == 0) buffer += Environment.NewLine; string hex = ETUtils.ByteConverter.ToHexString(data, i, 1); buffer += hex + " "; } this.DebugLog.Append(buffer + Environment.NewLine); } void Socket_PacketReceived(PacketOrigin origin, byte[] data, int bytesTransferred) { string buffer = origin.ToString() + " Packet Received"; for (int i = 0; i < bytesTransferred; i++) { if (i % 10 == 0) buffer += Environment.NewLine; string hex = ETUtils.ByteConverter.ToHexString(data, i, 1); buffer += hex + " "; } this.DebugLog.Append(buffer + Environment.NewLine); } #endregion #region IClient methods public void Start() { this.Socket.ConnectBS(this.Profile.ServerName); this.DebugLog.Append("Sending connection request"); this.Socket.SendBS(new byte[] { 0x01 }); this.Socket.SendBS(new ChiefD2.Net.Packet.BC.ConnectionRequest().Data); } public void SendBS(byte[] data) { this.Socket.SendBS(data); } public void SendRS(byte[] data) { this.Socket.SendRS(data); } public void SendGS(byte[] data) { this.Socket.SendGS(data); } public void ReceiveGC(byte[] data) { // Doesn't do anything--we're clientless, no reason to receive packets! } public bool CreateGame() { return CreateGame(this.Profile.Difficulty, Util.RandomString(12), Util.RandomString(3)); } public bool CreateGame(D2DataNew.GameDifficulty difficulty) { return CreateGame(difficulty, Util.RandomString(12), Util.RandomString(3)); } public bool CreateGame(string name, string pass) { return CreateGame(this.Profile.Difficulty, name, pass); } public bool CreateGame(D2DataNew.GameDifficulty difficulty, string name, string pass) { // The first character in game name/pass has to be uppercase, dunno why name = name.ToUpper(); pass = pass.ToUpper(); this.GameCounter += 0x02; this.DebugLog.Append("Making game #" + this.GameCounter.ToString() + " " + name + "//" + pass + " for " + difficulty.ToString() + " difficulty"); this.Socket.SendRS( new ChiefD2.Net.Packet.RC.CreateGame( this.GameCounter, difficulty, name, pass, "" ).Data); D2Packet packet = this.Socket.PacketHandler.WaitForPacket(D2PacketsNew.D2Packets.RealmServerPacket.CreateGameResponse, 5000); if (packet == null) return false; D2PacketsNew.RealmServer.CreateGameResponse response = new D2PacketsNew.RealmServer.CreateGameResponse(packet.Data); if (response.Result != D2PacketsNew.RealmServer.CreateGameResult.Success) return false; this.DebugLog.Append("CreateGame success!"); return JoinGame(name, pass); } public bool JoinGame(string name) { return JoinGame(name, ""); } public bool JoinGame(string name, string pass) { this.DebugLog.Append("Joining game #" + this.GameCounter.ToString() + " " + name + "//" + pass); this.Socket.SendRS(new ChiefD2.Net.Packet.RC.JoinGame(this.GameCounter, name, pass).Data); D2Packet packet = this.Socket.PacketHandler.WaitForPacket(D2PacketsNew.D2Packets.RealmServerPacket.JoinGameResponse, 5000); if (packet == null) return false; D2PacketsNew.RealmServer.JoinGameResponse response = new D2PacketsNew.RealmServer.JoinGameResponse(packet.Data); if (response.Result != D2PacketsNew.RealmServer.JoinGameResult.Success) return false; this.DebugLog.Append("JoinGame success!"); this.DebugLog.Append("Connecting to game server: " + response.GameServerIP.ToString()); if (!this.Socket.ConnectGS(response.GameServerIP)) return false; D2Packet requestLogonInfo = this.Socket.PacketHandler.WaitForPacket(D2PacketsNew.D2Packets.GameServerPacket.RequestLogonInfo, 5000); if (requestLogonInfo == null) { this.DebugLog.Append("Never received RequestLogonInfo"); return false; } this.Socket.SendGS( new ChiefD2.Net.Packet.GC.GameLogon( response.GameHash, response.GameToken, this.Profile.CharacterClass, this.Profile.CharacterName ).Data); D2Packet gameLogonSuccess = this.Socket.PacketHandler.WaitForPacket(D2PacketsNew.D2Packets.GameServerPacket.GameLogonSuccess, 5000); if (gameLogonSuccess == null) { this.DebugLog.Append("Never received GameLogonSuccess"); return false; } this.Socket.SendGS(new byte[] { 0x6b }); return true; } public bool LeaveGame() { if (this.Socket.IsInGame) { this.Socket.SendGS(new byte[] { 0x69 }); this.DebugLog.Append("Left game!"); this.Socket.CloseGS(); GameExited(); System.Threading.Thread.Sleep(1000); LobbyEntered(); return true; } return false; } public void Kill() { throw new NotImplementedException(); } #endregion #region Listeners #region BattleNet //private void BnetPing(D2Packet packet) //{ // this.Socket.SendBS(new byte[] { 0 }); //} private void BnetConnectionResponse(D2Packet d2packet) { D2PacketsNew.BnetServer.BnetConnectionResponse packet = new D2PacketsNew.BnetServer.BnetConnectionResponse(d2packet.Data); this.ClientToken = Environment.TickCount; this.ServerToken = packet.ServerToken; this.DebugLog.Append("Sending authorization check"); this.Socket.SendBS( new ChiefD2.Net.Packet.BC.AuthorizationCheck( (uint)this.ClientToken, packet.VersionFileName, packet.VersionFormulae, new string[] { this.Profile.GameExe, this.Profile.BnClientDll, this.Profile.D2ClientDll }, this.Profile.ClassicKey, this.Profile.LordOfDestructionKey, (uint)this.ServerToken, "Chief" ).Data ); } private void BnetAuthResponse(D2Packet d2packet) { D2PacketsNew.BnetServer.BnetAuthResponse packet = new D2PacketsNew.BnetServer.BnetAuthResponse(d2packet.Data); if (packet.Result != D2PacketsNew.BnetServer.BnetAuthResult.Success) { this.DebugLog.Append("Couldn't login: " + packet.Result.ToString()); throw new Exception("Couldn't login: " + packet.Result.ToString()); } this.DebugLog.Append("Logon success!"); this.Socket.SendBS( new ChiefD2.Net.Packet.BC.AccountInfo( this.Profile.Username, this.Profile.Password, this.ClientToken, this.ServerToken ).Data); } private void BnetLogonResponse(D2Packet d2packet) { D2PacketsNew.BnetServer.BnetLogonResponse packet = new D2PacketsNew.BnetServer.BnetLogonResponse(d2packet.Data); this.DebugLog.Append("Getting list of realms"); this.Socket.SendBS(new MBNCSUtil.BncsPacket(0x40).GetData()); } private void QueryRealmsResponse(D2Packet d2packet) { // PS, figured this out later, too lazy to change back :) // Doesn't work for some reason -- NullReference exception... //BnetServer.QueryRealmsResponse packet = new BnetServer.QueryRealmsResponse(d2packet.Data); // taken from the above function uint unknown = BitConverter.ToUInt32(d2packet.Data, 4); uint count = BitConverter.ToUInt32(d2packet.Data, 8); D2PacketsNew.D2Packets.RealmInfo[] realms = new D2PacketsNew.D2Packets.RealmInfo[count]; int offset = 12; for (int i = 0; i < count; i++) { realms[i] = new D2PacketsNew.D2Packets.RealmInfo(d2packet.Data, offset); offset += 6 + realms[i].Name.Length + realms[i].Description.Length; } this.DebugLog.Append("Found realm: " + realms[0].Name); this.DebugLog.Append("Logging onto realm"); this.Socket.SendBS( new ChiefD2.Net.Packet.BC.RealmLogon( realms[0].Name, this.ClientToken, this.ServerToken ).Data); } private void RealmLogonResponse(D2Packet d2packet) { // This also doesn't work, for some reason it skips a byte or something? D2PacketsNew.BnetServer.RealmLogonResponse packet = new D2PacketsNew.BnetServer.RealmLogonResponse(d2packet.Data); if (packet.Result != D2PacketsNew.BnetServer.RealmLogonResult.Success) { this.DebugLog.Append("Couldn't log on to that realm: " + packet.Result.ToString()); throw new Exception("Couldn't log on to that realm: " + packet.Result.ToString()); } this.DebugLog.Append("Logged into realm!"); this.DebugLog.Append("Connecting to realm " + packet.RealmServerIP.ToString()); this.Socket.ConnectRS(packet.RealmServerIP); this.DebugLog.Append("Connected"); this.Socket.SendRS(new byte[] {0x01}); this.DebugLog.Append("Logging onto realm..."); this.Socket.SendRS( new ChiefD2.Net.Packet.RC.Logon( packet.Cookie, packet.Status, packet.MCPChunk1, packet.MCPChunk2, packet.Username ).Data); } #endregion #region Realm Server private void RealmStartupResponse(D2Packet d2packet) { D2PacketsNew.RealmServer.RealmStartupResponse packet = new D2PacketsNew.RealmServer.RealmStartupResponse(d2packet.Data); if (packet.Result != D2PacketsNew.RealmServer.RealmStartupResult.Success) { this.DebugLog.Append("Couldn't connect to that realm: " + packet.Result.ToString()); throw new Exception("Couldn't connect to that realm: " + packet.Result.ToString()); } this.DebugLog.Append("Realm logon success!"); this.DebugLog.Append("Request list of characters, just for shits"); System.Threading.Thread.Sleep(50); this.Socket.SendRS(new byte[] {0x07, 0x00, 0x19, 0x08, 0x00, 0x00, 0x00}); this.Socket.PacketHandler.WaitForPacket(RealmServerPacket.CharacterList, 15000); System.Threading.Thread.Sleep(50); this.DebugLog.Append("Selecting character: " + this.Profile.CharacterName); this.Socket.SendRS(new ChiefD2.Net.Packet.RC.CharacterLogon(this.Profile.CharacterName).Data); } private void CharacterLogonResponse(D2Packet d2packet) { D2PacketsNew.RealmServer.CharacterLogonResponse packet = new D2PacketsNew.RealmServer.CharacterLogonResponse(d2packet.Data); if (packet.Result != D2PacketsNew.RealmServer.RealmCharacterActionResult.Success) { this.DebugLog.Append("Couldn't select character: " + packet.Result.ToString()); throw new Exception("Couldn't select character: " + packet.Result.ToString()); } this.DebugLog.Append("Character selected, entering lobby"); //this.Socket.SendRS(new D2PacketsNew.BnetClient.EnterChatRequest(); //this.Socket.PacketHandler.WaitForPacket(RealmServerPacket.CharacterList, 5000); this.DebugLog.Append("Ready to make games!"); LobbyEntered(); } #endregion #region Game Server private void GameLogonSuccess(D2Packet d2packet) { this.DebugLog.Append("Entered game!"); GameEntered(); } #endregion #endregion } }