network help:someone please check this code out

Blitz3D Forums/Blitz3D Programming/network help:someone please check this code out

chrisnorris007(Posted 2009) [#1]
this is my server i wrote....can I have some suggestions on optmizing this tcp server?

I see that some have used banks....can you explain the need and benefits of banks verses strings? Probably takes more bytes to send for string

anyway here is code:

the client sends 1,x,y,z,rx,ry,rz,animseq,animframe,name$ everytime a move is made

to synch up animation and position/rotation


here is the server

Type netplayer 
	Field netid#
	Field name$
	Field port$
	Field ip$
	Field stream
	Field x#,y#,z#,rx#,ry#,rz#
	Field animframe#
	Field AniSeq#
	Field updated#
End Type

Global currentid#

Dim splitdata$(10)

Graphics3D 800,600,0,2

AppTitle "Server 1.0 beta"

server=CreateTCPServer(8080)

Print "Server Again 1.0 beta"

If server<>0
Print "Server Accepting Connections..."
Print "press escape to close"
EndIf

Global currentcount=0

While Not KeyDown(1)

	stream = AcceptTCPStream(server)
		If stream
		currentcount=currentcount+1
               WriteLine(stream,currentcount)
		Print "New Net Player Joined : Player ID:"+currentcount
		n.netplayer=New netplayer
		n\netid=currentcount
		n\stream=stream
		n\ip$=DottedIP$(TCPStreamIP(stream))
		n\port$=TCPStreamPort(stream)
		n\x=0:n\y=0:n\z=0:n\rx=0:n\ry=0:n\rz=0
		For existing.netplayer=Each netplayer  ; send all player locations  - animation positions for existing players
			If existing\netid<>n\netid
				WriteLine(n\stream,1+","+existing\netid+","+existing\x+","+existing\y+","+existing\z+","+existing\rx+","+existing\ry+","+existing\rz+","+existing\aniseq+","+existing\animframe+","+existing\name$) ; notify new player of all existing players
				WriteLine(existing\stream,5+","+n\netid+","+n\x+","+n\y+","+n\z+","+n\rx+","+n\ry+","+n\rz+","+n\aniseq+","+n\animframe+","+n\name$) ; notify existing players of new player
			EndIf
		Next
		EndIf
			
	For n.netplayer=Each netplayer
	If Eof(n\stream)=True
		currentcount=currentcount-1
		Print  "player " + n\name$+ " has disconnected from ip address:" +  n\ip$ + " and port " + n\port$ + " leaving " + currentcount + " clients connected."
			For dd.netplayer=Each netplayer
			WriteLine(dd\stream,6+","+n\netid+","+n\x+","+n\y+","+n\z+","+n\rx+","+n\ry+","+n\rz+","+n\aniseq+","+n\animframe+","+n\name$);notify all players that this player has Left
			Next
		Delete n
		Else			
		If ReadAvail(n\stream)>2 Then
		message$ = ReadLine$(n\stream)
		If Instr(message$,",")
		d=split(message$)
			Select splitdata$(1)
		Case 1 ; update coordinates/rotation
		n\updated=1
		n\x=Float(splitdata$(3)):n\y=Float(splitdata$(4)):n\z=Float(splitdata$(5)):n\rx=Float(splitdata$(6)):n\ry=Float(splitdata$(7)):n\rz=Float(splitdata$(8))
		n\Aniseq=Int(splitdata$(9)):n\animframe=Int(splitdata$(10)):n\name$=splitdata$(11)	
	;		Print  1+","+n\netid+","+n\x+","+n\y+","+n\z+","+n\rx+","+n\ry+","+n\rz+","+n\aniseq+","+n\animframe+","+n\name$
		
		Case 2 ; send private message
		Print "sending a private" ; 2,chris,msg
		For p.netplayer=Each netplayer
			If p\name$=splitdata$(2)
						WriteLine(p\stream,"7,"+splitdata$(3))
			EndIf
		Next
		End Select
	EndIf
		EndIf
	EndIf
	Next		
 	For n.netplayer=Each netplayer
			If n\updated=1 
				For q.netplayer=Each netplayer
				WriteLine(q\stream,1+","+n\netid+","+n\x+","+n\y+","+n\z+","+n\rx+","+n\ry+","+n\rz+","+n\aniseq+","+n\animframe+","+n\name$)
				Next
				n\updated=0
			EndIf
	Next

		
Wend

CloseTCPServer(server)


Function Split(DataToSplit$)
;Declare our variables

Local splitcounter = 0, pntr = -1,chris=1, pntr2 = 0, counter = 0

;	Continue to check for splits until we reach the end of the string
	Local a=0
	While pntr <> 0

		pntr = Instr(datatosplit$, ",", chris)
;	If we found a split then add 1 to the counter
		If pntr > 0 Then splitcounter = splitcounter + 1
	chris=pntr+1
	a=a+1
	Wend

;	Now we have the amount of items (-1) in the string so we can dimension our array

	Dim SplitData$(splitcounter + 1)

;	the first item in the string is handled differently than the rest since there is no comma preceding it

	pntr = 0:pntr2 = Instr(DataToSplit$, ",", 1)
	SplitData$(1) = Left$(DataToSplit$, pntr2 - 1)

;	After splitting off the first item we move on to the rest of the string.

	For counter = 2 To splitcounter

;	These 2 pointers keep track of the beginning and ending of the item we are looking for in the string.

		pntr = pntr2 + 1
		pntr2 = Instr(DataToSplit$, ",", pntr)

;	Now that we have the items location we can pull it from the main string and insert it in to our return array

		SplitData$(counter) = Mid$(DataToSplit$, pntr, (pntr2 - pntr))
	Next

;	Once we have done every instance of the comma delimiters we are left with the very end of the string

;	This also is handled differently than the rest of the string since there is no comma after it.

	SplitData$(counter) = Right$(DataToSplit$, Len(DataToSplit$) - pntr2)

;	Now we can return the # of items located in the string back to the calling program.

	Return (splitcounter + 1)
End Function



chrisnorris007(Posted 2009) [#2]
does using banks speed things up?


Ross C(Posted 2009) [#3]
I don't think so in this instance. I believe the shortest possible packet size is obtained by sending a string.

TBH, unfortunately, there isn't too many people round here that i've noticed, that have done much with multiplayer games, and your unlikely to find much help i'm sorry to say... I found it rather difficult last time round.


chrisnorris007(Posted 2009) [#4]
so do you think sending EVERY time someone moves x,y,z,rx,ry,rz,name,animseq,animframe is too much?


chrisnorris007(Posted 2009) [#5]
if so, whats the better way to handle that?


Matty(Posted 2009) [#6]
What I do in my space combat sim, and it works quite well so far is this:

On the server - every x milliseconds (I've set it to x=100) the server sends an update to all of the clients.

Now, because mine is in 'space' with no obstructing terrain/rooms/scenery I am comfortable with sending the network update for all spaceships (read characters if it were an RPG I guess) to all the clients. You may gain some network performance if you only send data that is relevant to a particular client.

I do a little bit of compression to keep the packets small, and I combine all the position/rotation values for each spaceship into a single packet, splitting it if it goes over about 500bytes.

A basic update packet for my game from the server to the client looks something like this:

ShipID -> 2 byte 'short' (converted to a 2 byte string - there will never be more than 65535 ships in a session as far as I'm concerned.)
X/Y/Z -> 4 byte floats converted to 4 byte strings.
Pitch/Yaw/Roll -> 1 byte for each -> pitch is always between -90 and +90 (I ignore the values after the decimal point) so I simply add 90 to make it between 0 and 180 and store as a single byte. -> Yaw and Roll are always between -180 and +180 (ignoring values after the decimal point), so I add 180 and scale it to fit into a byte (meaning values 0-255 only) by doing this to the angle = 255*((yaw + 180)/360) (same for pitch)
Velocity -> 1 Byte converting a float into a single byte value (scaled assuming a maximum forwards velocity of 30 units per frame this scaled to be out of 255 to fit within a byte.

To answer your question "do you think sending every time someone moves ....is too much" I would say yes, it is too much.

I can only give my opinion based on my own game but here are some other tips:

The server may not need to be told what animation frame / animation sequence is currently being played. If the client performs an animation perform it on their PC, send a message to the server saying that "character x has waved their sword about" which the server then sends to the other clients who can actually see the player with the sword.

I would not send a 'name' to the server every time - it's not changing every time is it? An ID yes, but a 'name' no. No need to send "IAmTheGreatestGoblinKillerOfAllTime" across the network everytime that character turns around to have a look at something.

Some messages are critical and some are allowed to have a bit of error in them. If a player dies or removes something from the world, or spawns something into the world then these are critical and should be 'guaranteed' messages. Other messages are less critical, even position / rotation updates as they can be corrected by a later message.

Another thing, Pitch and Roll are not really all that important for characters walking on level ground, even on sloped ground to a large extent, so do you really need to send the pitch/roll values for your characters every time?

I could write more but I hope that may be a start of sorts.