08-03-2007, 05:48 PM
This version of the tutorial was released on the ED forums, but this is the one I used to add to Mirage Source which seemed to work first time.
Again, credit goes to Verrigan for this, and Aranshada for writing the tutorial.
This tutorial will NOT work with Windows 95/98/ME Servers
In order to do this tut, you will need to download this .dll file and register it. THIS IS A NECESSITY FOR THIS TUTORIAL!!!
http://aranshada.hopto.org/Elysium/Downl...Server.dll
-=: Server-Side :=-
First thing's first, go to Project > References... and scroll down until you find JetByte Socket Server 1.0 Type Library. Check the box beside it and click OK.
Download Sonires classes, these are guaranteed to work in MSE (Tested myself).
http://www.freewebs.com/msebuild1/classes.rar
To add these to your server project after downloading them, go to Project > Add Class Module, and click the Existing tab, and select the a module that you downloaded. Repeat this until you have all three class modules added to your project.
Now that we have the class modules out of the way, we have to go through the rest of the server and change some stuff. So, what do you think we'll need to change first? We'll need to change the methods for creating the sockets, destroying the sockets, and anything that checks a socket for a connection.
In modServerTCP, add these declarations at the top.
Those will serve to be our objects that we will control all TCP interaction from.
In modGeneral, find Sub InitServer.
Find:
Comment all of it out or just delete it if you'd like.
Either way, add this below it:
That will set our GameServer object as a new clsServer class.
Just below that you'll see this:
Comment out Load frmServer.Socket(i) and add this below it:
So the entire thing should look like this:
Keep looking in that same sub until you find this:
Comment out frmServer.Socket(0).Listen and add this below it:
Now, still in modGeneral, find Sub DestroyServer.
Inside of Sub DestroyServer, find this:
Change all of that to this:
Now go to Sub UpdateCaption in modServerTCP.
Change the whole sub to this:
If you'll notice, the only thing changed is that we use GameServer.LocalPort to show what the port is. I left the frmServer.Socket(0).LocalIP in there so it'd be easier to find out what my computer's private IP is.
What's our next target? Sub SendDataTo. It is also located in modServerTCP. Change the entire thing to this:
There is also another one in clsCommands. Be sure you change that one too if you intend on sending packets from the Main.txt.
You can see where I simply commented out the old code and added the new above it. This also shows you the method used for sending a packet.
So... we've changed how it loads the object, destroys it, and how it sends data. What's next?
Find Function IsConnected in modServerTCP.
Change the whole function to this:
There is also another one in clsCommands. Be sure you change that one, too.
Now that we can't exactly check the socket's state against VB's Winsock Constants, we can simply check to see if a certain socket Is Nothing. If it Is Nothing, then it hasn't been initialized, meaning that there isn't a live connection, so we return false. Otherwise, it's true.
Now we'll tackle all of the subs that are fired when a connection is received or closed.
In modServerTCP, find Sub AcceptConnection.
Change the whole thing to this:
Find Sub CloseSocket in the same module.
Change the whole sub to this:
Now to deal with IncomingData.
Also in modServerTCP, find Sub IncomingData.
Change the whole sub to this:
(Good catch, Pingu.)
There's not really any need to check for this anymore since I don't think the socket state COULD be like that with COMSocketServer.
Now, go to frmServer.
Find these subs:
DELETE ALL OF THEM. They will only cause errors now.
I believe that's all there is for the Server-Side of things.
On to the client!
-=: Client-Side :=-
Don't worry, we won't be here long.
In the client, go to modClientTCP and find Sub SendData.
Change the whole thing to this:
Done with the Client-Side stuff! Now, isn't the client much easier than the server?
Optionally, you might also want to set the MAX_PLAYERS in your Data.ini to something much higher. It's less stress on the server now since it doesn't have to load up a new Winsock object for every connection. Now it just adds a new object to a collection. I've seen some servers with IOCP that had MAX_PLAYERS set at 500. I've seen another one with it set at 1000. That's really just personal preference, though.
Now, I'm pretty sure I didn't miss anything. If anyone gets any errors after trying this tut, copy/paste the section of code that is highlighted as well as the surrounding code, and I'll see if there's something that I forgot to put in the tut.
Credit for the code in this tutorial goes to Dave's Valkorian Engine source because I looked at all of the code in there to find out how to do this. Some credit also goes to Pingu for requesting this tutorial in the first place.
Again, credit goes to Verrigan for this, and Aranshada for writing the tutorial.
This tutorial will NOT work with Windows 95/98/ME Servers
In order to do this tut, you will need to download this .dll file and register it. THIS IS A NECESSITY FOR THIS TUTORIAL!!!
http://aranshada.hopto.org/Elysium/Downl...Server.dll
-=: Server-Side :=-
First thing's first, go to Project > References... and scroll down until you find JetByte Socket Server 1.0 Type Library. Check the box beside it and click OK.
Download Sonires classes, these are guaranteed to work in MSE (Tested myself).
http://www.freewebs.com/msebuild1/classes.rar
To add these to your server project after downloading them, go to Project > Add Class Module, and click the Existing tab, and select the a module that you downloaded. Repeat this until you have all three class modules added to your project.
Now that we have the class modules out of the way, we have to go through the rest of the server and change some stuff. So, what do you think we'll need to change first? We'll need to change the methods for creating the sockets, destroying the sockets, and anything that checks a socket for a connection.
In modServerTCP, add these declarations at the top.
Code:
' Our GameServer and Sockets objects for the TCP interaction
Public GameServer As clsServer
Public Sockets As colSockets
In modGeneral, find Sub InitServer.
Find:
Code:
' Get the listening socket ready to go
frmServer.Socket(0).RemoteHost = frmServer.Socket(0).LocalIP
frmServer.Socket(0).LocalPort = GAME_PORT
Either way, add this below it:
Code:
Set GameServer = New clsServer
Just below that you'll see this:
Code:
' Init all the player sockets
Call SetStatus("Initializing player array...")
For i = 1 To MAX_PLAYERS
Call ClearPlayer(i)
Load frmServer.Socket(i)
Next i
Code:
Call GameServer.Sockets.Add(CStr(i))
Code:
' Init all the player sockets
Call SetStatus("Initializing player array...")
For i = 1 To MAX_PLAYERS
Call ClearPlayer(i)
'Load frmServer.Socket(i)
Call GameServer.Sockets.Add(CStr(i))
Next i
Keep looking in that same sub until you find this:
Code:
' Start listening
frmServer.Socket(0).Listen
Code:
GameServer.StartListening
Now, still in modGeneral, find Sub DestroyServer.
Inside of Sub DestroyServer, find this:
Code:
For i = 1 To MAX_PLAYERS
Call SetStatus("Unloading sockets and timers... " & i & "/" & MAX_PLAYERS)
DoEvents
Unload frmServer.Socket(i)
Next
Code:
For i = 1 To MAX_PLAYERS
Call SetStatus("Unloading sockets and timers... " & i & "/" & MAX_PLAYERS)
DoEvents
'Unload frmServer.Socket(i)
Call GameServer.Sockets.Remove(CStr(i))
Next
Set GameServer = Nothing
Now go to Sub UpdateCaption in modServerTCP.
Change the whole sub to this:
Code:
Sub UpdateCaption()
frmServer.Caption = GAME_NAME & " - Server - Powered By Elysium Source"
frmServer.lblIP.Caption = "IP Address: " & frmServer.Socket(0).LocalIP
frmServer.lblPort.Caption = "Port: " & STR(GameServer.LocalPort)
frmServer.TPO.Caption = "Total Players Online: " & TotalOnlinePlayers
Exit Sub
End Sub
What's our next target? Sub SendDataTo. It is also located in modServerTCP. Change the entire thing to this:
Code:
Sub SendDataTo(ByVal index As Long, ByVal Data As String)
Dim dbytes() As Byte
dbytes = StrConv(Data, vbFromUnicode)
If IsConnected(index) Then
GameServer.Sockets.Item(index).WriteBytes dbytes
DoEvents
End If
'If IsConnected(index) Then
' frmServer.Socket(index).SendData Data
' DoEvents
'End If
End Sub
You can see where I simply commented out the old code and added the new above it. This also shows you the method used for sending a packet.
So... we've changed how it loads the object, destroys it, and how it sends data. What's next?
Find Function IsConnected in modServerTCP.
Change the whole function to this:
Code:
Function IsConnected(ByVal index As Long) As Boolean
If GameServer.Sockets.Item(index).Socket Is Nothing Then
IsConnected = False
Else
IsConnected = True
End If
'If frmServer.Socket(index).State = sckConnected Then
' IsConnected = True
'Else
' IsConnected = False
'End If
End Function
Now that we can't exactly check the socket's state against VB's Winsock Constants, we can simply check to see if a certain socket Is Nothing. If it Is Nothing, then it hasn't been initialized, meaning that there isn't a live connection, so we return false. Otherwise, it's true.
Now we'll tackle all of the subs that are fired when a connection is received or closed.
In modServerTCP, find Sub AcceptConnection.
Change the whole thing to this:
Code:
Sub AcceptConnection(Socket As JBSOCKETSERVERLib.ISocket)
Dim i As Long
i = FindOpenPlayerSlot
If i 0 Then
' Whoho, we can connect them
'frmServer.Socket(i).Close
'frmServer.Socket(i).Accept SocketId
Socket.UserData = i
Set GameServer.Sockets.Item(CStr(i)).Socket = Socket
Call SocketConnected(i)
Socket.RequestRead
Else
Socket.Close
End If
End Sub
Find Sub CloseSocket in the same module.
Change the whole sub to this:
Code:
Sub CloseSocket(ByVal index As Long)
' Make sure player was/is playing the game, and if so, save'm.
If index > 0 Then
Call LeftGame(index)
Call TextAdd(frmServer.txtText(0), "Connection from " & GetPlayerIP(index) & " has been terminated.", True)
'frmServer.Socket(index).Close
Call GameServer.Sockets.Item(index).ShutDown(ShutdownBoth)
Set GameServer.Sockets.Item(index).Socket = Nothing
Call UpdateCaption
Call ClearPlayer(index)
End If
End Sub
Now to deal with IncomingData.
Also in modServerTCP, find Sub IncomingData.
Change the whole sub to this:
Code:
Sub IncomingData(Socket As JBSOCKETSERVERLib.ISocket, Data As JBSOCKETSERVERLib.IData)
'On Error Resume Next
Dim Buffer As String
Dim dbytes() As Byte
Dim Packet As String
Dim top As String * 3
Dim Start As Integer
Dim index As Long
Dim DataLength As Long
dbytes = Data.Read
Socket.RequestRead
Buffer = StrConv(dbytes(), vbUnicode)
DataLength = Len(Buffer)
index = CLng(Socket.UserData)
If Buffer = "top" Then
top = STR(TotalOnlinePlayers)
Call SendDataTo(index, top)
Call CloseSocket(index)
End If
Player(index).Buffer = Player(index).Buffer & Buffer
Start = InStr(Player(index).Buffer, END_CHAR)
Do While Start > 0
Packet = Mid(Player(index).Buffer, 1, Start - 1)
Player(index).Buffer = Mid(Player(index).Buffer, Start + 1, Len(Player(index).Buffer))
Player(index).DataPackets = Player(index).DataPackets + 1
Start = InStr(Player(index).Buffer, END_CHAR)
If Len(Packet) > 0 Then
Call HandleData(index, Packet)
End If
Loop
' Check if elapsed time has passed
Player(index).DataBytes = Player(index).DataBytes + DataLength
If GetTickCount >= Player(index).DataTimer + 1000 Then
Player(index).DataTimer = GetTickCount
Player(index).DataBytes = 0
Player(index).DataPackets = 0
Exit Sub
End If
' Check for data flooding
If Player(index).DataBytes > 1000 And GetPlayerAccess(index) 7 Then
' Call CloseSocket(i)
' End If
'Next
Call CheckGiveHP
Call GameAI
End Sub
There's not really any need to check for this anymore since I don't think the socket state COULD be like that with COMSocketServer.
Now, go to frmServer.
Find these subs:
Code:
Private Sub Socket_Close(index As Integer)
Call CloseSocket(index)
End Sub
Private Sub Socket_ConnectionRequest(index As Integer, _
ByVal requestID As Long)
Call AcceptConnection(index, requestID)
End Sub
Private Sub Socket_DataArrival(index As Integer, _
ByVal bytesTotal As Long)
If IsConnected(index) Then
Call IncomingData(index, bytesTotal)
End If
End Sub
I believe that's all there is for the Server-Side of things.
On to the client!
-=: Client-Side :=-
Don't worry, we won't be here long.
In the client, go to modClientTCP and find Sub SendData.
Change the whole thing to this:
Code:
Sub SendData(ByVal data As String)
Dim dbytes() As Byte
dbytes = StrConv(data, vbFromUnicode)
If IsConnected Then
'If InGame Then
'frmMirage.Socket.SendData Encrypt(data, EncryptPassword)
'Else
'frmMirage.Socket.SendData Encrypt(data, defaultEncryptPassword)
'End If
frmMirage.Socket.SendData dbytes
DoEvents
End If
End Sub
Optionally, you might also want to set the MAX_PLAYERS in your Data.ini to something much higher. It's less stress on the server now since it doesn't have to load up a new Winsock object for every connection. Now it just adds a new object to a collection. I've seen some servers with IOCP that had MAX_PLAYERS set at 500. I've seen another one with it set at 1000. That's really just personal preference, though.
Now, I'm pretty sure I didn't miss anything. If anyone gets any errors after trying this tut, copy/paste the section of code that is highlighted as well as the surrounding code, and I'll see if there's something that I forgot to put in the tut.
Credit for the code in this tutorial goes to Dave's Valkorian Engine source because I looked at all of the code in there to find out how to do this. Some credit also goes to Pingu for requesting this tutorial in the first place.