Fast Multiplayer?

Blitz3D Forums/Blitz3D Programming/Fast Multiplayer?

Tibit(Posted 2004) [#1]
My question is this: Is there a way in Blitz3D to program fastpeaced action multiplayer games. Such as a simple quake game?
I have tried working with my own UDP_Library for quite long but I can't make it work! There is always something wrong, especially speed! I tried to send the mouse pointer (infullscreen) to another computer over LAN, laggy as hell. I started out with BlitzPlay Lite, but it proved to be too slow so I tried making my own lib(not that my lib is faster^^).

Am I trying to acheive something impossible? Something that no one have ever really succeded at? Fast multiplayer exsists, cause I have played alot of games that work fine with even more than 8 people. I'm not aiming to make my game lag-free for modem users, I'm fine with a limit to 100Mbit connections. Still I can't, even with BlitzPlay, make a simple [send/receive -> x/y/dir for each player] to NOT lag when three players is in the game...
Anyone who can show me a solution? (test demo/game TOTALY lag-free) A /tutorial/link/sample code/ on network basics?
//Thanks in advance Wave


Bot Builder(Posted 2004) [#2]
You have to understand that multiplayer games employ many tricks to run lag free. For instance, usually you broadcast your x/y/dir less than the framerate. Maybe 10-20 times per second, possibly less. That might even be over kill. You can also have systems in which the server only gives a player info it needs to know. It doesn't need to know what happened in some far away unseen place. You can use splines to make motion appear smooth even when the data is sparse.


ZombieWoof(Posted 2004) [#3]
Take a look at Cubic Splines and other interpolation techniques.

The basic idea is to track the network lag either by timestamps or other means provided by your network library. The lag is how far in the past an update happened, you then project the update slightly into the future, and adjust the entity motion towards the future location.

Note the seperation between network updates and how the local instance of a remote entity moves -- there is no way to sync everything up using the net updates to control motion directly, so the next best thing is to "guess" where the entity will be, then try to get the local instance to "catch up"

So an entity never follows exactly the same path on your screen as it does on the screen of the player controlling the entity, but its not too far off.


Tibit(Posted 2004) [#4]
Do you have any rekommended links or examples on multiplayer tricks?

Maybe I got Interpolation techniques wrong but isn't that just a technique to use if there is serious latency? I mean, how much can I really decrease packet sending by using such techniques? Seems like very much work for just getting the possition of an object to update. I'll still take a look at it. Any links?

If I adjust the motion using splines, and it comes out wrong, won't that make lag more visiable? Like if you do drastic changes in direction N speed.


Techlord(Posted 2004) [#5]
Cubic Splines interpolation can be useful regardless. I'm curious about how your messenging between server / client? How many bytes needed to convey update? Using any compression?


Tibit(Posted 2004) [#6]
I use UDP, I have tried 2 ways. First to send X/y/dir in one package, then when received divde it up to X,y,dir.

The other method was to send x,y,dir separetly as different msgs(this is faster for me but it shouldn't be)

"I'm curious about how your messenging between server / client?" Here is how I do it:
From my_Network library.. (With blitzplay I get a better speed but it's only sufficient for MAX 2 players from what I have seen so far)
The size of the package are the UDP header+2float+1Int

:		For C.Client = Each Client ;Send to each computer 
								
			If C\Net_id <> My_Net_ID  ; broadcast to all players	
				WriteByte(UDP_Stream, MessageType%)	;One byte, Max 255	This is the "type" of the msg
				WriteByte(UDP_Stream, My_Net_ID%) ;Send your id in the message
							
					If MessageData <> ""
						For T = 1 To Len(MessageData$)
							WriteByte ( UDP_Stream, Asc (  Mid$(MessageData$,T,1)  )  )  ;This is the data of the msg
						Next
					EndIf
			;	DebugLog" Message sent to IP : "+C\IP+" and Port : "+C\Port 		
				SendUDPMsg( UDP_Stream, C\IP, C\Port );send data stream to Computer
			End If
Next



Tibit(Posted 2004) [#7]
Maybe I should add that the idea I have for my Tank game, is for it to be very FAST, You will turn/accelerate/fire/use special abilities/ as fast as possible! This means Splines will not solve the problem
(I will still use them as they do make enemy movment a bit smother) And it is meant to be a Team-based multiplayer game. I want 4v4 games to work =)


Bot Builder(Posted 2004) [#8]
You could probably shave of 2 bytes by making the direction a short integer. I doubt your variable goes higher than 65535.

You could try some forms of compression but it probably wouldn't work very well (probably make it slower) unless you were using a server-client system.

Anyway, I would like to point out that internet speeds might be the reason there aren't any (that I know of) really FAST tank games. It's probably not possible to achieve you goal of super fast near instantaneous play without some insane algorithms and really fast connections.


ZombieWoof(Posted 2004) [#9]
You have to accept the fact that every player is operating with their own framework for time and motion, and plan for it in your code... There is no way to achieve fast reaction without a LAN connection.

If you want internet play, you code will have to cope with 100-500ms lag times, and with the idea that each player is really playing in an independent game world that has bots controlled over the network (i.e. remote players).


Tibit(Posted 2004) [#10]
bot builder, how do I make short intergers in blitz?

And second I'm trying to acheive a fast game play in a LAN where TCP/IP pings (latency) are Below 0 milisecs and a bandwith of ~100Mbit. But I'll try to do it with deadreconing and cubic splines, if noone else has any suggestion?

Thanks for reply /wave


Rob(Posted 2004) [#11]
I recommend blitzplay library for blitz. www.blitzcoder.com/blitzplay

then learn some cubic spline tricks. otacon posted a fairly neat cubic spline function for me the other week. When we're not flirting with each other that is.


Tibit(Posted 2004) [#12]
I have checked up on cubic splines, seems lika a smart way to go, but what am I supposed to do with that function? Ok, I get a smoth line, but exatly, how will it make my tank go that way. I don't see how to implend it in network code. And I'm using BlitzPlay lol. I just can't get it to work fast enought.


ZombieWoof(Posted 2004) [#13]
the cubic spline is one technique for predicting the future location of the entity.. you then figure out the forces to apply to the local instance to make it reach the predicted location at the correct time....

The local entity continues using the using the computed forces until the next network update, then you recompute, using the new past (network entity update), current (local entity position), and future (predicted network entity location, reverse computed forces for local entity)


Tibit(Posted 2004) [#14]
So I need a function :P\X = Spline(P\X,P\Xvel);which returns his NewX, right?

How am I supposed to get NewX? What's the function? Do I have to update both his X and Y at the same time? And if so, how do I do that?

Rob are you willing to share some of your spline tricks?


ZombieWoof(Posted 2004) [#15]
This setup assumes you are using elapsed time to control distance moved per world update, so framerate isnt an issue.

also it is assumed that everything is expressed as forces applied to the entity, tho rotation forces arent handled, they can be easily added.

[vector (x,y,z) values]
P=position
V=force vector
N=Network data

[scalar (integer) values]
L=Local Entity
F=Future Entity
T=Timestamp

compute future timestamp
FT = LT + (prediction time offset, say 0.25 secs)
compute future position
FP = NP + (NV * (FT - NT))
compute vectors from local position to future position
LV = (FP - LP) / (FT - LT)
optionally use the computed local forces to rotate the entity to face in the direction of movement.

You will need some extra work to synchronize timestamps at net connect to make this work. Its not cubic splines, but it gets the job done.


Tibit(Posted 2004) [#16]
Ok I don't use vectors to calculate speed right now. But that's not a problem. Vectors are easy to implend.

"elapsed time to control distance moved per world update"
Here is where I get lost. It's probably obvious to you but I just don't understand. Sorry. Do you have any code of motion controlled over time, or about this. Time is my weak point here, Timestamp?

Thanks for the help so far!


ZombieWoof(Posted 2004) [#17]
everything is based on time :) speed = distance/unit time, if your unit time is millisecs (since thats what the blitz system clock runs in), then a force vector is how much an entity travels in one millisecond.

the difference between the timestamp on a net packet, and the current time is how far in the past that the remote player did something (moved, fired, etc), its also how much lag that player is currently getting off the net. (thats what makes this so kewl -- automatically smooths out the lag)

so given the time, force and position in the past, you can predict the entities position some time in the future. the distance vector from the local entity representation to the predicted position divided by the prediction time = the force that must be applied to the local entity to converge the local position with the predicted future position at the correct time.



In the 1st frame, each red dot + line represents the position and vectors sent over the net. In the 2nd frame, the green dots represent the predecited locations a short time in the future, in the 3rd frame, the forces that must be applied to the local entity.

The following code isnt adapted to the network, but shows the basic idea of using time to control motion... ticks is the number of milliseconds since the last RenderWorld. Basically makes motion independent of frame rate. (Tho I never run a game update cycle any faster than the vertical refresh interval of the monitor - people that run 200 fps update cycles are wasting cpu on generating graphics that the player will never "see")

Code also includes my smooth jump routine.. player jumps up, gravity drags them down, and it includes a recovery time before they can next jump. (that part of the code needs to be fixed up for tick handling, its setup for 60fps right now)

Global walkspeed# = 0.007
Global turnspeed# = 0.07

Global jumpcnt = 0;

Function Update()
	
	MoveEntity camera_pivot,0,0.2,0 ; gravity
	If KBS_Action("walk") MoveEntity camera_pivot,0,0,walkspeed# * ticks
	If KBS_Action("back") MoveEntity camera_pivot,0,0,-walkspeed# * ticks
	
	If KBS_Action("left") TurnEntity camera_pivot,0,turnspeed# * ticks,0
	If KBS_Action("right") TurnEntity camera_pivot,0,-turnspeed# * ticks,0

	If KBS_Action("jump") And jumpcnt = 0
		jumpcnt=40
	EndIf
		
	If jumpcnt > 0 
		If (jumpcnt > 35)
			MoveEntity camera_pivot,0,0.6,0
		EndIf
		jumpcnt = jumpcnt - 1
	EndIf
	
	MoveEntity camera_pivot,0,-0.3,0 ; gravity
	
End Function



Tibit(Posted 2004) [#18]
Ok I'm starting to getting the idea =) Now how do I sync two computers. I mean I can't use millisecs(), I have to somehow create a timer, in game.

This is my idea (tell me if I'm wrong)
When game starts:

Do.. Player/Timer = Player/Timer +1

receive Clients\Timers and pick a time in the future when all client should reset.
And then all client should reset at the same time to get in sync, substracting the lag (time for msg to be sent and retreived). Then I can continusly do these resets and make sure every player is in sync. Their Timer = My Timer. Then I can implend Quibic Splines or your version of predicting their possition at a given time in future, provided by the last packet received. If the package contains the players possition, then the timeStamp provided in the msg would tell me that X ticks ago player was at X1,Y1 and he is probably at X2,Y2 now. <-- calculated using splines.

Is the teori so far, right?

Now the simple question of.. How to code this? Where to start?

I really want to get the hang of this! Working hard //Wave~


ZombieWoof(Posted 2004) [#19]
you should actually only have to compute a millisecs offset once at net connection -- bit of a pain in the arse -- start off sending timestamp packets from server to client, and the client responding with its timestamp/... time the round trip, if you get 3 or 4 in a row that are very close together in elapsed transit time, average the differences between server and client timestamps, adjust for average lag, then send a packet to the slave telling it what offset to use.

This is the same basic idea used by unix the unix rtime command to remotely sync a computer clock.


Tibit(Posted 2004) [#20]
You mean I only need to know how many millisecs it takes (in avg) for packages to be returned?

Host sends testString

Client receives it and send it back. + it starts it' Millisecs timer.

Host receives the response and checks how long time has passed in millisecs()
Host now sends antoher test pack to the CLient with this time

The client Receivs this package and checks it's millisecstimer to the package. When done 3-4 times you can get an average.

Now each plackaet recieved by either host or client is known to have this latency and therefor you calculate where the other players most probably are when package was received.


ZombieWoof(Posted 2004) [#21]
latency can change through the life of the connection.. you need to figure a time offset to get each machine to agree on a timestamp.. then timestamped packets tell you how much lag you are getting at any moment....


Tibit(Posted 2004) [#22]
What is a timestamp? Is timestamp realtive to each computer or are they supposed to be same for all connected clients?


ZombieWoof(Posted 2004) [#23]
a timestamp is time in millisecs -- the problem is to determine the offset between the clocks on 2 computers so that, after adjustment, they are running on as close to the same clock as possible, preferably within 2 or 3 milisecs of each other

server.time = client.time + adjustment

then the client timestamps every packet (4 bytes of overhead) with time+adjustment, then the server had no problem figuring when in its time the packet was sent

on recieve packets, clients do timestamp-adjustment to change the timestamp to their local clock


Tibit(Posted 2004) [#24]
I don't get it, millisecs() returns the CPUclock in millisecound. But who say that my clock ain't going 2h 34min 3days 23sec and 123ms Wrong. Then the client will think the packet has an Enormous lag. Or should I set the computer clock somehow? If I do A=millisecs() A gets the value of 36632413. Now let say I send and receive a package I can check millisecs() again to see how it took for the client to reply, this is the latency,right? But this is not the same as a timestamp. So what I should sent in each packet is my A=millisecs()+LastpackedDelay?? But what should the client do with this millisecs_int. If the client substracts his millisecs() from the millisecs()_int received in the last package then he'll get a huge number or?, it seems like I'm walking in circles =)
If you have any good reasourses/links I'll be happy to read about the subject,I have searched, but I haven't found anything really useful.

Still trying// Wave~


ZombieWoof(Posted 2004) [#25]
which is why you jump through hoops at network connect to figure out the adjustment value to sync the clocks :)

here is a one step sync up... it assumes lag local->remote = lag remote->local (not always true, packets can take radically different routes in each direction)

server:
local_t1 = time()
send packet(request time)
remot_tm = recieve packet
local_t2 = time()
lag = (local_t2-local_t1)/2
avg = (local_t2+local_t1)/2
adjust = (avg - remot_tm)
send packet(adjust)

client:
serve_req = recieve packet
send packet(local time)
serve_adj = recieve packet

remote then timestamps all packets with its local time + adjust (which should be very close to server time)