Python networking sync help..!

Please take the time to read through this! I appreciate it.

Well, this wasn’t planned at all, it all started as a simple test which you can read all about here if you wish. So after the first test was a complete success i started to think “hmm, what if i make the game as planned, but have it online?” then before making any decision i made another test which was a map to walk around in, i encountered some problems the main one being you couldn’t see other players. After analysing the problem for hours on end i finally solved it! So after that worked great i then integrated this set-up into what i had so far of Z Day, made another test and low and behold it worked absolutely brilliantly i really could not believe it!!

I have encountered a lot of problems so far and again after analysing them for hours/days i always pulled through and created a fix for them! Except for doors, doors can be opened and closed by players, but they weren’t synced for players… Apart from that everything was looking up, it really was going fantastic, i pressed on with the city egger to finish it, i made a new GUI and finally added a zombie for testing! Once i was happy with the progress i labelled this as the first official 0.1a which was no longer a test more of a pre-alpha, i got everything ready for my little group of testers and finally launched the game for testing myself, again as usual everything was working fantastic things just got better and better! Until i realised the biggest problem so far, the zombies weren’t synced! No!!

This basically really pissed me off, so again i analysed the problem trying to find the root and finally a fix, but i never could fix it, no matter what i did the zombies or any moving object for that matter would not sync… Then it dawned on me, the core of this problem is that the set-up is client based, and most of what you see is not online, the player skin and chat is the only true online parts, this pretty much meant that anything player based wouldn’t be synced (opening and closing doors, shooting etc.) and i’d need a solution. So now i have the root of the problem and looking still for a fix. I have this Update script which is basically attached to the players and this is what feeds the movement of a client to other clients. So, if i could somehow adapt this script to feed the movement of the zombies and doors to other clients, this would solve the sync issue… Right?

I really don’t want to stop work now, zombies really are the backbone of my game. I’ve came so far and still have further to go! I just need a little aid in the area that i lack badly in, that being Python. Now i ask of someone who is good with Python to please, please god dam it help me :p! I will send all the scripts (there’s only 12 scripts 3 server scripts and 9 client they are all fairly small as well!) to you, you can then look over them and get a feel for how the system works and hopefully, be able to help me with creating a script, based on the ObUpdate script that would enable moving objects data to be sent across all clients from the server. This would have something to do with get and set orientation maybe? Anyway, it’s not like i’m asking you to build the game for me!

Also please don’t post anything like “you really shouldn’t do this” etc. Syncing moving objects is the last thing that’s needed before the networking part is finished, i can then concentrate on the game.

For any of you non-believers, here’s a video (sorry for the bad quality). And before anyone asks, i have a team of testers who have successfully connected and were playing also. So far i’ve had 7 connections lag free!

Sorry for the huge story! Thanks for reading,
i hope someone can help… :frowning:

Brave.

A lot of text to read.

If I understood you right, you have a script for the players already. Can you post the script together with the properties of the doors and/or zombies?

If you watch the video, the ObUpdate script is what shows you the movement of the other players (i’m sure). The doors have a simple IPO animation and a near sensor for the player to open and close when close. The zombies simply track to the player and move on both the x & y axis.

ObUpdate.py:

 
cont=GameLogic.getCurrentController()
own=cont.getOwner()
# compute objId from objet name
objId = int(own.getName().split('.')[1])
if hasattr(GameLogic,'bzInitialised') == True:
    if (objId < GameLogic.bzClient.nb_client) and (objId != GameLogic.bzClient.id):
        ObjPseudo = GameLogic.getCurrentScene().getObjectList()["OBpseudo.%03d"%objId]
        ObjBasePseudo = GameLogic.getCurrentScene().getObjectList()["OBbpseudo.%03d"%objId]
        ObjPseudo.Text = GameLogic.bzClient.pseudos[int(objId)] #"coucou"
        ObjPseudoMountPoint = GameLogic.getCurrentScene().getObjectList()["OBmntpseudo.%03d"%objId] 
        pspos = ObjPseudoMountPoint.getPosition ()
        ObjBasePseudo.setPosition(pspos)
        own.setPosition(GameLogic.bzClient.positions[objId])
        own.setOrientation(GameLogic.bzClient.orientations[objId])
    else:
        own.setPosition([0.0, 0.0, -20.0])
        own.setOrientation([[0.0, 1.0, 0.0],[-1.0, 0.0, 0.0],[0.0, 0.0, 1.0]])
        ObjBPseudo = GameLogic.getCurrentScene().getObjectList()["OBbpseudo.%03d"%objId] 
        ObjBPseudo.setPosition ([0.0, 0.0, -20.0])
