Networked GravityWalkers

I have a GravityWalker that controls a NodePath(my char) is it possible to feed it a class that inherits NodePath(and initializes NodePath) and is Distributed so it will exist across a network and have the avatar’s pos updated or should I use some other method so everything move smoothly and doesn’t flood the network connection?

It’s certainly possible, but you’ll have to write the code to do this.

But you might take a look at DistributedNode.py and DistributedSmoothNode.py, or even DistributedActor.py, which are set up to handle this sort of thing. Note that if you use any one of these (or inherit from them), you must call NodePath.init() explicitly, since these classes don’t initialize their NodePath base class.

DistributedSmoothNode is the most complex of the three; it implements smooth interpolations of position on the receiving end to compensate for the choppiness that you would get from receiving occasional network updates. It requires running a task on the sender (to send the current position periodically) as well as on the receiver (to smoothly lerp the position each frame).

I’d recommend getting DistributedNode to work first, to get a better handle on using distributed classes; or writing your own class to do this.

David

Well, here’s the problem: setPos, setHpr, etc. refer to the NodePath on the DistributedNode and DistributedSmoothNode, so I cannot give a DistributedSmoothNode as the parameter within a GravityWalker. There is another problem which is sendUpdate does not call the function locally at the same time it sends to the server, it sends to the server and waits for the signal to get back to update locally, not very graceful for dealing with latency. Below is my code(assuming you fill out the DC file accordingly):

import direct.directbase.DirectStart
from direct.showbase.ShowBaseGlobal import *
from direct.distributed.ClientRepository import *
from direct.distributed.ServerRepository import *
from pandac.PandaModules import *
from direct.distributed.DistributedSmoothNode import *
from direct.controls.GravityWalker import *

base.setSleep(0.05)

loader.loadModel(‘phase_4/models/minigames/tag_arena.bam’).reparentTo(render)

myserv = ServerRepository(tcpPort=977, udpPort=977)

exprep = ClientRepository()
exprep.connect([URLSpec(“http://localhost:977”)])
while (exprep.haveCreateAuthority() == 0): base.taskMgr.step()
exprep.sendSetZoneMsg(1)
locwalk = exprep.createWithRequired(“DistributedSmoothNode”, 1)
locwalk.reparentTo(render)
base.cTrav = CollisionTraverser()
myGrav = GravityWalker()
myGrav.initializeCollisions(base.cTrav, locwalk, 1.4, 0)
myGrav.enableAvatarControls()

messenger.accept(‘arrow_up’, None, inputState.set, [‘forward’, 1])
messenger.accept(‘arrow_up-up’, None, inputState.set, [‘forward’, 0])

run()

Ok, so first, DistributedNode and DistributedSmoothNode both inherit from NodePath. That means you can use them anywhere you can use a NodePath, including as the parameter to a GravityWalker. However, you do have to init the NodePath first.

You see, there is no call to NodePath.init(self) within the DistributedNode.__init(self) method, where normally there should be, in order to correctly initialize the base class. The reason for this is a sad and ugly story, but the upshot is that you have to call it yourself, e.g.:

locwalk = exprep.createWithRequired("DistributedNode", 1)
NodePath.__init__(locwalk, 'locwalk')
s = loader.loadModel('smiley.egg')
s.reparentTo(locwalk)
locwalk.reparentTo(render)

Also, let me reiterate an important point: I think you should use DistributedNode, not DistributedSmoothNode. Writing distributed applications is hard, and there are lots of places things can go wrong. The DistributedNode class is much simpler than DistributedSmoothNode, and it works almost as well. When you have a DistributedNode walking around on multiple windows, then you can worry about how to make DistributedSmoothNode work.

Now, sendUpdate() is not responsible for updating your local position; the GravityWalker does that. That’s why it’s ok that sendUpdate doesn’t do anything to your local position; all it does is tell all of the other clients what your position is. The idea is to let the GravityWalker move your node around locally, and every once in a while (say, a few times a second) you call sendUpdate() to broadcast your current position to anyone who’s listening. (In fact, normally there’s not a round-trip to the server–when you call sendUpdate(), the message goes out, but the server doesn’t normally echo it back to you, it only goes to everyone else.)

David

Hmm… Maybe I should write my own DistribuedNode of some sort. When you call setPos or setHpr or the others, it will first update it locally then send out. Is there a way to not make sendUpdate echo back from the server? I don’t want to update my position twice and create the world’s most jumpy avatar.

No, I think you misunderstand.

When you call DistributedNode.setPos(), it only sets the local position. It doesn’t send anything to anyone. This method is, in fact, inherited directly from NodePath.

When you call DistributedNode.d_setPos(), it only broadcasts the position to all the other clients. It doesn’t set anything locally, and the message never bounces back from the server. The d_ prefix stands for “distributed”, and is our universal convention for sending distributed updates.

So in order to update your position both locally and on all the other clients, you have to (a) call setPos(), and (b) call d_setPos(). In the case of the DistributedNode, you probably want to call setPos() every frame, but you only want to call d_setPos() a few times a second, to avoid flooding the network.

David

So, what you mean is something like this?

def updateTask(self, task):
if task.time - self.lastCall > .1:
self.lastCall = task.time
self.nodeToUpdate.d_setPos(self.nodeToUpdate.getX(), self.nodeToUpdate.getY(), self.nodeToUpdate.getZ())
return Task.cont

This is in a class, but not a DistributedNode. It would be in a class I might call “UpdaterDistributedNode” or something.

Would this be correct?

Yes, that’s the right idea.

David