Raknet

BlitzMax Forums/BlitzMax Beginners Area/Raknet

Farflame(Posted 2009) [#1]
The examples put together by Zeke initially work fine, but I have a couple of problems. I thought I'd try sending a constant stream of data to see how it handles it, so I simply removed the keyboard input from the Zeke_client program, so that it's sending the 6 bits of information continually. This immediately locks-up the server, which remains locked-up even after turning off the client.

Bearing in mind that both client and server have a Delay 5 on each loop, it should only be sending 6 pieces of information, 200 times per second. That really doesn't sound like a huge amount of information and shouldn't be locking up the server, should it?

I haven't altered the tutorials at all, only removed the If KeyHit(KEY_SPACE) Then / Endif lines.


Zeke(Posted 2009) [#2]
MaxGui is not so fast and thats why server "freeze"..

but if you replace Addlog function in server code to this it works:

Function Addlog(text:String)
	AddTextAreaText(txtlog , text + "~n")
	PollSystem()
End Function



Farflame(Posted 2009) [#3]
Ah yes, it's just MaxGUI, that's a relief. I don't need to use MaxGUI for my server anyway so that's cool, and the fix works :)


Farflame(Posted 2009) [#4]
Still having a similar problem though. When sending the information both ways - i.e from server to client AND client to server (just a packet of 6 random numbers), it sends for about 2 seconds, then both sides just stop receiving.

I think the problem is in the line ....

interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, True)

I'm not sure what unassigned_system_address does here. From what I remember of using Raknet in B3D, that was the 'address' of the recipient. I'm using that line on both server and client and they do seem to receive the info, then they both just stop.

In B3D, what I did was to store the 'ID' of each client after they connected, then use that when sending information to them. So unassigned_system_address was RN_PacketGetplayerID(packet) (to retreive the client's details directly from the packet and then use that to reply with some information).

Hmmm, so anyway, how do I store each connections 'ID' so that I can send information specifically to each connection? And what do I use instead of 'unassigned_system_address' to send information BACK to the server?

I've tried trawling through the Raknet documents but I find it very hard to understand. Once I know how to send/receive with both server and client I really don't intend to use anything more advanced in Raknet.


Zeke(Posted 2009) [#5]
when client connect to server, you can get client ID using:

local clientID:TRKSystemAddress=packet.GetSystemAddress()


and use that clientid instead UNASSIGNED_SYSTEM_ADDRESS. ( and change last True value to False, that value tells if we send stream to all connected clients (broadcast))

so quick help:

Server:
Send packet to ALL clients use:
interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, True)


Send packet to only one client use:
interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, clientID, False)


Client:
Send packet to server
interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, True)



Farflame(Posted 2009) [#6]
Excellent, thank you. Basically I just need to change my B3D syntax to the new Blitzmax syntax, but it does look very similar. Thanks for your help.


Farflame(Posted 2009) [#7]
Ok, that works too, but I still have the problem that communication seems to stop completely after about 2 seconds.

I've altered your Zeke_Server and Zeke_Client slightly - mostly just moved stuff into functions. But also, both server and client send the packet to each other, 10 times per second. They then just print out the information on the screen. It seems to be working fine, but then it just stops receiving, although they don't lock up now (that random number at the top is just to show it's not locked up).

Here's the programs......

Server.....

SuperStrict

Import bah.raknet

AppTitle ="Server"
'GUI
Graphics 300,200

'globals
Global interface:TRKRakPeerInterface
Global clientid:TRKSystemAddress

StartServer()

' ####################################
While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())
	Cls
	
	DrawText Rand(9),0,0
	
	NetWork()
	SendToClient()
	
	Flip
	
	Delay 100
	
Wend
' ####################################

If interface
	interface.Shutdown(300)
	TRKRakNetworkFactory.DestroyRakPeerInterface(interface)
EndIf

Function NetWork()
	If Not interface Then Return
	
	Local packet:TRKPacket
	packet = interface.Receive()
	If Not packet Then Return
	
	Local packetid:Int = packet.GetPacketIdentifier()
	
	Select packetid
		Case ID_NEW_INCOMING_CONNECTION
			'Addlog "New Client connected to server, IP = " + packet.GetSystemAddress().ToString()
			clientID:TRKSystemAddress=packet.GetSystemAddress()
		Case ID_DISCONNECTION_NOTIFICATION
			'Addlog "Client disconneced IP=" + packet.GetSystemAddress().ToString()
		Default
			Local bitstream:TRKBitStream = TRKBitStream.CreateFromData(packet.GetData(), packet.GetLength(), 0)
			
			Local _Byte:Byte
			Local _Short:Short
			Local _Int:Int
			Local _long:Long
			Local _float:Float
			Local _double:Double
			
			bitstream.ReadByte(_byte)
			bitstream.ReadShort(_short)
			bitstream.ReadInt(_int)
			bitstream.ReadLong(_long)
			bitstream.ReadFloat(_float)
			bitstream.ReadDouble(_double)
			
			DrawText _byte,10,50
			DrawText _short,10,70
			DrawText _Int,10,90
			DrawText _long,10,110
			DrawText _float,10,130
			DrawText _double,10,150

	End Select
	interface.DeallocatePacket(packet)
	
End Function

Function StartServer()
	'init
	interface = TRKRakNetworkFactory.GetRakPeerInterface()
	clientid = UNASSIGNED_SYSTEM_ADDRESS
	
	'start server
	Local sd:TRKSocketDescriptor = New TRKSocketDescriptor.Create(7777)
	Local result:Int = interface.Startup(5, 30, sd)
	interface.SetMaximumIncomingConnections(5)
	If result
		'Addlog "Server started"
	Else
		'Addlog "Server failed to start."
		End
	End If
	interface.SetOccasionalPing(True)
End Function

Function SendToClient()
	Local bitstream:TRKBitStream = New TRKBitStream.Create()
	
Local_byte:Byte=rand(255)
	Local _short:Short = Rand(2000)
	Local _int:Int = Rand(20000)
	Local _long:Long = Rand(10000000)
	Local _float:Float = Rnd(100000)
	Local _double:Double = Rand(10)
	Local bit:Byte=1
	
	bitstream.WriteByte(_byte)
	bitstream.WriteShort(_short)
	bitstream.WriteInt(_int)
	bitstream.WriteLong(_long)
	bitstream.WriteFloat(_float)
	bitstream.WriteDouble(_double)
	
	'send packet
	interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, clientID, False)
	
End Function


Client

SuperStrict

Import bah.raknet

AppTitle = "Client"
Graphics 300, 200

Global interface:TRKRakPeerInterface
Global clientid:TRKSystemAddress
Global cstate:Int 'connectstate
Global connectstate:String[] = ["Disconnected", "Connecting", "Connected", "Disconnecting", "Failed to connect server"]

Connect()

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())

	Cls
	
	DrawText Rand(9),0,0
	
	SendToServer()
	NetWork()

	Flip
	
	Delay 100
	
Wend
'Shutdown
If interface
	interface.Shutdown(300)
	TRKRakNetworkFactory.DestroyRakPeerInterface(interface)
EndIf

Function Connect()
	interface = TRKRakNetworkFactory.GetRakPeerInterface()
	
	Local sd:TRKSocketDescriptor = New TRKSocketDescriptor.Create(0)
	interface.Startup(1, 30, sd)
	interface.SetOccasionalPing(True)
	
	Local result:Int = interface.Connect("localhost", 7777)
	If Not result Then
		Print "Unable to connect server"
	End If
	cstate = 1 'connecting
End Function

Function Network()
	If cstate = 4 Then Return
	
	If Not interface Then Return
	
	'Handle packets
	Local packet:TRKPacket = interface.Receive()
	If Not packet Then Return
	
	Local packetid:Int = packet.GetPacketIdentifier()
	
	Select packetid
		Case ID_CONNECTION_ATTEMPT_FAILED
			'Notify "Unable to connect server..."
			cstate = 4 'unable to connect
		Case ID_CONNECTION_REQUEST_ACCEPTED
			'Notify "Connected to server " + packet.GetSystemAddress().ToString()
			cstate = 2 'connected
		Default
			DrawText "Received",50,50
			
			Local bitstream:TRKBitStream = TRKBitStream.CreateFromData(packet.GetData(), packet.GetLength(), 0)
			
			Local _Byte:Byte
			Local _Short:Short
			Local _Int:Int
			Local _long:Long
			Local _float:Float
			Local _double:Double
			
			bitstream.ReadByte(_byte)
			bitstream.ReadShort(_short)
			bitstream.ReadInt(_int)
			bitstream.ReadLong(_long)
			bitstream.ReadFloat(_float)
			bitstream.ReadDouble(_double)
			
			DrawText _Byte,10,50
			DrawText _Short,10,70
			DrawText _Int,10,90
			DrawText _Long,10,110
			DrawText _Float,10,130
			DrawText _Double,10,150
			
	End Select
	interface.DeallocatePacket(packet)
	
End Function

Function SendToServer()
	If cstate = 2 'connected
		Local bitstream:TRKBitStream = New TRKBitStream.Create()
		
		Local _byte:Byte=Rand(255)
		Local _short:Short = Rand(200)
		Local _int:Int = Rand(200)
		Local _long:Long = Rand(100000)
		Local _float:Float = Rnd(100)
		Local _double:Double = Rand(1)
		
		bitstream.WriteByte(_byte)
		bitstream.WriteShort(_short)
		bitstream.WriteInt(_int)
		bitstream.WriteLong(_long)
		bitstream.WriteFloat(_float)
		bitstream.WriteDouble(_double)
		
		'send packet
		interface.SendBitStream(bitstream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, True)
		
	End If
End Function


Is it perhaps something to do with not deallocating a packet somewhere? Or is Raknet just becoming overloaded? Or have I made a more basic error in the code somewhere?


Zeke(Posted 2009) [#8]
ah.. got it..
first BYTE what you write to bitstream is "packetID"

right now raknet use first 109 packet id.. and first "free" packet id starts from 110.. BUT there is constant variable (declarend in common.bmx) ID_USER_PACKET_ENUM and i set that value to 150 (if in the future raknet want to use more packetid:s)

this packetID is BYTE and readed FIRST.. and because this byte can only handle 0-255 bytes.. and first free packetid is 150, so you have available only 105 packetid for YOUR packets (move,shoot,chat,etc..)

Server:


Client:



Farflame(Posted 2009) [#9]
Ah, thank Goodness I asked because I would have never worked that out on my own! It works fine now...... seems to work perfectly even with Delay 1 instead of Delay 5 or 100.

So, should I always add bitstream.WriteByte(ID_SendData) as the first byte when sending data? Is it only when sending bitstreams?


Zeke(Posted 2009) [#10]
yeah. first byte what to write bitstream is that "packet ID".. and that byte MUST be =>ID_USER_PACKET_ENUM and only when you send bitstreams.

i dont know if there is better way to handle your own packet id:s what i use. but this is what i use and this works.. this also catch invalid packet ids...

i use delay 5 when coding and debugging... or if i see that cpu usage is high... but its not needed.. also if you want more speed:

change:
Local result:Int = interface.Startup(5, 30, sd) '=> 30 to 0 (thats 30 is raknet internal delay. now its 30ms.. )


today i also made some tests, when reading bitstreams is similar like bmax streams.. like mybyte:byte=bitstream.readbyte()

i made this earlier.. but changed back to original bruceys method bitstream.readbyte(mybyte)..

but now this works well.. and i also wanted to use bitstream.writeint(200) <= its not possible right now.. because bah.raknet is coded that way.. but i made changes and it works also pretty well..

im still testing.. and trying to add writestring functions...

but what do you like to use:

original:
local mybyte:int,myint:int
bitstream.readbyte(mybyte)
bitstream.readbyte(myint)

local intseven:int=7777
bitstream.writeint(intseven)


or

like bmax:
local mybyte:byte=bitstream.readbyte()
local myint:byte=bitstream.readint()

bitstream.writeint(7777)



Farflame(Posted 2009) [#11]
Are you saying there's no working Writestring yet?

The Bmax version above looks better, just because it's shorter :)


Farflame(Posted 2009) [#12]
.


Zeke(Posted 2009) [#13]
no writestring right now..

this is right now quick solution:
'Sending:
Rak_WriteString(bitstream,"Hello World!")

Rem
	bbdoc: Write string (max 255 chars)
End Rem
Function Rak_WriteString(bitstream:TRKBitStream , text:String)
	bitstream.WriteByte(text.Length) 'or WriteShort/WriteInt
	For Local i:Int = 0 Until text.Length
		bitstream.WriteByte(text[i])
	Next
End Function


'Reading:
Local mystring:String=Rak_ReadString(bitstream)

Rem
	bbdoc: Read String (Max 255 chars)
End Rem
Function Rak_ReadString:String(bitstream:TRKBitStream)
	Local strlen:Int = bitstream.ReadByte() 'or WriteShort/WriteInt
	Local str:String
	For Local i:Int = 0 Until strlen
		str:+ Chr(bitstream.ReadByte() )
	Next
	Return str
End Function



Farflame(Posted 2009) [#14]
Sweet thanks. I was going to say it should be possible to do it character by character, but that saves me the need to do that :)

I'll keep my eyes open for upgrades, but will use your functions in the meantime.


Farflame(Posted 2009) [#15]
Also, regarding disconnecting. If my client just disconnects, I assume the server has no idea that's happened, so I need to write my own disconnect routine? i.e If the client switches off, do I just send ID_DISCONNECTION_NOTIFICATION to the server?


Zeke(Posted 2009) [#16]
when client close(normal way) i use this:
Method CloseConnection()
	If interface
		interface.Shutdown(300)
		TRKRakNetworkFactory.DestroyRakPeerInterface(interface)
	EndIf
	interface = Null
End Method

and that Shutdown send ID_DISCONNECTION_NOTIFICATION to server.. so you dont need to send that.
also if client crashed. there is some seconds delay when server see that client is disconnected and server got that disconnection id.


Farflame(Posted 2009) [#17]
That's the code I'm currently using to shut down my client (my code is still mostly your server/client example but I'm expanding/changing it as I go along), but the server doesn't seem to receive anything. I've even put a simple 'End' after the server receives ID_DISCONNECTION_NOTIFICATION but it doesn't respond - meaning it isn't receiving it?


Farflame(Posted 2009) [#18]
Any idea what's wrong with the code below?

When my client initially connects, I'm storing TRKSystemAddress=packet.GetSystemAddress() in a type, in a list. So then, when my client sends something to the server, I can identify which client sent it using the code below...

Local ClientID:TRKSystemAddress = packet.GetSystemAddress()
Local C:Client = GetClient(ClientID)


Function GetClient:Client(ClientID:TRKSystemAddress)
	Local C:Client
	If Not ClientsList Then Return Null
	For C = EachIn ClientsList
		If C.id = ClientID Then Exit
	Next
	Return C
End Function


There are no errors, but the function always just returns the first client. When I run it through the debugger, the line 'If C.id = ClientID Then Exit' always seems to be true, even when the numbers don't match.

Am I doing something wrong here. Or do you have a version of this function already written Zeke?


Farflame(Posted 2009) [#19]
I think the problem is probably that I shouldn't be storing TRKSystemAddress, but something else. Looking back at my old B3D code with Raknet, I was storing RN_PacketGetplayerIndex(packet). Is there an equivalent of RN_PacketGetPlayerIndex now?


Zeke(Posted 2009) [#20]
use:
Local ClientGuid:TRKRakNetGUID = packet.GetGuid()

and then it should work.

and use it like this:
Function GetClient:Client(GUID:TRKRakNetGUID)
	Local C:Client
	For C = EachIn clients
		If C.GUID.ToString() = GUID.ToString() Then Return C
	Next
End Function



Farflame(Posted 2009) [#21]
Thank you, works perfectly now :)


Zeke(Posted 2010) [#22]
updated Raknet version http://www.blitzmax.com/Community/posts.php?topic=87434#1007510