else:
    own.setPosition([0.0, 0.0, -20.0])
    own.setOrientation([[0.0, 1.0, 0.0],[-1.0, 0.0, 0.0],[0.0, 0.0, 1.0]])
    if "OBbpseudo.%03d"%objId in GameLogic.getCurrentScene().getObjectList().keys():
        ObjBPseudo = GameLogic.getCurrentScene().getObjectList()["OBbpseudo.%03d"%objId] 
        ObjBPseudo.setPosition ([0.0, 0.0, -20.0])

I was writing you a looong explanation what the script does. But the browser means I should not send it. So it is lost. Here is a short explanation:

The objects with the script are grouped by the extention: <own>.xxx where xxx is an object id (objId).

The script sets the location and orientation of the object (own) to the values read from GameLogic.bzClient.pseudos[xxx]. I assume that GameLogic.bzClient contains information received from network.

The script sets also the Text property of an object called pseudo.xxx. The string comes from GameLogic.bzClient.pseudos[xxx]. I think this is a text that is displayed somewhere in the game.

There is also an object named bpseudo.xxx (base) which gets the position from mntpseudo.xxx (mount). I have no clue what they are for.

All this objects (own.xxx, pseudo.xxx, bpseudo.xxx, mntpseudo.xxx) belong to eachother.

There must be another script that sends the information of the master object to the network.

To get your doors and zombies working they need to fit into the naming scheme. Just select an available xxx object ID. The location and orientation are set with the above script.
If you want to transfer properties you have to modify the script.

I hope this helps a bit.

Monster

Yes, from my understanding, this has to be “Base.xxx” this determines what connection spawns with what skin. For example a male skin has Base.000 and a female with Base.001, meaning that the first connection will spawn with the male skin, second with the female and anyone after that won’t spawn with a skin until more are added.

From the main connection script:

GameLogic.bzInitialised = True
GameLogic.bzClient = bzClient (GameLogic.host, GameLogic.port, GameLogic.pseudo)
GameLogic.bzClient.Connect()

This is the username text displayed above players’ head’s.

These two are actually the object names of two emptys that are working with the name above the players’ head’s, what they do i also have no idea.

What do you mean belong to each other? Well, there is a protocol script (it’s not connected to any players though), this has things such as:

  • class ServerMessage (Message) i.e when someone connects/disconnects it’s printed into the server console.
  • class ClientMessage (Message) not sure what these do exactly, maybe chat?
  • class PositionReport(ClientMessage) Now this is quite interesting, is this what sends the positions of the client to the server?
  • class SayMessage(ClientMessage) Again to do with the chat system, i presume?
  • class ClientsSituationReport(ServerMessage) This is similar to the Client report, but it’s much longer, i noticed in the client & server position reports they do not include any values… Because it reads where the player is?Monster, i really appreciate you taking the time to help me, i really hope you can help me further and finally into a fix for this problem! Do you think a fix is possible without it being a client…?

Also studying these scripts so closely has made me feel much more confident with Python which i love!

Brave.

You need to setup a simple dictionary of variables, for example:

{“door_001”:“open”, “door_002”:“closed”, “switch_001”:“on”}

And also, for your zombies:

{“zombie_001”:{“position”:[0.0, 0.0, 0.0], “health”:10}}

Then all you need is a setup where, when “VAR=door_001.closed” is received by client, it sets all the door_001 to “closed”.

You will also want to have the zombies running in a special AI client that is made only to run the zombies, as they will need to run in blender.

Hmm, i understand that, seems quite simple, but i ask if you or some else could write this piece of code for me (i know, asking doesn’t go down well… ) to where i could use it and easily expand it with adding for example another door (door.003, door.004 etc) or another zombie (zombie.004 etc). Also, what about guns? Would they work in the same way as the zombies?

