Speeding up Sockets

BlitzMax Forums/BlitzMax Programming/Speeding up Sockets

foosh(Posted 2008) [#1]
So I'm working an a massively multiplayer online game client, and I'm running into a little snag. Right now (in the early stages), every time a player moves, the new coordinates are sent to every user in the game. The game runs VERY slow with two players, and I was wondering if you could give me any pointers on how to optimize my code.

I've included a link to an archive with my code. You need python to run the server. To get the game running, cd into the server directory and type "python simpleserver.py". Then launch client and client copy to test it out with 2 users online. You control using arrow keys.

http://www.gamemakersgarage.com/downloads/vulcan.zip

You want to compile client.bmx if you're going to compile. network.bmx controls networking, and player.bmx controls player functions.

Thank you very much in advance for your help, and feel free to use the code you see in your games.

foosh


pls(Posted 2008) [#2]
Hi foosh!

I took a look at your code. There are several things you should probably change or at least reconsider:

- Do not send messages updating coordinates every time they change, send them a couple of times a second instead... a quick change on you main loop in order to send /receive positions only 5 times a second keeps your famerate higher.

Local elapsed:Int=MilliSecs()

While Not KeyDown(Key_ESCAPE) Or AppTerminate()
	Cls
	time_passed_secs = stop-start
	time_passed_secs = time_passed_secs/1000
	start = MilliSecs()
	DrawText "fps: " + (time_passed_secs*1000),200,465
	main_player.UpdateState()
	For Local o:TGameObject=EachIn GameObjectList
		o.DrawSelf()
	Next
	DrawText "alias: " + player_name,0,465
	DrawText "x,y: " + main_player.X+","+main_player.Y,400,465
	Flip
	stop = MilliSecs()
	If(elapsed+200<MilliSecs())
		If Not (main_player.X=main_player.pX And main_player.Y=main_player.pY) Then game_network.sendCoords(main_player.X,main_player.Y)
		game_network.updateMap()
		elapsed=MilliSecs()
	End If
Wend


- While receiving data from the network you are getting loads of intermediate positions from the remote objects... and you are probably only interested in the "latest one" and can easily calculate the intermediate points to make remote objects move smoother on the local display. Some sample code to ilustrate it (it probably breaks the function purpose but try it out to see what I mean):

Also, going thru the objects list several times, for each and every single one of those intermediate changes, was killing your your framerate. You would probably do much better if you got a notification from the game server on "when" and "what" objects changed, so you would request the latest (not the history) info on the ones that "interest" you.

Method updateMap()
	Local other_player:TOtherPlayer
	Local mapState:String
	Local parseCoords:String[]
	Local x,y,id:Int
	Local name:String
		
	Local last:Int[][]	
	Local unfinished:Int=1
	Local updated:Int=0
	
	While(unfinished=1)
		If SocketReadAvail(socket) > 0 Then
			mapState = ReadLine(stream)
			If mapState Then
				parseCoords = mapState.split(":")
				id = parseCoords[0].toint()
				parseCoords = parseCoords[1].split(",")
				x = Trim(parseCoords[0]).toint()
				y = Trim(parseCoords[1]).toint()
				name = Trim(parseCoords[2])
				Print x+"*"+y+"*"+name+"*"+id+"*"
				updated =+ 1
			End If
		Else
			If(updated)
				For Local o:TGameObject=EachIn GameObjectList
					If o.ID = id Then
						o.X = x
						o.Y = y
					Else
						other_player = TOtherPlayer.Create(name,file_server+"playerorange.png",x,y,id)
					End If
				Next
			End If
			unfinished=0
		End If
	Wend
End Method


- A echo server may be used for game room handling... but it is NOT your best option for keeping track of remote objects in real time... use must use UDP for that.

PLS

PS: "I'm working an a massively multiplayer online game client"... you will fail (sorry!). Code a few smaller networks games (real time or turn based), have fun, and give yourself a chance of finishing a game.


foosh(Posted 2008) [#3]
I know I may not finish, but regardless I wanna get as far as I can, and then perhaps use what I have to make a fun game. It's been a great learning experience so far, and I'm enjoying it!

I was considering porting my code to C++ for threading capabilities, etc... but the overhead is just too much, so I just wanna push the limits of BlitzMax.

Thank you very much for your help! I'll be implementing your suggestions, and I also think I found a few ways to optimize my own code since I last posted.

I'll post occasionally about progress. (it'll be slow) =)

Thanks a lot!
foosh


Dreamora(Posted 2008) [#4]
The main problem for the slowdown is the fact that you trash the socket totally.

WriteLine / ReadLine are a TOTAL no go for realtime!
They work by reading byte per byte till they find an end line, which is about the worst thing you can do!

Make your application write packets.
A packets contains:

1. Unique ID definining the content
1.2. Size of the packet in whole if it has varying sizes (for something that transfers strings like chat for example)
2. The content as specifyed by the Unique ID

For example for the position update you would have a

const P_UpdatePosition:int = 1


and the packet structure would be
byte | float | float | float | float | float | float

or in non typed:
P_UpdatePosition | X | Y | Z | RotationX | RotationY | RotationZ

the | are not written into the package, they are only to visually seperate the different byte parts.
That way the server / client just has to:
1. Check if bytes are available
2. if yes, read the first byte to get the type of package
3. Wait till as many bytes are at least available as the message size is. In above case this would mean wait till at least 24 bytes are available
4. ReadBytes and read the whole remaining message as 1 memory block.
5. parse the data out of the block in the way you prefer (TBank, memcopy into a TPositionUpdate type instance, ...)

If your game is ground based with no free 3D movement, RotationX and RotationZ can be removed theoretically.
if it is 2D you can remove the Z as well