Distributed Networking

I have been discussing the use of the distributed networking system with Drwr in private messages, and he suggested we move the conversation to the main forums so others may participate/benefit, so that’s what I’m doing here.

This is the conversation so far:

A DistributedNode is just a class that inherits from DistributedObject and PandaNode (actually, NodePath). So it is both a distributed object and it is a node. You should use DistributedNode whenever you have something that wants to be inserted into the graph, like a node–something that has position, rotation, maybe children, and so on, and will presumably contain renderable geometry–and use DistributedObject whenever you have something that does not have these properties.

This is just a syntactic convention used by the dc file. It doesn’t actually mean only division; it actually means that Panda will multiply your parameters by this factor when sending messages, and dividing by the same factor when receiving them. It’s just a trick to send a decimal number via an integer. For instance, DistributedNode.setX() sends its parameter as int16 / 10, which means the legal values for X are in the range -3276.8 to 3276.7. (That is, you can store -32768 to 32767 in an int16, and the Panda convention allows you to store up to one decimal point of precision.) An int16 is half the size of a float32, so it’s a win if every byte counts. Mostly, nowadays, every byte doesn’t count for much, so there’s not a whole lot of reason to use this convention often.

This is what the keywords following the message definitions are for. If the keyword “ram” is present, it means that the message is persistent, and the server will automatically retransmit it to any new clients who see this object. (It does this by calling the corresponding getFoo() method when needed.) If the keyword “ram” isn’t present, then the message is not persistent, and new clients who see the object won’t hear the message. The idea is that “ram” should be added to messages that are part of the object’s state, like its color, position, whatever, and it should not be added to messages that are transitory in nature (like a command to play a sound effect or something).

A manual update is required. Our convention is to write a trio of Python methods for each Distributed method: foo(), which does the operation locally, d_foo(), or “distributed foo”, which sends the message to the distributed system, and b_foo(), or “both foo”, which calls d_foo() and foo() together.

David

Is there a way to tell a clientRepository to generate DistributedObjects that exist in a zone other than having it create a DistributedObject in that zone?

I ask because I want clients that connect to a host to create only one distributed object, and that object is dependent on other distributed objects.

Yes; that’s the set of “interest zones”. The client maintains a set of zoneIds that it is interested in hearing about. By default, this set is the same set of zoneIds that it has created DistributedObjects in; but it can also include any additional zones you specify.

Use ClientRepository.setInterestZones() to add additional interest zones.

David

Excellent, thank you.

When a client connects to a server in this system, does the client get some kind of unique identifier to differentiate it from other clients? Or is there a way for a client to get a count of how many other clients are connected to the same server?

I’m looking for a way for clients to uniquely identify objects they create. For instance, the first client my preface object names or id numbers with a “A”, while the second client uses “B”, and so on.

My reason is that my clients are each creating DistributedNodes at regular intervals, and I need to be able to tell those DistributedNodes apart on all clients because other DistributedNodes need to be assigned to them. The plan is tell children “You’re a child of A1” or some such, so the child can find the correct parent.

Each client is assigned a range of legal doId’s, so you can tell which object belongs to which client by examining its doId.

In particular, you can call ClientRepository.isLocalId(doId), which returns True if the doId belongs to this client, or False if it belongs to some other client. There’s also ServerRepository.getDoIdBase(doId), which returns the client’s doIdBase given the object’s doId. This is only a server-level call, though.

There’s no easy way for a client to know how many other clients are connected to the server, but you could build this into your particular game by having all clients create an “I am here” object in a common zone, or something like this. (Of course, this requires that you trust all of your clients, which is generally a bad idea in a distributed environment. But much of the ClientRepository/ServerRepository infrastructure suffers from this same trust issue anyway; and that’s an issue for another discussion.)

David

So, what you’re telling me is that I need to learn C++ and write my own Distributed Networking system for scratch, eh?

Kidding, of course.

Thanks for the info. That “I am here” object sounds like a good idea. I can probably build that into the player object I’m using to store their information, like color and team.

Is there a way to kick a client from a server?

No, only the clients can decide when to connect or disconnect, but you can build a “please disconnect” message, which you can program your clients to respect.

If you don’t trust your clients, of course, we have bigger issues anyway, as I already mentioned.

David

It’s more for a host wanting to kick another player out of the waiting room prior to starting a match.

How would I build such a message?

Also, I did find a way to kick a client while you waiting for your last post. It was just… unpleasant.

I looked up all the clients with the clientsByDoIdBase, got a Client object from the list, got it’s connection, got the connection’s manager, and told the manager to close the connection.

Not a very happy way to go about it, certainly.

The same way you build any message–with a DistributedObject method that, when the server calls it, the client disconnects.

Ah, good point. That’s actually a fine way to do it, though it is messy for both the server and the client. For the purposes of making a good show for a well-behaved client, it makes more sense to use a message that says “leave the waiting room”.

David

Is there any way to make a particular client ignore a class of DistributedObjects, or DistributedObjects with a certain zoneId? I ask because I’m getting errors when my AIRepository can’t create an AI version of a DistributedObject, regardless of it’s zoneId, and I dislike having to create an AI version of an object the AI doesn’t need to touch, and never will.

I don’t think there’s any way built in to handle that currently. The assumption is that the AI will want to meddle with (or at least know about) all of the objects in the world.

David