lastly, that was what i thought! If i made a client take away the controls and implement AI then connect so the zombies were on the server, but if the client works the same way as the regular client, that would mean that anyone connecting wouldn’t see the zombies and/or be able to spawn as one… At the moment, all the player skins are in every single client, without them in the clients you can’t see each other but with the skins having base.000, base.001 etc that means you can spawn as one… If you understand.

I ssume with skin you mean an player object. Therefore you have two player (base.000 and base.001).

Can you have more than 2 player?

I suggest to setup zombies as base.100 with the same logic as the player (base.000). You can replace the controls the AI logic. For that you will not need a change in the ObUpdate.py.

For doors you could do the same thing maybe starting with base.200.

Yes, you can have as many players as you like, you just need to keep adding new player skins to the client with the base base.002, base.003 etc.

Well, i’ve tried to alter ObUpdate.py, but this script just seems like a bunch of numbers and letters and i’m finding it hard to understand, this is more like advanced python. Could you possibly help me out with the script?

That would also mean zombies would need to be separate clients? Which is going to mean i need to connect around 10-12 times to have a nice zombie count… Isn’t there any other way?

I do not think you need to change the script. For zombies you can use it as it is. Add they like players. But instead of giving them keyboard controls add the AI.

But then how do i get the zombies to spawn? Without needed to connect a client for each one?

This is a general problem. You need to define which client controlls the “master”.
For player it is easy. There is one player per client, represented with a camera as master and a full body mesh object as slaves on each client. The slaves get the position and orientation from the master (what the DoUpdate.py script does).

For zombies you need one master as well. In difference to the player the master should also be represented by a mesh object and is controlled by AI and not keybord/mouse. The zombie slaves on the other clients do the same as the player slaves: getting the position/orientation from the master (with DoUpdate.py)

It is up to you to decide which client has the master zombies.
You can have one client to have all master zombies. E.g. 3 clients and 5 zombies. Client A controlls 5 master zombies, client B and C have five slave zombies each.
Or you spread the master zombies over all clients. E.g. 3 clients and 5 zombies. Client A and B have 2 master and 3 slave zombies. Client C has 1 master and 4 slave zombies.

As I wrote you do not need to change DoUpdate for the slave zombies. You might need an update in the script for the master zombies. As I do not have the “master” script I can’t tell.

I don’t quite understand, but I think i’m going to keep it the same way as players, where all the skins or zombie slaves rather are on each client. But if they are connected to a base.xxx then a player with the corresponding connection (0 = .000, 1 = .001 etc.) will spawn as them. Another problem is each zombie would have to be it’s own player like set-up or all only one of the mesh’s will have collision and/or all the mesh’s will snap to the same position if they all follow the same empty.

What if i send you all the scripts via PM?

Hello braveheart,

I can imagine that this is not easy to understand. I think you used the Bzoo template as base for your game. I managed to get it running on my laptop. I’m not really happy with the logic that the template.

The network part is a fine thing, but the application side is a little bit to restrictive in my mind. With application part I mean how the messages are structured and the objects are assembled together.

Anyway this is a good base to work with. Unfortunatly you will need a fairly good understanding of the logic behind it and Python! There are a lot of tricks in the scripts already. I think you have to understand them.

I want to give you an overview of the logic. Please keep in mind that this is what I understood. I haven’t done any network games yet. So most of that is theory.

The Server:

The server receives connections from the clients. The clients send their name called pseudo to the server. The server stores the clients name in internal tables.

The server receives messages from all connected clients. The messages are parsed for position, orientation and actions and stored in internal tables according to the clients name. Therefore the server knows what data is from which client.
The server sends the content of this tables icluding the clients name (pseudo) to all clients. Therefore the clients receive the data from all other clients including their own data.

The Client:
The object “Client” executes the script “bzPulse.py”. The script looks for an object named “player” and stores the position and orientation of this object. It also calls bzclient.Pulse() to read and write new messages from and to the server.
I would move this logic directly to the object “player” (removing it from object Client, adding it to player). In this case the script can be changed to following:

 
import bzClient
from bzClient import bzClient
 
