Multiplayer packets

Blitz3D Forums/Blitz3D Programming/Multiplayer packets

RifRaf(Posted 2009) [#1]
Ok, im getting to the nitty gritty in tiny tanks.. have the server up and clients can get in and kill each other (only have tested 1v1)

My question is packet size and bandwidth.. Currently I have this data
Packet_Movement is 29 bytes per send (think i can improve)
Packet_TurretOrientation is 9 bytes
Packet_Shotfired is 33 bytes. Complete tank info so we sync each shot.


Those are the three basics, the server sends out some of its own such as
GameTick, Scores ect. Not to mention syncing when players leave or join.
Mines exploding send a damage pack of those effected. But all the first
three packets mentioned are situational, or events that dont happen every
second.


The main three packets are 29,33, and 9. I send the main packet 3 times
a second (29x3 = 87 bytes per second per player). the 9byte packet I send
one a second , the 33 byte is sent only when a player fires, and there
are not any rapid fire shots.. quickest reload is 2000 milliseconds(one
game tick from server)

So in all, not counting packets that are not continual Its an average of
225 bytes per 2000 ms per player if the player has the weapon with the
fastest reload, and shoots every chance he/she can.


Do you think 225 bytes per 2000ms is too much if there are 24 players on ?
thats about 5400bytes per 2000ms for the player to handle.

I could drop the limit to 16 players, and the game general bandwidth would drop to around 3600 per 2000ms


Wayne(Posted 2009) [#2]
You didn't indicate what your incoming and outgoing server bandwidth would be.

Like you said you could improve things, for instance could a turret orientation be 0-359 degress or integer value packed into 2 byte string.

I use the pack/unpack stuff from the code archives, here's a snippet from unpacking:

	;-----------------------------------------
	;Position Packet - jx#, jy#, jz#
	If Left(packet$,1)="P" Then 
	
		packet$=Right(packet$,Len(packet$)-1) ; eat the op code
		
		jx#=unpackFloat(Left(packet,4))
		packet$=Right(packet$,Len(packet$)-4)
		
		jy#=unpackFloat(Left(packet,4))
		packet$=Right(packet$,Len(packet$)-4)
		
		jz#=unpackFloat(Left(packet,4))
		packet$=Right(packet$,Len(packet$)-4)



The following are useful from the code archives ( thanks Matty ):
;--------------------------------------------------
;PackInt$(value)
;
;Pack a 4 byte integer value into a 4 byte string

Function PackInt$(value)

A=(value Shr 24) And 255
B=(value Shr 16) And 255
C=(value Shr 8) And 255
D=value And 255
Return Chr$(A)+Chr$(B)+Chr$(C)+Chr$(D)

End Function 

;--------------------------------------------------
;PackShort$(value)
;
;Pack a 2 byte short value into a 2 byte string

Function PackShort$(value)

A=(Value Shr 8) And 255
B=Value And 255
Return Chr$(A)+Chr$(B)
End Function 

;--------------------------------------------------
Function PackByte$(value)
;Pack a 1 byte value into a 1 byte string

Return Chr$(Value And 255)

End Function 

;--------------------------------------------------
Function PackFloat$(value#)
;Pack a 4 byte float into a 4 byte string

Bank=CreateBank(4)
PokeFloat Bank,0,value
A=PeekByte (Bank,0)
B=PeekByte (Bank,1)
C=PeekByte (Bank,2)
D=PeekByte (Bank,3)
FreeBank Bank
Return Chr$(A)+Chr$(B)+Chr$(C)+Chr$(D)

End Function 

;--------------------------------------------------
Function UnPackInt(value$)
;Unpack a 4 byte string into a 4 byte integer

Return (Asc(Mid(value,1,1)) Shl 24) Or (Asc(Mid(value,2,1)) Shl 16) Or (Asc(Mid(value,3,1)) Shl 8) Or (Asc(Mid(value,4,1)) )

End Function

;--------------------------------------------------
Function UnPackShort(value$)
;Unpack a 2 byte string into a 2 byte short

Return (Asc(Mid(Value,1,1)) Shl 8) Or (Asc(Mid(Value,2,1)) )

End Function 

;--------------------------------------------------
Function UnPackByte(value$)
;Unpack a 1 byte string into a 1 byte value
Return Asc(Value)
End Function 

;--------------------------------------------------
Function UnPackFloat#(value$)
;Unpack a 4 byte string into a 4 byte float

Bank=CreateBank(4)
PokeByte Bank,0,Asc(Mid(value,1,1))
PokeByte Bank,1,Asc(Mid(value,2,1))
PokeByte Bank,2,Asc(Mid(value,3,1))
PokeByte Bank,3,Asc(Mid(value,4,1))
fvalue#=PeekFloat(bank,0)

FreeBank bank 
Return fvalue
End Function




RifRaf(Posted 2009) [#3]
Hey Matty, thanks.

But, im already packing everything. Blitzplay pro by default has this ability.

Server Bandwidth I guess will be typical 3mb DSL or equivalent.


Wayne(Posted 2009) [#4]
I'm in the process of adjusting packet protocol for a simple multiplayer racing game.

It uses B3d Collisions, UDP, and some fake physics. Mostly done to show people how one might do multiplayer.


RifRaf(Posted 2009) [#5]
..


RifRaf(Posted 2009) [#6]
Instead of making another topic regarding this project, ill ask here , as its related to the multiplayer portion of Tiny Tanks

The issue - When a tank gets slightly out of sync.. rarely this very slight difference(Wich will correct quickly) can cause the tank to slide off a cliff on the client side, while locally you stay on it.

The solutio so far - I simply do a linepick betwen current and last pos, if I hit a wall I pop the tank back to its "real" position. This works great. BUT.. is there a better way?

If the remote copy of a tank falls off a cliff in game to the ground below. the other player will see the tank slip and jerk back to position quickly. Again, this happens rarely if the player is intentionally trying to make it happen by daring the edge over and over and over while runnign across it and doing circles ..ect. and it self corrects fast, but the small flash back to position can be seen. I can live with it if I must. The primary reason it happens at all, is because ive tried to design communications to be easy and quick so it breaks down like this

-Tank idle, sends rotation for tank(turning without moving orientation)
-Tank Goes from moving to idle state - Send Complete orientation packet.
-Tank is moving.. locally I place a destination point a few meters in
. front of the tank.
. The code then moves tanks to this destination. Also if you hold
. the keys to move down, the destination point is updated in real
. time so the tank just keeps going until you let go of the move key.
. If you turn, it also moves the destination point so you can turn
. and move at the same time as though you are not moving to a
. destination point at all.
. LOCAL SENDS DESTINATION POINT TO SERVER, Server broadcasts this..
. The reason is easy. it removes alot of movement lag, since the
. destination point is always the carrot in front of the horse.. no
. stop go jerking, without splines.
. THIS ALSO REMOVES ALOT OF SYNC ISSUES. because locally you move
. tanks by varied speeds depending on how far they are from incomming
. destination points.. if they are farther move them slightly faster
. and they catch up from any latency


Ok all that above was the message and method for movement.. the turret rotation is sent, but its not relative really , because when you fire a shot, the shot orientations are sent with that packet.

So knowing some of the system.. can anyone think of a better way to handle the initial issue decribed at the top of this message ?

Thanks


Wayne(Posted 2009) [#7]
After thinking about it, If the server or other clients are doing prediction and the predicted path crosses triangles with abs(slope) > 80 degrees, then don't do prediction for that step.

Your solution assumes the tank will drop quickly enough so that a linepick will cause a hit with the wall. I do rather like simple solutions like this one.

Perhaps this is related to a game I once saw where they preprocessed the height map, and recorded all the edges with cliff like slopes.

I'll do some research and see what other solutions might be out there.

Thanks for highlighting this issue.


Wayne(Posted 2009) [#8]
This article has some good info, and suggests we keep bandwidth down to 1-2KB/sec.

http://www.gamasutra.com/view/feature/2326/massively_multiplayer_game_.php


RifRaf(Posted 2009) [#9]
thanks, im still reseraching and experimenting.


BIG BUG(Posted 2009) [#10]
To minimize your "falling tank situation" you could set a small delay before gravity affects enemy tanks.
I would send the tank position more often though. Maybe 10 times per second.

One hint in general: Try to collect packages as much as possible and send them as one.
Each UDP package has a header of 28 bytes which comes additional to your payload.
If all your communication is rootet through one server, you could send the current position of all tanks in one packet.


RifRaf(Posted 2009) [#11]
Well, im pretty sure after a certain size that becomes a problem. While I know there arent too many dialup connections anymore I want this to run on them. I have the falling thing fixed, and I have a pretty darn low bandwidth requirement now wich works out for pretty smooth play so far, but my testing is just my local machines.. 4 players at once

The falling thing was easier than I made it out to be. I just added another peice of information to the destination packet.. the local machines Y position. On the receivers end I test the Y value sent, versus the last received Y value to see if that tank should have fallen, when it didnt. I also check basic distance by a factor of double the distance the tank should travel to account for any "stuck" tank problems that may arrise, although ive not been able to make one stick on a remote machine despite my best efforts.


Chroma(Posted 2009) [#12]
Rifraf, other players on a client shouldn't be able to slide off a cliff...and they shouldn't even have physics applied to them. It should be a straight "put the tank at these coordinates" type thing with interpolation etc. Physics would only be applied to each players tank on his own client, then his info sent to the host and broadcast to other players where his tank is just placed at the new coords with no physics applied etc. The visual effect is the same and also less taxing on the system resources.


xtremegamr(Posted 2009) [#13]
I agree w\what Chroma said. It looks like you're trying to do physics simulations client-side. What I recommend is that you do all physics calculations server-side. Then, send the player coordinates to the client.

Does that make sense to you?


RifRaf(Posted 2009) [#14]
Hey guys, thanks for replying. But ive already went this route , the last several days ive been revamping the movement comms and all gravity and velocities are removed from remote players. the vertical movement is not as liquid as it was, but the possibility of sync issues is now gone. I have not uploaded this new version yet, theres still alot on my list to do before I upload.

However phycics are done client side atm, but only for the local player. i cannot do all the physics server side, its just a console app.


Chroma(Posted 2009) [#15]
Awesome! If it's not as smooth as you are liking..maybe the timing technique itself is the cause. There's an article called Fix Your Timestep from Gaffer On Games that specifically tells you how to do gametime over multiplayer and it's not hard to implement. I have the system converted to bmax if you want to check it out.


RifRaf(Posted 2009) [#16]
Well Ive gotten it silky smooth now, and accurate as can be.. plus I only have to send the position packet once every 32 logic loops, or in other words about 1.5 times per second.(At 50 fps logic) ! Wich is a vast improvement over the last version, wich was a packet every 6 logic loops.. The bandwidth has gone way down and the movement and smoothness has gone way up.


Chroma(Posted 2009) [#17]
1.5 a sec is pretty decent. Unreal sent a position update once every second. If you want some really good reading on packet construction, google the unreal multiplayer code. It's a very interesting and I learned a lot from it.


Chroma(Posted 2009) [#18]
But actually, I've been doing some testing on timing code and it seems that the tween code from the castle demo is the smoothest. I did the Gaffer thing and while it works...it still suffer from the stutters. Prolly something to do with float inaccuracy. Who knows. /shrug


RifRaf(Posted 2009) [#19]
im using tweening and delta timing. Tweening my main fps, and deltatiming extra sensative visuals , because tweening only works if your system meets the minimun requirements to push the required fps for the game logic. Below that it will attempt to hit the desired fps, but can fall behind. Deltatiming can keep this in check, and step the visuals in the proper increments to stay in sync.. This has nothing to do with the data that is most important, wich the server tracks. But rather visual cues on the client end.


Chroma(Posted 2009) [#20]
Are you using a threaded server a la BMax or straight For/Next to loop through all the players?

I wish someone would do something with threads. Would each player get his own thread serverside? Not sure how that works TBH.


RifRaf(Posted 2009) [#21]
No threads atm. But it runs really well, you can jump in anytime and check it out, servers always up unless im updating it.


Guy Fawkes(Posted 2009) [#22]
why not try realm crafter? :)

It suits exactly what ur looking for, for multiplayer games at least.

and they rewrote it in c++.

so its like fast :P

http://realmcrafter.com/

if you're interested mate :)

~DS~


RifRaf(Posted 2009) [#23]
It suits exactly what ur looking for, for multiplayer games at least.


While I appreciate your effort to help, im afraid you couldnt me more wrong on that point. RC, is a mmo engnie.

Apples and Oranges,although rc was made very well,it its not designed for action games.


Guy Fawkes(Posted 2009) [#24]
ah.. ok..

sorry