Hi, is there any working tutorial on how to create a simple chat bot using VB.NET/VB6?
Also,
Warcraft III
SEND -> SID_AUTH_INFO (0x50)
RECV <- SID_PING (0x25)
RECV <- SID_AUTH_INFO (0x50)
SEND -> SID_PING (0x25) (Optional)
SEND -> SID_AUTH_CHECK (0x51)
RECV <- SID_AUTH_CHECK (0x51)
SEND -> SID_AUTH_ACCOUNTLOGON (0x53)
RECV <- SID_AUTH_ACCOUNTLOGON (0x53)
SEND -> SID_AUTH_ACCOUNTLOGONPROOF (0x54)
RECV <- SID_AUTH_ACCOUNTLOGONPROOF (0x54)
SEND -> SID_NETGAMEPORT (0x45)
SEND -> SID_ENTERCHAT (0x0A)
In order to send the 'SID_AUTH_INFO' packet, for example, do I simply send a buffer with the information? Or do I have to send something like 0x50 first?
There's a packet header format described here (http://www.bnetdocs.com/) for sending messages - yes, you need to send the packet ID as part of the header.
If you're using VB.NET and aren't married to doing the packet processing yourself, you can grab BN# (http://jinxbot.googlecode.com/), which is a library I've been working on (documentation's here (http://www.jinxbot.net/bns/), and tutorial (http://www.jinxbot.net/wiki/index.php?title=How_to_develop_a_Battle.net_client_in_10_minutes)). It's not quite complete but it's very usable for .NET developers. It's not supported for VB6.
Well if I wanted to send SID_AUTH_INFO, then which type of header would I use? 'BNCS', Realm, D2GS, Botnet, BNLS, WC3 Ingame or Storm?
Seince you're connecting to the Battle.Net Chat Server...... I don't know.
Anything SID_ is BNCS
How do I send 'IX86' as a DWORD? Isn't it a string?
Quote from: Ozzapoo on November 09, 2008, 11:53 PM
How do I send 'IX86' as a DWORD? Isn't it a string?
no?
isn't it 'IX86' (single quote marks intended) for a reason?
insert it as you would any other 32 bit value: InsertDWORD('IX86');
InsertDword()?
I'm using VB.Net for this.. =/
I'm not sure what is meant by the single-quotes, so do you care to explain?
Yeah, i was just showing you some example usage.
Also, sorry about the single quotes thing, since you're using VB .NET i'm not sure if single quotes work for numerical constants. try converting the value IX86 to hexidecimal and insert it into your packet as you would a 32 bit number such as 1, or 40.
&H49583836
Andy, is there a way I can get the using VB.NET?
uh... 0x49583836? I don't know how .NET denotes hex.
Nevermind, I figured it out anyway. Uh...
(DWORD) Product language
(DWORD) Local IP for NAT compatibility*
(DWORD) Time zone bias*
(DWORD) Locale ID*
(DWORD) Language ID*
Can these fields really safely be set to zero? I tried send a packet and had all thse fields as 0, but I didn't get a response from Battle.Net. However, when I connected using Stealthbot and captured the packets, some of the fields weren't 0 and it DID receive a response from Battle.net. Here is the captured data for comparison between my packet and the one sent by StealthBot.
EDIT: I tried to match it even more, but Battle.Net still won't give me a response
My Packet
0000 00 17 9a 1d e6 67 00 1d e0 52 ca 73 08 00 45 00 .....g.. .R.s..E.
0010 00 62 6f 7c 40 00 80 06 ed 1c 0a 01 01 03 3f f1 .bo|@... ......?.
0020 53 08 ee 66 17 e0 22 1d 9b f4 5b a2 e3 82 50 18 S..f..". ..[...P.
0030 42 30 c3 a0 00 00 ff 50 3a 00 00 00 00 00 36 38 B0.....P :.....68
0040 58 49 33 52 41 57 16 00 00 00 00 00 00 00 00 00 XI3RAW.. ........
0050 00 00 6c fd ff ff 09 0c 00 00 09 0c 00 00 55 53 ..l..... ......US
0060 41 00 55 6e 69 74 65 64 20 53 74 61 74 65 73 00 A.United States.
SB Packet
0000 00 17 9a 1d e6 67 00 1d e0 52 ca 73 08 00 45 00 .....g.. .R.s..E.
0010 00 62 70 14 40 00 80 06 ec 84 0a 01 01 03 3f f1 .bp.@... ......?.
0020 53 08 ee 73 17 e0 87 b5 09 f4 a6 cf ec c8 50 18 S..s.... ......P.
0030 42 30 9b 88 00 00 ff 50 3a 00 00 00 00 00 36 38 B0.....P :.....68
0040 58 49 33 52 41 57 16 00 00 00 00 00 00 00 00 00 XI3RAW.. ........
0050 00 00 6c fd ff ff 09 0c 00 00 09 0c 00 00 55 53 ..l..... ......US
0060 41 00 55 6e 69 74 65 64 20 53 74 61 74 65 73 00 A.United States.
EDIT: Here's the full info:
http://pastebin.com/f7431517d <-My packet
http://pastebin.com/f57167bd4 <-SB Packet
Did you initiate the connection with a binary byte of 1?
Dim protocolID() As New Byte(1)
protocolID(0) = 1
sck.Send(protocolID)
I'm going to throw this out there - and you can ignore it if you like, but it'll save you a lot of headache. If you're going to push through writing your own binary connection, I'd highly suggest utilizing MBNCSUtil (http://www.jinxbot.net/mbncsutil/). It's written in C#, so it's completely compatible with your VB.NET code, it uses the standards set within the .NET runtime, and it's going to save you headache (for example, for converting those strings/32-bit integer values such as 'IX86'). It's also highly optimized for memory and does all the authentication math, as you opt-in to its features.
At the very least, consider using or porting its DataBuffer (http://www.jinxbot.net/mbncsutil/html/T_MBNCSUtil_DataBuffer.htm) and DataReader (http://www.jinxbot.net/mbncsutil/html/T_MBNCSUtil_DataReader.htm) classes - these will let you create outgoing data and read incoming data packets much more easily than doing the binary operations by hand or by rolling your own. And I guarantee they're a lot more correct than some of the other (http://forum.valhallalegends.com/index.php?topic=7937.0) stuff (http://forum.valhallalegends.com/index.php?topic=4150.0) you might (http://forum.valhallalegends.com/index.php?topic=6860.0) find here (and yes, I'm aware that two of those were my own). It's open-source (http://code.google.com/p/jinxbot/source/browse/#svn/branches/mbncsutil) under the BSD license (http://www.jinxbot.net/wiki/index.php?title=License) (generally).
Do what you will, I'm not trying to advertise a product. I'm just trying to avoid headache for you and for the rest of us. :)
Thanks! I completely forgot to send the first byte >.<
I'll try that. And I'll also try your library if I have any more headaches :P
EDIT: W00T! It worked!
EDIT: o.o The MBNCSUtil is pretty nice, I'm gonna use it :D
EDIT: Uh...How can I get the Client token? Do I have to use BNLS or can MBNCSUtil generate it for me? Oh wait...Can ClientToken be any random value?
Client token is generated by the client.
But I don't get how I'm supposed to generate it =/
Also, in SID_AUTH_CHECK, when It refers to 'EXE Version' and 'EXE Hash', what file is EXE? The hash exe file?
GetTickCount works... or 123456... or anything you want. When I say it's generated by the client, I mean it. It can be any value from 0x00000000 to 0xFFFFFFFF.
And yes. The EXE file. Doesn't MBNCSUtil do that for you?
Alright, I got it, but when I try to get the 'Key Private' using MBNCSUtils, it returns a 10-byte array, but it's supposed to fit in a DWORD?
Also, in SID_AUTH_CHECK, is '(DWORD) [5] Hashed Key Data' supposed to have the values hashed using the 'Broken SHA-1' thing?
Warcraft 3 style CDKeys have a 10 byte private value instead.
The array of 5 dwords in SID_AUTH_CHECK is the resulting 20-byte (suprise!) hash of the cdkey values and the tokens.
Quote from: Ozzapoo on November 11, 2008, 03:05 AM
Alright, I got it, but when I try to get the 'Key Private' using MBNCSUtils, it returns a 10-byte array, but it's supposed to fit in a DWORD?
Also, in SID_AUTH_CHECK, is '(DWORD) [5] Hashed Key Data' supposed to have the values hashed using the 'Broken SHA-1' thing?
You don't actually use the private for anything though. You'd use .Product and .Value1 on the CdKey object.
Yes, hashed key data is the 20-byte hash returned by cdKey.GetHash(clientToken, serverToken) (http://www.jinxbot.net/mbncsutil/html/M_MBNCSUtil_CdKey_GetHash.htm). It does not need to be converted to integers first; you can simply insert the byte array using .InsertByteArray() if you're using the DataBuffer/BncsPacket classes.
So I can just use CdKey.GetHash(ClientToken,ServerToken) and put that result as the [5] DWords?
Yeah,
C#:
CdKey key = new CdKey(variablecontainingkeyasstring);
byte[] hash = key.GetHash(clientToken, serverToken);
...
packet.InsertByteArray(hash);
VB.Net:
Dim key As CdKey = New CdKey(variablecontainingkeyasstring)
Dim hash() As Byte = key.GetHash(clientToken, serverToken)
...
packet.InsertByteArray(hash)
Quote from: Ribose on November 11, 2008, 01:51 PM
Yeah,
C#:
CdKey key = new CdKey(variablecontainingkeyasstring);
byte[] hash = key.GetHash(clientToken, serverToken);
...
packet.InsertByteArray(hash);
VB.Net:
Dim key As CdKey = New CdKey(variablecontainingkeyasstring)
Dim hash As Byte() = key.GetHash(clientToken, serverToken)
...
packet.InsertByteArray(hash)
Actually for VB it'd be:
Dim hash() As Byte = ...
Edited, then.
Wait hold on...
(DWORD) [5] Hashed Key Data
Shouldn't that be 24 bytes? Or is the [5] the length?
Quote from: Ozzapoo on November 12, 2008, 12:30 AM
Wait hold on...
(DWORD) [5] Hashed Key Data
Shouldn't that be 24 bytes? Or is the [5] the length?
A DWORD is four bytes long. 5 is the length of the array. 5 time 4 is 20. Therefore the correct number of bytes is 20.
QuoteThe data that should be hashed for 'Hashed Key Data' is:
Client Token
Server Token
Key Product (from decoded CD key)
Key Public (from decoded CD key)
(DWORD) 0
Key Private (from decoded CD key)
Then why are there six values to hash?
You shove all those into a xSHA-1, and the digest is the 5 dwords you send.
You can have unlimited ammounts of data in a SHA1 hash, and the digest will always be 20 bytes
Arlight....Well I think I got the basics right, but now when I connect it gives me the old product version error -.-
P.S. Hdx, congrats on 1000th post! :D
Logs. And you sure your performing the check revision correctly?
Quote from: Ozzapoo on November 12, 2008, 01:31 AM
Arlight....Well I think I got the basics right, but now when I connect it gives me the old product version error -.-
Um... update your game files and verbyte? :P Old version != invalid version! Old version is very specific.
Quote from: Ozzapoo on November 12, 2008, 01:18 AM
QuoteThe data that should be hashed for 'Hashed Key Data' is:
Client Token
Server Token
Key Product (from decoded CD key)
Key Public (from decoded CD key)
(DWORD) 0
Key Private (from decoded CD key)
Then why are there six values to hash?
These are the values that get fed into the hashing algorithm. The hashing algorithm produces a 20-byte (5 DWORD) value that is inserted into the outgoing data buffer.
If you're using MBNCSUtil, the CdKey.GetHash(clientToken, serverToken) (http://www.jinxbot.net/mbncsutil/html/M_MBNCSUtil_CdKey_GetHash.htm) method handles doing all that legwork for you - you just need to provide the client and server tokens.
>.< I forgot to do all this stuff...Because I must have thought the GetHash() thing included it >.<
QuoteFor Each Key:
(DWORD) Key Length
(DWORD) CD-key's product value
(DWORD) CD-key's public value
(DWORD) Unknown (0)
But yeah....Then I
thought I fixed it but I got Invalid Cd Key instead -.-"
I think it's because I stuffed up again and forgot t include the Unknown value... Well I'm going to try again.
EDIT: I think it's working now! (At least 'CD Key in use' is working...) I'm going to test it a bit more.
EDIT: Yeah, it definitely works. Time to go onto SID_AUTH_ACCOUNTLOGON! :D
EDIT: W00T!!!!!! It's entered chat! =D
EDIT: Uh oh...Problem.
Right now, I have it set up using a System.Net.Sockets.TcpListener object and a NetworkStream object. And it works like this:
Stream.Write(bnetpacket,0,bnetpacket.Length)
Stream.Read(<some variable>,0,<something>)
But in this, it
expects a response to everything. However, I'm stuck on how I'm supposed to make my application respond to every packet that Bnet sends me... I'm not sure how I can detect a packet when Bnet sends it and respond to it. Is there something else that I need to do? Please help >.<
The way that BN# does it is to have a single thread that simply sits on a Read() loop (the thread will block as long as Read() is waiting). Writes are always done on-demand by another thread.
Alright...So will a BackgroundWorker Object do the job?
Aand...I don't quite understand what the 'padding' parameter is in ReadDwordString()..
Quote from: Ozzapoo on November 12, 2008, 01:16 PM
Alright...So will a BackgroundWorker Object do the job?
Aand...I don't quite understand what the 'padding' parameter is in ReadDwordString()..
BackgroundWorker will work fine.
The Padding parameter is a character to strip out in the event that the value isn't four bytes long. For example, clan tags are written as integers that might not have four characters. There was another situation in the protocol, I don't remember where, that used 0x0d as a padding character.
Generally using 0 as the parameter is adequate.
Alright I've pretty much got it. Just a small problem now...The background worker doesn't work quick enough to detect all packets when you do something like '/f list'...Which can send up to 26 packets at once.
My current code for the Sub is as follows:
Sub DetectIncoming()
Dim rec(bnetClient.ReceiveBufferSize) As Byte
Dim bnetStream As NetworkStream = bnetClient.GetStream()
Do
ReDim rec(bnetClient.ReceiveBufferSize)
bnetStream.Read(rec, 0, rec.Length)
If rec(1) = &HF Then
Dim packetreader As New BncsReader(rec)
Dim eventid As Int32 = packetreader.ReadInt32()
Dim userflags As Int32 = packetreader.ReadInt32()
Dim ping As Int32 = packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
Dim username As String = packetreader.ReadCString()
Dim text As String = packetreader.ReadCString()
Select Case eventid
Case &H2
Console.WriteLine("-INFO- User " + username + " has joined the channel.")
Case &H3
Console.WriteLine("-INFO- User " + username + " has left the channel.")
Case &H4
Console.WriteLine("<" + username + "> has whispered: " + text)
Case &H5
Console.WriteLine("<" + username + "> " + text)
Case &H6
Console.WriteLine("-BROADCAST- " + text)
Case &H7
Console.WriteLine("-JOINED CHANNEL- " + text)
Case &H9
Console.WriteLine("User Flags Update: " + text)
Case &HA
Console.WriteLine("-WHISPER SENT-")
Case &HD
Console.WriteLine("Error! Channel Full!")
Case &HE
Console.WriteLine("Error! Channel does not exist!")
Case &HF
Console.WriteLine("Error! Channel is restricted!")
Case &H12
Console.WriteLine("-INFORMATION- " + text)
Case &H13
Console.WriteLine("Error! " + text)
Case &H17
Console.WriteLine("-INFO- Emote Used by " + username)
End Select
ElseIf rec(1) = &H0 Then
SendNull(bnetStream)
End If
Loop
End Sub
What should I do to fix it?
Why are you not splitting the packets based on length? {Remember the BNCS Header 0xFF ID (word)Length}
The problem you're getting multiple packets at once when you do recv.
Also, switch ftw
Quote from: Hdx on November 13, 2008, 01:38 AM
Why are you not splitting the packets based on length? {Remember the BNCS Header 0xFF ID (word)Length}
The problem you're getting multiple packets at once when you do recv.
Also, switch ftw
Is the length always static?
Any suggestions on how I can fix it :(?
What's "switch"?
err its select case in vb
No the length is not always static, hence why its part of the header....
{AE read the 1st byte if its not oxff its not a real bncs packet, read the next one, thats the id, read the next two, thats the length, read the next X where X was the previous word}
Wait, are you saying I should only use the stream to read the first 4 bytes (the header) and then read however much is left? Will it reduce the amount of time needed, as right now it's the ReceivedBufferSize property or something and there are LOTS of null bytes in the output.
QuoteThe problem you're getting multiple packets at once when you do recv.
Wait...You mean if I read too much at a time I might read two packets at once?
EDIT: Had a go at making some changes...But it still screws up and now I have no idea what's wrong.
Sub DetectIncoming()
Dim rec1() As Byte
Dim rec(bnetClient.ReceiveBufferSize) As Byte
Dim header(3) As Byte
Dim bnetStream As NetworkStream = bnetClient.GetStream()
Do
ReDim header(3)
Dim packetsize As Integer
bnetStream.Read(header, 0, 4)
If header(0) = &HFF Then
packetsize = BitConverter.ToInt16(header, 2)
Else
Continue Do
End If
ReDim rec1(packetsize - 4) 'Error: Arithmetic operation resulted in an overflow. <----------------------------ERROR!!
bnetStream.Read(rec1, 0, rec1.Length)
ReDim rec(packetsize)
rec = ConcatenateByteArray(header, rec1)
If rec(1) = &HF Then
Dim packetreader As New BncsReader(rec)
Dim eventid As Int32 = packetreader.ReadInt32()
Dim userflags As Int32 = packetreader.ReadInt32()
Dim ping As Int32 = packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
Dim username As String = packetreader.ReadCString()
Dim text As String = packetreader.ReadCString()
Select Case eventid
Case &H2
Console.WriteLine("-INFO- User " + username + " has joined the channel.")
Case &H3
Console.WriteLine("-INFO- User " + username + " has left the channel.")
Case &H4
Console.WriteLine("<" + username + "> has whispered: " + text)
Case &H5
Console.WriteLine("<" + username + "> " + text)
Case &H6
Console.WriteLine("-BROADCAST- " + text)
Case &H7
Console.WriteLine("-JOINED CHANNEL- " + text)
Case &H9
Console.WriteLine("User Flags Update: " + text)
Case &HA
Console.WriteLine("-WHISPER SENT-")
Case &HD
Console.WriteLine("Error! Channel Full!")
Case &HE
Console.WriteLine("Error! Channel does not exist!")
Case &HF
Console.WriteLine("Error! Channel is restricted!")
Case &H12
Console.WriteLine("-INFORMATION- " + text)
Case &H13
Console.WriteLine("Error! " + text)
Case &H17
Console.WriteLine("-INFO- Emote Used by " + username)
End Select
ElseIf rec(1) = &H0 Then
SendNull(bnetStream)
End If
Loop
End Sub
I made it:
1. Only read the first 4 bytes.
2. If the first byte is no &HFF, then skip the rest of the actions in the loop and go from the start. If it IS &HFF, then it will use BitConverter to get the header size.
3. It will ReDim the 'rec1' array to be PacketSize - 4.
4. It will receive the rest of the packet data.
5. It will concatenate the header and 'rec1', so it
should become an entire packet, into 'rec'.
6. It will continue to do the rest, as it did before.
Pretty much, if what Hdx said is true, I
should have eliminated those problems, but almost every time I try to do '/f list', I get an error saying 'Arithmetic operation resulted in an overflow.' at around the sixth packet (Refer to the comment in the code). And even when I don't (
rarely), it starts off displaying the messages in order, but then it just skips and finishes..... Any help?
The 'packetsize' variable is 0 when the error occurs, which leads me to believe that it isn't set, maybe because the packet header isn't 'read' properly...
EDIT:
Sub DetectIncoming()
Dim rec1() As Byte
Dim rec(bnetClient.ReceiveBufferSize) As Byte
Dim header(3) As Byte
Dim bnetStream As NetworkStream = bnetClient.GetStream()
Do
ReDim header(3)
Dim packetsize As Integer
Dim startbyte As Byte = bnetStream.ReadByte()
Dim header2(2) As Byte
If startbyte = &HFF Then
bnetStream.Read(header2, 0, 3)
header(0) = startbyte
header(1) = header2(0)
header(2) = header2(1)
header(3) = header2(2)
packetsize = BitConverter.ToInt16(header, 2)
Else
Continue Do
End If
ReDim rec1(packetsize - 4)
bnetStream.Read(rec1, 0, rec1.Length)
ReDim rec(packetsize)
rec = ConcatenateByteArray(header, rec1)
If rec(1) = &HF Then
Dim packetreader As New BncsReader(rec)
Dim eventid As Int32 = packetreader.ReadInt32()
Dim userflags As Int32 = packetreader.ReadInt32()
Dim ping As Int32 = packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
Dim username As String = packetreader.ReadCString()
Dim text As String = packetreader.ReadCString()
Select Case eventid
Case &H2
Console.WriteLine("-INFO- User " + username + " has joined the channel.")
Case &H3
Console.WriteLine("-INFO- User " + username + " has left the channel.")
Case &H4
Console.WriteLine("<" + username + "> has whispered: " + text)
Case &H5
Console.WriteLine("<" + username + "> " + text)
Case &H6
Console.WriteLine("-BROADCAST- " + text)
Case &H7
Console.WriteLine("-JOINED CHANNEL- " + text)
Case &H9
Console.WriteLine("User Flags Update: " + text)
Case &HA
Console.WriteLine("-WHISPER SENT-")
Case &HD
Console.WriteLine("Error! Channel Full!")
Case &HE
Console.WriteLine("Error! Channel does not exist!")
Case &HF
Console.WriteLine("Error! Channel is restricted!")
Case &H12
Console.WriteLine("-INFORMATION- " + text)
Case &H13
Console.WriteLine("Error! " + text)
Case &H17
Console.WriteLine("-INFO- Emote Used by " + username)
End Select
ElseIf rec(1) = &H0 Then
SendNull(bnetStream)
End If
Loop
End Sub
That has the error too, except now the 'packetsize' value is -1.
EDIT: Just added 'If packetsize < 1 Then Continue Do'.....But it still skips some packets. All that and I didn't get anywhere in the end -.-
EDIT: OK WTF. I made it say 'w00t' whenever it ran the IF that checked for &HFF and I found some revealing stuff .. -.-"
Quote
-JOINED CHANNEL- The Void
w00t
/f l
w00t
-INFORMATION- Your friends are:
w00t
-INFORMATION- 1: lfuzzyl, offline
w00t
-INFORMATION- 2: rts178, offline
w00t
-INFORMATION- 3: shamanno1, offline
w00t
-INFORMATION- 4: ace)of(clubs, offline
w00t
w00t
-INFORMATION- 6: water_knight, offline
w00t
w00t
-INFORMATION- 8: computer(hairy), offline
w00t
w00t
-INFORMATION- 10: carnifox, offline
w00t
w00t
-INFORMATION- 12: lordkintaro, using Warcraft III The Frozen Throne in a private
channel.
w00t
w00t
-INFORMATION- 14: caesio, offline
w00t
w00t
-INFORMATION- 16: tonton.vfr[1], offline
w00t
w00t
-INFORMATION- 18: tonton.vfr[3], offline
w00t
w00t
-INFORMATION- 20: tonton.vfr[5], offline
w00t
w00t
-INFORMATION- 22: FlipCirca, offline
w00t
w00t
-INFORMATION- 24: TonTonSNORT, offline
Anyone have any ideas? =X
Bedtime for me now =D
My listen loop follows the principle the others have discussed. Here's some adapted code that more pseudocode-ish:
while (IsConnected)
{
// I wrote Receive(). It guarantees a buffer filled with as many bytes as you request and blocks until it comes in,
// or else returns null.
byte[] hdr = Receive(header, 0, 4);
if (hdr == null) return; // disconnected.
byte[] result = null;
ushort length = BitConverter.ToUInt16(hdr, 2);
if (length > 4)
{
byte[] data = AllocateIncomingBuffer(length);
result = Receive(data, 0, unchecked((ushort)(length - 4)));
if (result == null) return; // disconnected.
length = unchecked((ushort)(length - 4));
}
else if (length == 4)
{
// no data to parse; length is just for header.
length = unchecked((ushort)(length - 4));
result = new byte[0];
}
else
{
throw new ProtocolViolationException(Strings.InvalidPacketSizeFromBnet);
}
ParseData parseData = new ParseData(hdr[1], length, result);
Priority priority = DeterminePriority((BncsPacketId)parseData.PacketID);
ParsePacket(priority, parseData);
}
Two things:
// no data to parse; length is just for header.
length = unchecked((ushort)(length - 4));
result = new byte[0];
// no data to parse; length is just for header.
length = 0;
result = null;
Neh?
And Priority priority = DeterminePriority((BncsPacketId)parseData.PacketID);
I'd like to see your DeterminePriority() function. {Just curious}
Quote from: MyndFyre[vL] on November 13, 2008, 10:36 AM
My listen loop follows the principle the others have discussed. Here's some adapted code that more pseudocode-ish:
while (IsConnected)
{
// I wrote Receive(). It guarantees a buffer filled with as many bytes as you request and blocks until it comes in,
// or else returns null.
byte[] hdr = Receive(header, 0, 4);
if (hdr == null) return; // disconnected.
byte[] result = null;
ushort length = BitConverter.ToUInt16(hdr, 2);
if (length > 4)
{
byte[] data = AllocateIncomingBuffer(length);
result = Receive(data, 0, unchecked((ushort)(length - 4)));
if (result == null) return; // disconnected.
length = unchecked((ushort)(length - 4));
}
else if (length == 4)
{
// no data to parse; length is just for header.
length = unchecked((ushort)(length - 4));
result = new byte[0];
}
else
{
throw new ProtocolViolationException(Strings.InvalidPacketSizeFromBnet);
}
ParseData parseData = new ParseData(hdr[1], length, result);
Priority priority = DeterminePriority((BncsPacketId)parseData.PacketID);
ParsePacket(priority, parseData);
}
I still don't really get it...(partially because of some of the functions used)
Could you please explain what the your code does?
while (IsConnected)
{
byte[] hdr = Receive(header, 0, 4); //Reterives 4 bytes from the socket, garentees it's 4 bytes or null
if (hdr == null) return; // The socket was closed
byte[] result = null; //var for the actuall data
ushort length = BitConverter.ToUInt16(hdr, 2); //Gets the length of the packet from the header {hdr[2] | hdr[3] << 8}
if (length > 4){ //If it actually has data
byte[] data = AllocateIncomingBuffer(length); //Allocates 'length' amount of bytes for the data
result = Receive(data, 0, unchecked((ushort)(length - 4))); //Reads length - 4 bytes from the socket
if (result == null) return; // read failed, socket closed
length = unchecked((ushort)(length - 4)); //Data length = length - 4 {length - size of header}
}else if (length == 4){ // packet contains only header {AE: SID_NULL}
length = unchecked((ushort)(length - 4)); //length = 0
result = new byte[0]; //No data
}else{
throw new ProtocolViolationException(Strings.InvalidPacketSizeFromBnet); //OMFG THIS SHOULDNT HAPPEN DONT HAX ME!
}
ParseData parseData = new ParseData(hdr[1], length, result); //Creates a new Incoming packet buffer using the data from above.
Priority priority = DeterminePriority((BncsPacketId)parseData.PacketID); //Gets the priority {So the bot knows which packets to handle first, you don't need to do this}
ParsePacket(priority, parseData); //Passes it off to the handeling sub
}
The main thing you need to do is this:
while (IsConnected)
{
byte[] hdr = Receive(header, 0, 4); //receive 4 bytes
if (hdr == null) return; // disconnected.
byte[] data = null;
ushort length = hdr[2] | hdr[3] << 8;
if (length > 4){
data = Receive(data, 0, length - 4); //Read length - 4 bytes {The body of the packet}
if (data == null) return; // disconnected.
}
ParsePacket(hdr[1], data, length);
}
An extreamly striped down version that should work {well, its psudo code cuz i havent tested but the base ideas are there}
Note that the synax hdr[2] | hdr[3] << 8 might not work in C# or Visual Basic (it would require expansion from byte to a larger data type to do the shift, plus it's just not really clear in the intent: you need to specifically look up operator precedence to be sure that shift operator takes precedence over the bitwise or | operator). Use BitConverter.ToInt16 or BitConverter.ToUInt16 as I did - it's built-in to the system.
Alright thanks, I'll try that.
EDIT: Where did Receive() come from?
EDIT: W00T! It works!!
May be crude, but works fine.
Sub DetectIncoming()
Dim bnetStream As NetworkStream = bnetClient.GetStream()
Do
Dim header(3) As Byte
bnetStream.Read(header, 0, 4)
If header(0) = 0 Then
Continue Do
End If
Dim packetsize As Int16
If header(0) = &HFF Then
packetsize = BitConverter.ToInt16(header, 2)
End If
Dim packet(packetsize - 5) As Byte
If packetsize > 4 Then
bnetStream.Read(packet, 0, packetsize - 4)
End If
Dim rec() As Byte = ConcatenateByteArray(header, packet)
header = Nothing
packet = Nothing
If rec(1) = &HF Then
Dim packetreader As New BncsReader(rec)
Dim eventid As Int32 = packetreader.ReadInt32()
Dim userflags As Int32 = packetreader.ReadInt32()
Dim ping As Int32 = packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
packetreader.ReadInt32()
Dim username As String = packetreader.ReadCString()
Dim text As String = packetreader.ReadCString()
Select Case eventid
Case &H2
Console.WriteLine("-INFO- User " + username + " has joined the channel.")
Case &H3
Console.WriteLine("-INFO- User " + username + " has left the channel.")
Case &H4
Console.WriteLine("<" + username + "> has whispered: " + text)
Case &H5
Console.WriteLine("<" + username + "> " + text)
Case &H6
Console.WriteLine("-BROADCAST- " + text)
Case &H7
Console.WriteLine("-JOINED CHANNEL- " + text)
Case &H9
Console.WriteLine("User Flags Update: " + text)
Case &HA
Console.WriteLine("-WHISPER SENT-")
Case &HD
Console.WriteLine("Error! Channel Full!")
Case &HE
Console.WriteLine("Error! Channel does not exist!")
Case &HF
Console.WriteLine("Error! Channel is restricted!")
Case &H12
Console.WriteLine("-INFORMATION- " + text)
Case &H13
Console.WriteLine("Error! " + text)
Case &H17
Console.WriteLine("-INFO- Emote Used by " + username)
End Select
ElseIf rec(1) = &H0 Then
SendNull(bnetStream)
End If
Loop
End Sub
And also, which packet returns all the people currently in the channel?
EID_SHOWUSER (http://www.bnetdocs.org/?op=packet&pid=307)
Crude, but works i guess, think about it a little more and you can clean it up.
Alright Thanks! But can you give me any ideas on how I can clean it up?
Quote from: Ozzapoo on November 13, 2008, 11:20 PM
Alright thanks, I'll try that.
EDIT: Where did Receive() come from?
I commented about it:
// I wrote Receive(). It guarantees a buffer filled with as many bytes as you request and blocks until it comes in,
// or else returns null.
It's inherited from ConnectionBase (http://www.jinxbot.net/bns/html/T_BNSharp_Net_ConnectionBase.htm) (source (http://code.google.com/p/jinxbot/source/browse/trunk/development/projects/BNSharp/Net/ConnectionBase.cs#235)).
But does it really matter? Read() seems to work fine.
IIRC Read will read data from the socket, but does not guarantee that it is of the length you specify.
For example, if in some fluke the BNCS header comes in two tcp segments, {\xff\x51 in the 1st and \x10\x00 in the second}
Your Read() function will read 2 bytes and then return. So you'd have a header value of: \xff\x51\x00\x00
But, this is just speaking of C, I don't know if VB.net's Read() is diffrent.
Quote from: Hdx on November 14, 2008, 03:33 PM
IIRC Read will read data from the socket, but does not guarantee that it is of the length you specify.
For example, if in some fluke the BNCS header comes in two tcp segments, {\xff\x51 in the 1st and \x10\x00 in the second}
Your Read() function will read 2 bytes and then return. So you'd have a header value of: \xff\x51\x00\x00
But, this is just speaking of C, I don't know if VB.net's Read() is diffrent.
Nope. That's exactly how Socket.Read() works. That's why it modifies a buffer you pass it as a parameter, and its return value is the number of bytes read.
Uh..I've stumbled across a bit of an off-topic problem...
Is there any way I can modify the properties of a control on another thread (yes) from a piece of code on a different module (not sure)?
You cannot modify a control's properties on a thread other than the one on which it was created - that's a Win32 rule. You can use BeginInvoke[url=http://or just [url=http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invoke.aspx]Invoke] or just [url=http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invoke.aspx]Invoke (http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx) to marshal it to the UI thread.
Alright. Thanks. I've pretty much got my bot set up now! :D
Just wondering, but how would I do an IP Ban, because the User IP thing is labeled DEFUNCT, so you can't exactly just grab someone's IP.
Squelch is by IP. If you squelch an account, any other accounts that are squelched are on the same IP. Simply ban anyone who's squelched.
Wait, so if in Stealthbot, for example, you do /exile username
And it kicks, bans, and squelches 'username', does it just ban anyone who then attemps to join who is also squlched? If so, wouldn't it also ban other people who join your channel but are squelched? (Who are actually different people?)
Quote from: Ozzapoo on November 17, 2008, 02:40 AM
Wait, so if in Stealthbot, for example, you do /exile username
And it kicks, bans, and squelches 'username', does it just ban anyone who then attemps to join who is also squlched? If so, wouldn't it also ban other people who join your channel but are squelched? (Who are actually different people?)
yes. however, why would the bot squelch unless it was for the purpose of IP ban?
That's why one doesn't typically chat via moderation bot...
Quote from: Ozzapoo on November 17, 2008, 02:40 AM
Wait, so if in Stealthbot, for example, you do /exile username
And it kicks, bans, and squelches 'username', does it just ban anyone who then attemps to join who is also squlched? If so, wouldn't it also ban other people who join your channel but are squelched? (Who are actually different people?)
What difference does it make? If a user is squelched in the first place for whatever reason, you clearly don't care what happens to them.
Alright thanks for the info. And I'm just curious as to whether SID_GETADVLISTEX joins a game for you or lists them or both?
GETADVLISTEX just gives you a game list.
Then what do you put for the string values?
If you jsut want to list them, leave them blank.
If you want to get the information for a specific game, you put the title/pw in.
For some reason I get "Too many server requests" (0x06) is response to SID_GETADVLISTEX
You have to wait a few minutes between joining and sending that request, and about 30 seconds between every request... It's picky.
This CAN be used for Warcraft III custom games, right? If so, then howcome I am able to easily log on using WC3 and straight away go to the custom games list?
Warcraft 3 has a built in anti-flood system... maybe that's why? I honestly have spent no time on any actual client, so I really only know the packets and what I've done with them, not the original use.