cont=GameLogic.getCurrentController()
own=cont.getOwner()
if hasattr(GameLogic,'bzInitialised') == True:
    scene = GameLogic.getCurrentScene()
    GameLogic.bzClient.StorePosition(own.getPosition())
    GameLogic.bzClient.StoreOrientation(own.getOrientation())
 
    GameLogic.bzClient.Pulse()

This allows to have any object as the player. Unfortunatly we can only store one set of positions and orientations.

The object Base.000 has script “ObUpdate.py” assigned to. This script parses through the tables containing data from the server message(see bzPulse.py). The script sets the position and orientation of the object Base.000 to the position and orientation read from the tables (originated from player). Also the text property of pseudo (which is diplayed on screen) is set to the senders name.

Base.000 will get the pos/ori/name of the first client that connects to the player regardless of the players name.
Base.001 will get the pos/ori/name of the second client,
Base.002 will get the pos/ori/name of the third client and so on.
The Base.xxx objects do not really spawn as they are always present, but hidden under the ground.

Adding zombies is not that easy because:

  • bzClient allows only one set of position/orientation to be stored. You need at least 2 (player + one zombie).
  • objUpdate assumes all Base.xxx objects look the same.
  • it is difficult to transfer additional information (status, health, events) with the current setup.

My conclusion is the scripts and the structure needs a lot of improvement. Unfrtunatly they need advanced Python knowledge.

