Ok so I'm really bored atm and have an hour or two to kill. This post will NOT be useful to very many people as I am NOT going to be like "paste this code in this file etcetc and BOOM you have a custom source!", I'm simply going to go through some basic steps to create a workable source. Eg: you can log in... if I bother getting even that far.
USUAL DISCLAIMER: I'm far from good at this stuff and am just learning it myself. If I make a mistake I request that you bring it to my attention and I will fix it/give credit. I have no desire to lead people down the road to bad coding (lets face it, I will) but I thought I'd do this mini project to kill an hour or two.
BEFORE YOU START:
Read and hopefully understand Null's excellent explanation of what a server is and how it works. A lot of that stuff will be repeated here but he does an awesome job explaining things. I'm simply going to back it up with sample code from various public sources.
[Only registered and activated users can see links. Click Here To Register...]
Things we will need for our source
A C# editor (personally I use visual studio 2010 ultimate edition but that's just me)
A brain
Time
Things we need to do for our source
-Connections
There is a ton you can do to write an efficient async socket system but for now I'm going to just go with an existing socket system that works fairly well. In this case, Impulse's.
Personally I enjoy his system because it's incredibly easy to impliment for a rapid development type situation. DO NOT LIMIT YOURSELF! If you feel comfortable enough working with async sockets (sync are not really perfect for this application but feel free to try) then you may want to write your own system. Personally I just find this simple to use.
Note: some of the things from his original system are changed in this.. I'm not using thread per client and I may have changed other things... I personally don't remember what all I've changed atm so lets just use this as an example for now.
Setting up the listeners!
For a conquer server you will need two sockets blocked off for listening, namely game and auth ports.
If you are making a server for a recent tq patch you will be using auth port 9959 and game port 5816 wheras older patches used 9958 for auth port iirc.
So yes, lets setup our basic listeners!
WinSocket AuthSocket = new WinSocket(AuthPort);
WinSocket GameSocket = new WinSocket(GamePort);
In this case obviously I have Auth and Game ports defined as a static variable in my source which can then be read from a database, a text file... anything you want really.
Now, lets set up the actions for this system.
Simply do something along the lines of...
AuthSocket.AnnounceNewConnection += new Action<Wrapper>(ClientConnect);
Explanation! ClientConnect is the function/method to call when a client connects on the authsocket!
Now, we are creating a new instance of our ClientSocket. What is this? Well this is the class which contains all of our variables for this login client. NOTE: In this case it is only login information (login encryption, user, pass, passwordseed, etc) You could write this a number of ways. Just make sure you are doing things in the proper order.
Personally I send the password seed directly when the client connects (only needed if you are doing patch 52xx+ (forget which patch exactly) but I'll explain it slightly now.
public class ClientSocket
{
//Client variables
public ClientSocket(Socket C)
{
Client = C;
AuthType = 1;
AuthCrypt = new Encryption.AuthCrypt();
PassSeed = Program.Rand.Next(100000, 90000000);
byte[] S = Packets.PassSeed((uint)PassSeed);
Packets.Send(S, this);
}
}
Now, not all of this will be needed depending on which patch you are running. Basically I'm just initializing the values for my auth connection. This includes in this case setting the auth type (I use this to sort out which stage of authorization my client is at), encryption, password seed and then sending this.
In the case of newer patches seed MUST be sent before the client will send a login request packet.
This leads us to packet receiving/handling and encryption!
Encryption:
There are 2-3 main encryptions you will need to worry about for conquer emulation currently.
There are complete solutions to ALL of these encryption. You could write your own but personally I simply see no reason to.
1: Login Encryption. Very simple to setup and use. Requires no initializing values, you simply run login packets through the algorithm and you are good to go.
Have a note in my source that the credit for this was from immune's source. Credit to him or whoever he used it from.
2: Blowfish encryption (5017+ only)
Somewhat more complex as it requires a dhkey exchange as well as a static key (the gamekey, DR654dt34trg4UI6 on new'ish patches). Loads of solutions for this already. I'll post one but I'm fairly sure they are the same in 99 pct of 5017+ sources.
We also need our key exchange routine
Packet Receiving and Handling
Ok so the boring encryption stuff is pushed back for now so lets deal with actually receiving our packets!
Back on our authsocket we need to set up a new action. In this case AnnounceReceive
AuthSocket.AnnounceReceive += new Action<byte[], Wrapper, byte[]>(ClientReceive);
Again, ClientReceive is the function/method being called. Lets deal with that code shall we?
So in this case we are saying the ClientSocket Sender = the connector of the sender.. AS client socket. This may not make much sense but .connector is an object. This means it doesn't particularly have a type and you can ATTEMPT to interpret it.
Mini desc from msdn
"all types, predefined and user-defined, reference types and value types, inherit directly or indirectly from Object. You can assign values of any type to variables of type object. When a variable of a value type is converted to object, it is said to be boxed. When a variable of type object is converted to a value type, it is said to be unboxed"
So seeing as we assigned it to a clientsocket when we received the connection, we can interpret it as one now (unbox it)
Now, we run our client packet handler using the data received!
So we need to first decode the data obviously. We can do this using the Client's encryption (which we initiated when they first connected remember?)
IMPORTANT: You MUST setup encryption and decrypt packets as they are sent. The encryption uses a counter meaning if you don't decrypt everything being received this will be off and the encryption won't work anymore (Don't ignore packets is what we're saying)
So now we have our array of bytes that makes up a packet... What do we do with it now?! Well we need a way to handle this packet we've received.
INFORMATION ON PACKETS/STRUCTURING: [Only registered and activated users can see links. Click Here To Register...]
So if you've read the link I just gave you, you'll know that conquer packets use little endian format. This probably makes no sense to you but basically when reading something we read the bytes from right to left (the ones on the left are the smallest... think 8 in 18...)
To do this efficiently we need a way to structure what we're reading.
Byte = 1 byte
ushort = 2 bytes
uint = 4 bytes
ulong = 8 bytes
or we can read as many as needed (usually for strings)
USEFUL NOTE: Encoding.ASCII.GetString(arrayofbytes) will give you the string for an array of bytes... obviously. Useful to know though if you just want to quickly try to view string information in a packet.
Personally I've been using Tannels system for reading packets... I'm sure there are FAR better systems out there but I have it handy and am used to is so I'll post it as an example.
This doesn't need to make much sense to you yet... Lets go back to our packet handler and use it!
Ok so if you remember we have our Handle(Data, Client) method created and have decrypted the packet. Now what? Well we need to start reading the packet! To do this lets call our new shmancy packet reader.
PacketReader PR = new PacketReader(Data, 0);
This means we are reading the byte array known as "Data" and starting at offset 0. If you wanted to only read certain sections of the packet you may wish to skip ahead (therefor start at a higher offset).
Now. If you read the information I linked you to on how to interpret packets you would know that all packets have a length value to start and then generally a packet type.
So lets handle this!
ushort Length = PR.ReadUInt16();
ushort Type = PR.ReadUInt16();
We are calling the Packet reader we set up (PR) and reading by increments of 2 bytes. In this case we don't truly need the length but I've assigned it anyways. You could just do PR.ReadUInt16(); instead to skip it... or set the 0 in our starting offset to 2. Regardless what is important is the "Type" that we've read. Lets setup a switch statement to handle each type! (login only really uses 1... but w/e!)
switch (Type)
{
}
Now inside this we want a Case ##: for each packet we wish to handle (this only becomes an issue at all when we get to game server)
In this case we want to handle the login request. aka type 1086
case 1086:
break;
This is the client sending us the lovely login request which contains things such as...
Username, Password (remember to decrypt using password encryption if you are doing a current patch! Look for an example in immune's source, I don't care to go over it atm) and server name.
Now. I'm not going to go very far in depth with packet structure as this is something you will need to learn yourself but the link I provided earlier, trial and error as well as korv's wiki are GREAT resources!
[Only registered and activated users can see links. Click Here To Register...]
Generally you want to look at a packet and try to figure our what it contains.
Eg: seeing a small number followed by a bunch of values in order indicates it's probably a string with a length value preceding. What you can do for this would be..
string Whatever = Encoding.ASCII.GetString(PR.ReadBytes(PR.ReadByte( )));
This would read the next byte in the stream to read the number of bytes that the string is, then you read those bytes to a byte array and convert it to string.
Now that we've received a username and password for the client (and decrypted if needed) we need to check if it is valid!
We do this vs our character database (sql, flatfile, doesn't matter! You still need to check!)
An example would be a sql function which attempts to pull a password from accounts where username = 'username'. Then check if the password read == the one from the packet and if not return false.
NOTE: User/Pass in this packet do NOT have lengths and therefor will have whitespace! You can replace these using something like...
User = User.Replace("\0", "");
DO THIS BEFORE CHECKING VS DATABASE PASSWORD OR YOU WILL ALWAYS GET IT AS BEING WRONG!
Once we've validated the user/pass we need to respond to the client using the auth response packet: Type 1055
The structure of this packet is very simple... we will want to change the values of it though depending on if the account should try to log in to the game server now or not.
This will contain things such as..
Character UID
Server IP
Server Port
and type of response (valid, invalid pass, banned, etc)
2 = valid login
0 = invalid pass
57 = banned
Forget what others their are but easy enough to try some.
Once this is done we should have the client trying to connect to the game server! Congratz!
We've already set up the listener for it so do a similar setup where you have a onreceive/onconnect type action and setup your client class (possibly inheriting the information from the old login client) and setting up encryption.
NOTE: Blowfish takes a bit more to setup. I MAY go into the game server side of things if people see a need for me to continue this "guide"
Closing:
Hopefully this will help those who are getting some decent coding knowledge and are wanting to at least attempt making their own source. Setting up something basic isn't too difficult but I thought I'd point out some of the steps you will need to go through to hopefully help in that journey.
USUAL DISCLAIMER: I'm far from good at this stuff and am just learning it myself. If I make a mistake I request that you bring it to my attention and I will fix it/give credit. I have no desire to lead people down the road to bad coding (lets face it, I will) but I thought I'd do this mini project to kill an hour or two.
BEFORE YOU START:
Read and hopefully understand Null's excellent explanation of what a server is and how it works. A lot of that stuff will be repeated here but he does an awesome job explaining things. I'm simply going to back it up with sample code from various public sources.
[Only registered and activated users can see links. Click Here To Register...]
Things we will need for our source
A C# editor (personally I use visual studio 2010 ultimate edition but that's just me)
A brain
Time
Things we need to do for our source
-Connections
There is a ton you can do to write an efficient async socket system but for now I'm going to just go with an existing socket system that works fairly well. In this case, Impulse's.
Personally I enjoy his system because it's incredibly easy to impliment for a rapid development type situation. DO NOT LIMIT YOURSELF! If you feel comfortable enough working with async sockets (sync are not really perfect for this application but feel free to try) then you may want to write your own system. Personally I just find this simple to use.
Note: some of the things from his original system are changed in this.. I'm not using thread per client and I may have changed other things... I personally don't remember what all I've changed atm so lets just use this as an example for now.
Setting up the listeners!
For a conquer server you will need two sockets blocked off for listening, namely game and auth ports.
If you are making a server for a recent tq patch you will be using auth port 9959 and game port 5816 wheras older patches used 9958 for auth port iirc.
So yes, lets setup our basic listeners!
WinSocket AuthSocket = new WinSocket(AuthPort);
WinSocket GameSocket = new WinSocket(GamePort);
In this case obviously I have Auth and Game ports defined as a static variable in my source which can then be read from a database, a text file... anything you want really.
Now, lets set up the actions for this system.
Simply do something along the lines of...
AuthSocket.AnnounceNewConnection += new Action<Wrapper>(ClientConnect);
Explanation! ClientConnect is the function/method to call when a client connects on the authsocket!
Code:
static void ClientConnect(Wrapper C)
{
C.connector = new ClientSocket(C._socket);
}
Personally I send the password seed directly when the client connects (only needed if you are doing patch 52xx+ (forget which patch exactly) but I'll explain it slightly now.
public class ClientSocket
{
//Client variables
public ClientSocket(Socket C)
{
Client = C;
AuthType = 1;
AuthCrypt = new Encryption.AuthCrypt();
PassSeed = Program.Rand.Next(100000, 90000000);
byte[] S = Packets.PassSeed((uint)PassSeed);
Packets.Send(S, this);
}
}
Now, not all of this will be needed depending on which patch you are running. Basically I'm just initializing the values for my auth connection. This includes in this case setting the auth type (I use this to sort out which stage of authorization my client is at), encryption, password seed and then sending this.
In the case of newer patches seed MUST be sent before the client will send a login request packet.
This leads us to packet receiving/handling and encryption!
Encryption:
There are 2-3 main encryptions you will need to worry about for conquer emulation currently.
There are complete solutions to ALL of these encryption. You could write your own but personally I simply see no reason to.
1: Login Encryption. Very simple to setup and use. Requires no initializing values, you simply run login packets through the algorithm and you are good to go.
Have a note in my source that the credit for this was from immune's source. Credit to him or whoever he used it from.
2: Blowfish encryption (5017+ only)
Somewhat more complex as it requires a dhkey exchange as well as a static key (the gamekey, DR654dt34trg4UI6 on new'ish patches). Loads of solutions for this already. I'll post one but I'm fairly sure they are the same in 99 pct of 5017+ sources.
We also need our key exchange routine
Packet Receiving and Handling
Ok so the boring encryption stuff is pushed back for now so lets deal with actually receiving our packets!
Back on our authsocket we need to set up a new action. In this case AnnounceReceive
AuthSocket.AnnounceReceive += new Action<byte[], Wrapper, byte[]>(ClientReceive);
Again, ClientReceive is the function/method being called. Lets deal with that code shall we?
Code:
static void ClientReceive(byte[] arg1, Wrapper C, byte[] arg3)
{
ClientSocket Sender = C.connector as ClientSocket;
PacketHandler.Handle(arg1, Sender);
}
Mini desc from msdn
"all types, predefined and user-defined, reference types and value types, inherit directly or indirectly from Object. You can assign values of any type to variables of type object. When a variable of a value type is converted to object, it is said to be boxed. When a variable of type object is converted to a value type, it is said to be unboxed"
So seeing as we assigned it to a clientsocket when we received the connection, we can interpret it as one now (unbox it)
Now, we run our client packet handler using the data received!
Code:
public static void Handle(byte[] Data, ClientSocket Client)
{
Client.AuthCrypt.Decode(Data);
}
IMPORTANT: You MUST setup encryption and decrypt packets as they are sent. The encryption uses a counter meaning if you don't decrypt everything being received this will be off and the encryption won't work anymore (Don't ignore packets is what we're saying)
So now we have our array of bytes that makes up a packet... What do we do with it now?! Well we need a way to handle this packet we've received.
INFORMATION ON PACKETS/STRUCTURING: [Only registered and activated users can see links. Click Here To Register...]
So if you've read the link I just gave you, you'll know that conquer packets use little endian format. This probably makes no sense to you but basically when reading something we read the bytes from right to left (the ones on the left are the smallest... think 8 in 18...)
To do this efficiently we need a way to structure what we're reading.
Byte = 1 byte
ushort = 2 bytes
uint = 4 bytes
ulong = 8 bytes
or we can read as many as needed (usually for strings)
USEFUL NOTE: Encoding.ASCII.GetString(arrayofbytes) will give you the string for an array of bytes... obviously. Useful to know though if you just want to quickly try to view string information in a packet.
Personally I've been using Tannels system for reading packets... I'm sure there are FAR better systems out there but I have it handy and am used to is so I'll post it as an example.
This doesn't need to make much sense to you yet... Lets go back to our packet handler and use it!
Ok so if you remember we have our Handle(Data, Client) method created and have decrypted the packet. Now what? Well we need to start reading the packet! To do this lets call our new shmancy packet reader.
PacketReader PR = new PacketReader(Data, 0);
This means we are reading the byte array known as "Data" and starting at offset 0. If you wanted to only read certain sections of the packet you may wish to skip ahead (therefor start at a higher offset).
Now. If you read the information I linked you to on how to interpret packets you would know that all packets have a length value to start and then generally a packet type.
So lets handle this!
ushort Length = PR.ReadUInt16();
ushort Type = PR.ReadUInt16();
We are calling the Packet reader we set up (PR) and reading by increments of 2 bytes. In this case we don't truly need the length but I've assigned it anyways. You could just do PR.ReadUInt16(); instead to skip it... or set the 0 in our starting offset to 2. Regardless what is important is the "Type" that we've read. Lets setup a switch statement to handle each type! (login only really uses 1... but w/e!)
switch (Type)
{
}
Now inside this we want a Case ##: for each packet we wish to handle (this only becomes an issue at all when we get to game server)
In this case we want to handle the login request. aka type 1086
case 1086:
break;
This is the client sending us the lovely login request which contains things such as...
Username, Password (remember to decrypt using password encryption if you are doing a current patch! Look for an example in immune's source, I don't care to go over it atm) and server name.
Now. I'm not going to go very far in depth with packet structure as this is something you will need to learn yourself but the link I provided earlier, trial and error as well as korv's wiki are GREAT resources!
[Only registered and activated users can see links. Click Here To Register...]
Generally you want to look at a packet and try to figure our what it contains.
Eg: seeing a small number followed by a bunch of values in order indicates it's probably a string with a length value preceding. What you can do for this would be..
string Whatever = Encoding.ASCII.GetString(PR.ReadBytes(PR.ReadByte( )));
This would read the next byte in the stream to read the number of bytes that the string is, then you read those bytes to a byte array and convert it to string.
Now that we've received a username and password for the client (and decrypted if needed) we need to check if it is valid!
We do this vs our character database (sql, flatfile, doesn't matter! You still need to check!)
An example would be a sql function which attempts to pull a password from accounts where username = 'username'. Then check if the password read == the one from the packet and if not return false.
NOTE: User/Pass in this packet do NOT have lengths and therefor will have whitespace! You can replace these using something like...
User = User.Replace("\0", "");
DO THIS BEFORE CHECKING VS DATABASE PASSWORD OR YOU WILL ALWAYS GET IT AS BEING WRONG!
Once we've validated the user/pass we need to respond to the client using the auth response packet: Type 1055
The structure of this packet is very simple... we will want to change the values of it though depending on if the account should try to log in to the game server now or not.
This will contain things such as..
Character UID
Server IP
Server Port
and type of response (valid, invalid pass, banned, etc)
2 = valid login
0 = invalid pass
57 = banned
Forget what others their are but easy enough to try some.
Once this is done we should have the client trying to connect to the game server! Congratz!
We've already set up the listener for it so do a similar setup where you have a onreceive/onconnect type action and setup your client class (possibly inheriting the information from the old login client) and setting up encryption.
NOTE: Blowfish takes a bit more to setup. I MAY go into the game server side of things if people see a need for me to continue this "guide"
Closing:
Hopefully this will help those who are getting some decent coding knowledge and are wanting to at least attempt making their own source. Setting up something basic isn't too difficult but I thought I'd point out some of the steps you will need to go through to hopefully help in that journey.