Multiplayer using directplay

Blitz3D Forums/Blitz3D Programming/Multiplayer using directplay

yoshark(Posted 2012) [#1]
I have already coded a pretty cool game that is a single player fps. I have recently wanted to make my game multiplayer from more than one computer...no mmofps, just a small game that can accept 2 or three people. I have found code from user 'b32' but the code is set for a 2d simple game. I'm trying to get the game to 3d. I understand the whole messaging and client thing, but I'm not sure how to code it. Here's b32's code for directplay...

[bbcode]
;-----------------------------------------------------------------------------------
; DirectPlay example
;-----------------------------------------------------------------------------------
;run this example twice, once as a server and once as a client.

Graphics 800, 600, 0, 3
SetBuffer BackBuffer()

;start network game
Select StartNetGame()
Case 0
End
Case 1
Print "Joined!"
Case 2
Print "Server!"
End Select

;create player
player = CreateNetPlayer("Bram")

x = Rand(400) + 200
y = Rand(300) + 150

;main loop
While Not KeyHit(1)

Cls

;cursor keys to move around
If KeyDown(203) Then x = x - 10
If KeyDown(205) Then x = x + 10
If KeyDown(200) Then y = y - 10
If KeyDown(208) Then y = y + 10

;send message
io = MilliSecs() / 50
If io > ioi Then
SendNetMsg 1, y + x * 800, player, 0, 1
ioi = io
End If

;incoming messages
If RecvNetMsg() Then
msgType = NetMsgType()
msgFrom = NetMsgFrom()
msgData = NetMsgData$()
a# = msgData / 800
b# = msgData Mod 800
End If

;draw player
Color 255, 255, 0
Oval x, y, 20, 20, True
;draw player
Color 255, 0, 0
Oval a, b, 20, 20, True

Flip

Wend

End
[/bbcode]

and that I have tested and works fine on 2 computers....

here is my code...for my fps...

[bbcode]
;sandbox city
;------------------

Graphics3D 1280,960
SetBuffer BackBuffer()

;types
type_player=1
type_city=2
type_boundary=3

;camera
camera=CreateCamera()
Cam_range=100000
EntityType camera,type_player

;light
light=CreateLight()

;city
city=LoadMesh("metro 1.3ds")
tex=LoadTexture("metro01.jpg","metro02.jpg")
EntityTexture city,tex
PositionEntity city,0,-30,+100
ScaleEntity city,10,10,10
EntityType city,type_city

;soldier
soldier=LoadMesh("soldier.3ds")
tex3=LoadTexture("swattex.jpg")
RotateEntity soldier,0,180,0
ScaleEntity soldier,.08,.08,.08
EntityTexture soldier,tex3
PositionEntity soldier,EntityX(camera),EntityY(camera)-5,EntityZ(camera)+.5
EntityParent soldier,camera

;boundary
boundary=CreateCube()
PositionEntity boundary,0,0,0
ScaleEntity boundary,2200,2200,2200
FlipMesh boundary
EntityAlpha boundary,.1
EntityType boundary,type_boundary

;sky
sky=CreateSphere()
tex2=LoadTexture("sky9.jpg")
EntityTexture sky,tex2
ScaleEntity sky,100000,100000,100000
FlipMesh sky

;plane
ground=CreatePlane()
PositionEntity ground,0,-30,0
EntityColor ground,0,0,0

;collisions
Collisions type_player,type_city,2,2
Collisions type_player,type_boundary,2,1
EntityRadius camera,5

HidePointer

;pro run
velocity#=0

While Not KeyDown(1)

;velocity
If CountCollisions (camera)
time=0
velocity#=0
End If
If KeyHit(57) Then
time=time+1
velocity#=7-.01*time
TranslateEntity camera,0,velocity#,0
End If

yaw#=yaw#-.01
roll#=roll#-.01
RotateEntity sky,0,yaw#,roll#

time=time+1
velocity#=velocity#-.01*time
TranslateEntity camera,0,velocity#,0

If KeyDown(17)=True Then MoveEntity camera,0,0,5
If KeyDown(17)=True Then cam_range=cam_range+1
CameraRange camera,1,cam_range
If KeyDown(30)=True Then MoveEntity camera,-5,0,0
If KeyDown(31)=True Then MoveEntity camera,0,0,-5
If KeyDown(32)=True Then MoveEntity camera,5,0,0

UpdateWorld

RenderWorld

mxs#=mxs#+MouseXSpeed()/4
mys#=mys#+MouseYSpeed()/4

If mxs#<0 Then mxs#=360
If mxs#>360 Then mxs#=0
If mys#>80 Then mys#=80
If mys#<-80 Then mys#=-80

RotateEntity camera,mys#,-mxs#,0
MoveMouse 400,300

Flip

Wend
End
[/bbcode]

I understand that I will first need to create a dialog box asking to join a server....but I am asking how to make the soldier's movements and positions become transfered so another soldier can recieve a message of the location (client / server)...Please help.


RifRaf(Posted 2012) [#2]
Basically you decide how often to send them, in tiny tanks I am sending them (if memory is correct, ive changed it a few times) about 5 times per second, and while that sounds like a lot. Games like counterstrike send about 10 or more movement/position packets per second.

The real trick is to get your interpolation working well on the client side so that even though the packet data has "missing positions" your client will interpolate or "morph" positions and rotations between these missing positions so it is still a smooth movement.

Compress the data as much as possible before sending, and uncompress it where you can.

My game is Client-->Server--->ALL Clients. This allows me to bundle messages from multiple clients before retransmitting and reducing the number of packets received(per client, not server) a great deal.

The server being the middle man also allows more data checking, for example even if you fire a rocket locally, the server does the check to see if it detonated close to a rival, and tells everyone about the impact. even the player that fired the rocket. Another example is for firing and reloading, this is all checked server side as well, you press the fire button and the server will reply with a success shot or a restriction where needed (ie.. still reloading ect)

TIP 2:
Its important to have fixed rate logic or things will get wacky, in blitz3d I couldnt send position packets for every projectile so I send firing postion, and rotation, and time fired. Then fixed rate logic on each client keeps things the same from there. The projectiles are tracked and when impacted a new packet of impact is sent so even if they are a few (game feet) difference on a machine the exact impact coordinates are resent to re sync that impact.

For my game I use rendertweening, and in my projectile management code I also have delta timing in there.. In all of my tests projectiles on each client (even with simulated packet loss of 35%) stay in the same positions.

Last edited 2012

Last edited 2012


yoshark(Posted 2012) [#3]
Thank you for your extensive help and I will try what you suggested as soon as possible. I downloaded your Tiny Tanks online game, it looks really cool but nobody is on it, I'll try to get my friends to download this and play it..


RifRaf(Posted 2013) [#4]
sweet, let me know if you get a game going.. Would join in myself if possible.


_PJ_(Posted 2013) [#5]
Has anyone any experience of how programs using DirectPlay work (or not) with DX11 ?

Since all lthe DirectPlay code is deprecated, is it still supported by COM libraries?


RifRaf(Posted 2013) [#6]
Holy moly, I missed the topic entirely.

Yoshark, I used UDP for my game not direct play.
There are many libraries available for UPD games in Blitz3D , rottnett is a good one, and blitzplay is good too.

I used TCP WEBGET for my file updating and such.


John Blackledge(Posted 2013) [#7]
Wow RifRaf, that was a stunning description of comms, and what's involved and what you have achieved in TinyTanks.
I truly felt like applauding.


RifRaf(Posted 2013) [#8]
Well if ever asked I could go on and on really. I learned soo much making Tiny Tanks Online. A lot more than I bargained for, troubleshooting online action games can take a lot out of you. Especially when your support group is less than three people. If it werent for KingNothings help playing and bug hunting (used to be his name here) I would not have had a stable game at all. Im always looking for someone who is making online games with a blitz product to share with or learn from.

Edit : Tip3.. dont use molebox, that thing caused me so much trouble with false virus flags I almost pulled my hair out. In the end I ditched Molebox and reworked my own compression and CRC system.

Last edited 2013


Banshee(Posted 2013) [#9]
Packets per second is outright wrong unless you are using an analogue controller (and even then...)

In most cases when doing multiplayer I keep track of system time in a variable for each individual network component, in this case, a player.

time = millisecs()
sendNextPacketAt = time + 30000
if time > sendNextPacketAt then SendAPacket()


Yes, 30 seconds... But here is the thing, when a player changes their control state, or is struck for damage etc. I change the sendNextPacketAt to 0.

The reason is simple, if I send a data packet 5 times I second I am sending every 200ms. If I press a control key just after a packet it could be 199ms before it is sent, add in some network latency over the internet (or even local network is not 0ms) and suddenly it takes quarter of a second for the other machine to respond.

This is insanity, but it is the standard approach. However, but sending the packet timing interval to "RIGHT NOW" by making it 0, you send that information straight away so the only lag is literally the ping time and the speed of your own program code to display that result.

-*-

My own method of sending is only to send the data required to recreate what has happened, ie: position in 3D space (often Y isnt needed but that depends upon map design), bearing of the player, and the momentum of the player. And probably some animation information (I would be happy with current sequence ID rather than current frame in most cases).

The goal is not to recreate your full physics system for each player, if an AI you dont need the AI logic running on both machines. You just need to recreate the movement.

So in effect the remote machine is effectively serving a replay, rather than "a gameplay" if that makes sense.


RifRaf(Posted 2013) [#10]

Packets per second is outright wrong


not sure what you would say that? state changes are easy to send off as needed but I was talking about an action game, and position data not state data. Jumping, Firing, typing, and many more things can be considered state.. im my several years working with this the only practical dependable way to translate fast motion across a network is with continual streaming position data.. I would like to see your game in action and a snippet of your position code, this could perhaps show me something I have not seen before.. although in my research and testing my own game I have seen a lot, I even looked over and counterstrike and a few other popular multiplayer games code where it was available to view



time = millisecs()
sendNextPacketAt = time + 30000
if time > sendNextPacketAt then SendAPacket()


This is not uncommon type of usage. However you may want to replace millisecs with a Millisecs2() command (or similar timer scaling.) if you plan on running a server for a long time.

Global Game_StartTime#=millisecs()
Function Millisecs2#()
return ( MilliSecs()-game_starttime# )
End Function 


here is my code from Tiny Tanks Online for movement/position packets

	If NetSendFreq_Cnt% = NETSendFreq_MAX And pSTATE=0 Then ;WAS A CHECK FOR FINISHNEXTMAP=0 AS WELL
        maxtime#=millisecs2()-sendtime#
        sendtime#=millisecs2()
            ;;if our chat state changes , show it over the tank
            If home\chatstate<>home\chatstateold 
            	home\chatstateold=home\chatstate 
            	If home\chatstate And 2 Then
               	   EntityTexture home\chatplate,chatstate_Texture(1)
                   ShowEntity home\chatplate
               ElseIf home\chatstate And 4 Then
                   EntityTexture home\chatplate,chatstate_Texture(2)
                   ShowEntity home\chatplate
               Else
                  HideEntity home\chatplate
               EndIf
           EndIf
           ;;create and send off packet, including any state changes/values
     	packet$=createpositionpacket(LocalTank)
        Message(HOST_ID,home\NETID,msgDestinationSuper%,packet$,False)
	EndIf



Last edited 2013


RemiD(Posted 2013) [#11]
RifRaf>>I don't understand something.

In my current project, i have organized the routines like this :

UserInput()
CollidersUpdate()
UserLogic1()
NpcsLogic1()
WeaponsLogic1()
ProjectilesLogic1()
ParticlesLogic1()
Updateworld()
UserLogic2()
NpcsLogic2()
WeaponsLogic2()
ProjectilesLogic2()
ParticlesLogic2() ;optional it depends if the particle uses a collider and requires collision calculation+update
HideShowEntities()
AnimationsUpdate()
Renderworld()
HUDUpdate() or GUIUpdate()
Flip()
SoundsUpdate()

As you can see i update the logic of the moving entities before the collisions calculation+update and after the collisions calculation+update. This is because it is only possible to retrieve the informations about the collisions that have happened only after UpdateWorld()

Does this mean that i will have to send some datas on the server twice per loop ?
Or only the state/orientation/movement before the collisions calculation+update and calculate the collisions on the server and update the states on the server and send back the states to the clients ?

Can you give more details about this please ?


RifRaf(Posted 2013) [#12]
No I would not think you would need to send position data twice, just once after the movement of the player is completed. If you do it continually X times per second as I do then it will self correct from small variances of the recipients end via interpolation.

I cant tell whats happening exactly there, but you shouldnt use collisions on remote clients, let each local client handle its own physics and just transmit its coordinates. Performing local physics for remote players, can give you the "jitter" or "bounce back" effect on remote player movement.

Hope that makes sense, or better yet I hope I understood you question.

EDIT: You could put your comms for position at the beginning of your loop if you wanted to, that would in fact be send directly after the previous loop and all its movement.

Last edited 2013


RemiD(Posted 2013) [#13]
So if i understand correctly i only have to update the orientation/movement of the user and of the weapons/projectiles used/thrown by the user, then i send these datas (state, orientation, position) to the server, then the server receives the datas of all users, calculates the collisions, decides what is the state of each entity and send back these results to the users ?

If yes, can you explain how to code a "server" that manages this ? Or maybe you have links or code examples ?
And where do you put this "server", i guess it is a program that runs on its own and communicates with the client programs. Yes ? No ?


RifRaf(Posted 2013) [#14]
RemiD,

Basically yes,

You could do real poly collisions server side if your server was 3D and had dummy models for everyone and level geometry and such, but my example is with a 2d server as a redistribution center of messages.

With a 2D server, you can have xyz positions, speed and any other data you need and run some basic math to do checks based on distance, line intersects and other neat things to tell if things have collided between each new position update.

Tip4.
In Tiny Tanks Online my projectiles geometry collisions are tracked by their shooter (player to player impacts are server checked) to see if they touch something, or for any other reason need to explode/expire.

They then send the hit data to the server with xyz positions of the impact. The server checks the impact and does a check to make sure this impact falls within reason from the recorded original firing position of the projectile vs time allowed to travel, if it does fall within reason the impact is recorded, and sent to all players.. each client will then display the proper explosion, the server will send out any health changes or damage done this is never calculated client side, as mentioned the server has the real time positions, health, and states of all clients so if sees an explosion it can tell who was hit by it, adjust health and send the new health information to the clients

Last edited 2013


*(Posted 2013) [#15]
Reading from the first post and quickly flicking through (bit hard to read on a ipod) i wouldnt use directplay i would use tcp/ip or udp networking quite simply because directplay was 'removed' from dx 9 so you games wont work properly on any of todays os's.

There are apps that compensate for it like hamatchi but its not ideal.


_PJ_(Posted 2013) [#16]
Thanks, EdZup - that pretty much answers my earlier question :)