Here are my ideas:

  • the server rejects all connections with non-unique names.
  • the server places all messages in a list (similar to the current processing) and sends it to all clients
  • the clients save all data to be transferred in a dictionary (that could include other dictionaries).
  • This can be data from multiple objects (player and zombies).
  • The data can contain property values together with the property name.
  • There should be at least one property that defines the kind of object (e.g."player,“npc”, “zombie”, “door”)
  • the clients send a message with the above dictionary together with the (unique) clients (senders) name.
  • the clients parse through the list of messages. The data should be processed depending on the senders name.
  • For new sender a new set of “Base” objects should be added (via add object actuator) depending on the kind of sender object. A property should store the name of the sender.
  • For existing senders the state of the “Base” object should be updated.
  • For “Base” objects with lost connection the objects will be removed.
    You should know that there are some additional problems to solve.
    A classical one is on client A player dies but on client B player one survived. Which client is right?

Conclusion
As you see this is much work. This is a project in itself.

Oh damn… I probably should have mentioned i was using the Bzoo set-up with a few modifications here and there. I don’t really have the Python knowledge to understand all of these scripts and how they work, which is a bad thing…

I was thinking, would it be easier if the zombies and doors and anything that needs to be moving for everyone were on the server? How would that be done? Would it be more work?

From what i’ve understood about the set-up is, that the ObUpdate.py script would need to be adjusted for anything that moves other than players. I thought it was that script that actually sent the data (position, orientation etc.) to the server then distributed to the other clients. Obviously i was wrong…

What about Chaser’s suggestion? I noticed you mentioned a dictionary but would it really take that much work to implement such a system? For the doors it seems simple (Door.000 = 1 clients see it open, Door.000 = 0 clients see it closed…) for zombies a little more complicated, but they could work like a player does currently? And yeah sending the properties such as health will be hard…

The script you posted, you say that allows any object to be the player? What exactly do you mean by that?

You say bzClient only allows for one set of positions to be stored, what if the zombies had their own bzClient?

Then meaning ObUpdate needs a little change? Taking out checking for a pseudo etc.

Here are my ideas:

  • the server rejects all connections with non-unique names.
  • the server places all messages in a list (similar to the current processing) and sends it to all clients
  • the clients save all data to be transferred in a dictionary (that could include other dictionaries).
  • This can be data from multiple objects (player and zombies).
  • The data can contain property values together with the property name.
  • There should be at least one property that defines the kind of object (e.g."player,“npc”, “zombie”, “door”)
  • the clients send a message with the above dictionary together with the (unique) clients (senders) name.
  • the clients parse through the list of messages. The data should be processed depending on the senders name.
  • For new sender a new set of “Base” objects should be added (via add object actuator) depending on the kind of sender object. A property should store the name of the sender.
  • For existing senders the state of the “Base” object should be updated.
  • For “Base” objects with lost connection the objects will be removed.
    You should know that there are some additional problems to solve.
    A classical one is on client A player dies but on client B player one survived. Which client is right?
  • What do you mean by “Non-unique names”?
  • Similar to the current set-up, but with what change?
  • Like {Door.000 = 1} {Zombie.000 = 00.00, 00.00, 00.00, Health = 2} ?
  • Right.
  • Okay.
  • Uhh okay… Don’t quite understand.
  • Same as above.
  • Same as above.
  • Right.
  • As they do already!
  • Lol, but if one sees him dieing, then he’s dead, right?

Again, i appreciate the time you’re taking to help me! Thanks!
:wink:

Yes I thought it would be easier too.

Remarks for the server:
The main problem is that the server does not forward the messages from client as they are. The server stores received messages in internal tables. From the tables the server builds new messages to be send to the clients.

As the received and send messages depend on the table you can’t send any message. They need the to have a specific structure.

Digging deeper into the server script the received messages should have following structure:

 
{"0": [position,orientation,action]} # id do not know what action is
{"2":[msg]} #I think a chat message

the server sends following messages:

 
{"1":[[pseudos],[positions], [orientations],[actions]]} # a table with tables; pseudos = clients name
{"3":[msg]} #I think the chat message from "2"

As you might see you have one position, oritentation and action for each client. We would need more than one and we need the possibility to send more and different data.

I think the server.py needs to enhanced to perfrom a more generalized message processing, without looking into the messages.

Here I can answer you the question what I mean with unique/non-uniwue names:
As the clients receive a table of names/pos/ori/act they do not care of the senders name. Therefore you can have two clients with the name “guest”. Unfortunatly you can’t distinguish between the messges from them except tfrom the position in the table.
e.g.: message from server:


{"1":["guest","guest","braveheard"],
     [[position from guest],[position from guest],[position from braveheard]],
     [[orientation from guest],[orientation from guest],[orientation from braveheard]],
     [action from guest,action from guest,action from braveheard]}

The server should not allow to have two “guest”. The second one should be rejected. If the server does that the client can use the names as key to identify the sender.

Chasers suggestion is what you need. But it will not be that easy to send this through the server, because of the current message structure.
I will explain this a later to you.

I have to go to work now.
Regards

Okay, i await your further explanation! I just need to say though, if you connect with the name “Guest” and there’s already a connected client with the name “Guest” you’re name will appear as “Guest.000”, another person connecting with “Guest” will get “Guest.001” etc… It’s already set up like that :yes:.

continue…

Your right the server already make the client names unique. So we can mark this as resolved.

Forget about the bzPulse. I think that must be changed anyway. Currently it takes the position and orientation from the object “player” but it is in the logic bricks of the object “client”. The idea was to remove the hardcoded “player” and to read the position and orientation of the owner instead. Currenty there is no real benefit in changing it, as you can store the data for one object only.

Another bzClient will be in conflict with the other one. So better improving the bzClient.py.

I’m working on a general concept that makes it a bit easier to to build an fps scenario. Basicly I want to make the server to act in a similar way as the message actuator does.
That means sending messages to all clients and sending messages to specific clients. The server will also not care bout the messages itself. It will be just a bunch of data to be transferred.

I would suggest messages in this form:

[ from, to, data ]
from is the name of the senders client, to is the name of the receivers client
while data contains all data to be transferred from the client.
If to is empty [] the message should be send to ll clients.

data could be:

 
{ objects name : dict }
dict = {  "position":[0.0,0.0,0.0],
          "orientation": orientation matrix,
          "property":value, etc.}

As I said I’m working on it

When the server is changed the clients must be changed as well to read the new message structure. The client need to parse through the data and take the appropriate action (e.g. setting the position and the orientation and properties of specified objects).

The client must be able to collect data, format a new message and send it through the server to one or all clients.

After the server this would be the next step.

…thats for now.

to be continued

Good news, the changes to the server have a lot of progress. It was more restrictive than I thought. Now you can send any kind of message through the server. There are still some issues before it can be used for gaming.

braveheart,

maybe it is easier to switch to this one:
http://blenderartists.org/forum/showthread.php?p=916431#post916431