DistributedNode

I am attempting to use the DistributedObject system to create a base for my game and am having a bit of a hard time figuring out how to work the DistributedNode so that I can render the node on all clients. Can anyone please shed some light on this for me please? A small example of how to get a client to render a distributed node would be great.

EDIT: look near the bottom for the most up to date code :smiley:

-=Z=-

1 Like

FWIW: I found 2 videos that I am currently trying to watch and understand. They are a bit over a year old, so maybe some of the more knowledgeable people here can tell us if the info still applies.

video.google.com/videoplay?docid ā€¦ 1394&hl=en

video.google.com/videoplay?docid ā€¦ 3409&hl=en

Enjoy!

1 Like

I have watched both of these and were very helpfull for concepts but they donā€™t go into implementation at all. Any help with implementation of this? I have managed to get the basics of the system working but canā€™t quite figure out how to work with the DistributedNode class. I have managed to get a working sample using a class that inherits DistributedObject but canā€™t quite figure out what to do with the DistributedNode and how to get it into the scene graph of the clients.

1 Like

Do you have anywhere you can post what yo have? I am going to try to figure this out as well, so maybe we can work on it together. I have noticed that networking questions on this forum seem to be a bit scarce for answers, so it may be up to us.

1 Like

Another problem I found. I was going to try to follow along with the client video, but a search for *.dc in my panda-1.5.4 directory does not reveal the existence of the samples.dc file they discuss. Are the videos still applicable to the current versions of Panda?

1 Like

They are in the source only, I think.
panda3d.cvs.sourceforge.net/view ā€¦ iew=markup
or a more elaborate one:
panda3d.cvs.sourceforge.net/view ā€¦ iew=markup

1 Like

Here is what I have so far. It is a work in progress and currently does work. Feel free to make suggestions. I have setVerbose so it will display lots of debugging info in the console.

import direct.directbase.DirectStart
from direct.showbase.ShowBaseGlobal import *
from direct.showbase.DirectObject import DirectObject
from direct.distributed.DistributedObject import *
from direct.distributed.DistributedSmoothNode import *
from direct.distributed.ClientRepository import *
from direct.distributed.ServerRepository import *
from direct.interval.IntervalGlobal import *

class Avatar(DistributedNode.DistributedNode):
	def __init__(self,cr):
		self.cr = cr
		self.name = "workstation"
		self.zone = 1
		self.myBody = 0
		self.x = 0
		self.y = 0
		self.z = 0
		self.h = 0
		self.p = 0
		self.r = 0

	def setName(self, name):
		self.name = name

	def getName(self):
		return self.name

	def tellPlayer(self, message):
		print "SELF:",self
		print "message from "+self.name+">"+message

class Server(DirectObject.DirectObject):

	def __init__(self):
		self.serverRepository = ServerRepository(tcpPort=4400,udpPort=4400,dcFileNames=["base2.dc"])
		print "Server Repository instantiated"

class Client(DirectObject.DirectObject):

	def __init__(self):
		self.clientRepository = ClientRepository(dcFileNames=["base2.dc"])
		self.clientRepository.setVerbose(1)
		self.host = "127.0.0.1"
		self.avatar = 0
		self.zone = 1

	def connectToServer(self, host, port):
		if(self.clientRepository.isConnected()): return
		self.clientRepository.connect([URLSpec("http://"+host+":"+port)],
			self.connectSuccess,[],self.connectFail,[])

	def connectSuccess(self):
		print "Connected to server"
		base.taskMgr.add(self.tskMonitorConnection,"tskMonitorConnection")

	def connectFail(self):
		print "Connection to host failed"

	def tskMonitorConnection(self, task):
		if(self.clientRepository.isConnected() == 0):
			print "Connection to server lost"
			return Task.done
		if(self.avatar == 0):
			if(self.clientRepository.haveCreateAuthority()):
				self.avatar = self.clientRepository.createWithRequired("Avatar",1)
				NodePath.__init__(self.avatar, 'avatar')
		return Task.cont

	def setZone(self, task):
		self.clientRepository.sendSetZoneMsg(self.zone)
		return Task.done


class World(DirectObject.DirectObject):

	def __init__(self):
		self.myClient = Client()
		self.acceptOnce("c",self.connectToServer)
		self.acceptOnce("s",self.startServer)
		self.accept("t",self.sendText)

	def connectToServer(self):
		self.myClient.connectToServer("localhost","4400")
		# can't send this too soon or it gets lost so send it in the next frame
		base.taskMgr.doMethodLater(0,self.myClient.setZone,"setzone")

	def startServer(self):
		myServer = Server()

	def sendText(self):
		print ":",self.myClient
		self.myClient.avatar.sendUpdate("tellPlayer",["hello"])

w = World()
run()
1 Like

I figured out the problem with inheritance. I had to modify the dc file because it didnā€™t like some of the stuff in there for some reason.
Here is an updated version where the ā€˜tā€™ key sends a set_xyz update out. Now the question is, how do I get some geometry associated with the thing so the clients on both ends can see each other?

Here is the updated code that I have so far:

base.dc

from direct.distributed import DistributedObject
from direct.distributed import DistributedNode
from newServer import Movable, Avatar

typedef uint32 DoId;

import types;

dclass Movable {
	
	set_xyz(int16 x, int16 y, int16 z) required broadcast ram;
	set_hpr(int16 h, int16 p, int16 r) required broadcast ram;

};

dclass Avatar : Movable {

	setName(string text) required broadcast;
	tellPlayer(string message) broadcast;
};

newServer.py (I know this isnā€™t a very good name for this)

import direct.directbase.DirectStart
from direct.showbase.ShowBaseGlobal import *
from direct.showbase.DirectObject import DirectObject
from direct.distributed.DistributedObject import *
from direct.distributed.DistributedSmoothNode import *
from direct.distributed.ClientRepository import *
from direct.distributed.ServerRepository import *
from direct.interval.IntervalGlobal import *

class Movable(DistributedNode.DistributedNode):
	def __init__(self,cr):
		DistributedNode.__init__(self,cr)
		self.x = 0
		self.y = 0
		self.z = 0
		
	def set_xyz(self,x,y,z):
		self.x = x
		self.y = y
		self.z = z
		
	def get_xyz(self):
		return tuple((self.x,self.y,self.z))
		
	def set_hpr(self,h,p,r):
		self.h = h
		self.p = p
		self.r = r
		
	def get_hpr(self):
		return tuple((self.h,self.p,self.h))

class Avatar(Movable):
	def __init__(self,cr):
		self.cr = cr
		self.name = "workstation"
		self.zone = 1
		self.myBody = 0
		self.x = 0
		self.y = 0
		self.z = 0
		self.h = 0
		self.p = 0
		self.r = 0

	def setName(self, name):
		self.name = name

	def getName(self):
		return self.name

	def tellPlayer(self, message):
		print "SELF:",self
		print "message from "+self.name+">"+message

class Server(DirectObject.DirectObject):

	def __init__(self):
		self.serverRepository = ServerRepository(tcpPort=4400,udpPort=4400,dcFileNames=["base.dc"])
		print "Server Repository instantiated"

class Client(DirectObject.DirectObject):

	def __init__(self):
		self.clientRepository = ClientRepository(dcFileNames=["base.dc"])
		self.clientRepository.setVerbose(1)
		self.host = "127.0.0.1"
		self.avatar = 0
		self.zone = 1

	def connectToServer(self, host, port):
		if(self.clientRepository.isConnected()): return
		self.clientRepository.connect([URLSpec("http://"+host+":"+port)],
			self.connectSuccess,[],self.connectFail,[])

	def connectSuccess(self):
		print "Connected to server"
		base.taskMgr.add(self.tskMonitorConnection,"tskMonitorConnection")

	def connectFail(self):
		print "Connection to host failed"

	def tskMonitorConnection(self, task):
		if(self.clientRepository.isConnected() == 0):
			print "Connection to server lost"
			return Task.done
		if(self.avatar == 0):
			if(self.clientRepository.haveCreateAuthority()):
				self.avatar = self.clientRepository.createWithRequired("Avatar",1)
				NodePath.__init__(self.avatar, 'avatar')
		return Task.cont

	def setZone(self, task):
		self.clientRepository.sendSetZoneMsg(self.zone)
		return Task.done


class World(DirectObject.DirectObject):

	def __init__(self):
		self.myClient = Client()
		self.acceptOnce("c",self.connectToServer)
		self.acceptOnce("s",self.startServer)
		self.accept("t",self.sendText)

	def connectToServer(self):
		self.myClient.connectToServer("localhost","4400")
		# can't send this too soon or it gets lost so send it in the next frame
		base.taskMgr.doMethodLater(0,self.myClient.setZone,"setzone")

	def startServer(self):
		myServer = Server()

	def sendText(self):
		print ":",self.myClient
		self.myClient.avatar.sendUpdate("set_xyz",[2,2,2])

w = World()
run()
1 Like

After fooling with this some more I am getting stumped. Here is what Iā€™m trying to do:

class Movable(DistributedNode.DistributedNode):
	def __init__(self,cr):
		DistributedNode.__init__(self,cr)
		self.x = 0
		self.y = 0
		self.z = 0
		
	def set_xyz(self,x,y,z):
		self.x = x
		self.y = y
		self.z = z
		self.setPos(x,y,z)     #trying to set the position here
		
	def get_xyz(self):
		return tuple((self.x,self.y,self.z))
		

Now when the second client is connecting and doing the generate this is the error I get:
Any help with this would be greatly appreciated. The first part of this is the debugging info that is produced in the console.

Connected to server
ConnectionRepository sending datagram:
0000  1d 00 01 00 00 00                                ......
CR::SEND:CLIENT_SET_ZONE_CMU
  0000  1d 00 01 00 00 00                                ......
CR::RECV:CLIENT_SET_DOID_RANGE
  0000  4a 00 81 84 1e 00 40 42 0f 00                    J.....@B..
CR::RECV:CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER
  0000  24 00 01 00 00 00                                $.....
ConnectionRepository sending datagram:
0000  22 00 01 00 00 00 01 00 81 84 1e 00 00 00 00 00  "...............
0010  00 00 00 00 00 00 00 00 0b 00 77 6f 72 6b 73 74  ..........workst
0020  61 74 69 6f 6e                                   ation
CR::SEND:CLIENT_CREATE_OBJECT_REQUIRED_RESP
  0000  22 00 01 00 00 00 01 00 81 84 1e 00 00 00 00 00  "...............
  0010  00 00 00 00 00 00 00 00 0b 00 77 6f 72 6b 73 74  ..........workst
  0020  61 74 69 6f 6e                                   ation
CR::RECV:CLIENT_CREATE_OBJECT_REQUIRED_RESP
  0000  22 00 01 00 41 42 0f 00 00 00 00 00 00 00 00 00  "...AB..........
  0010  00 00 00 00 0b 00 77 6f 72 6b 73 74 61 74 69 6f  ......workstatio
  0020  6e                                               n
Traceback (most recent call last):
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 579,
in readerPollUntilEmpty
    while self.readerPollOnce():
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 586,
in readerPollOnce
    self.handleDatagram(self.private__di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 140, in h
andleDatagram
    self.handleGenerateWithRequired(di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 170, in h
andleGenerateWithRequired
    distObj = self.generateWithRequiredFields(dclass, doId, di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 210, in g
enerateWithRequiredFields
    distObj.updateRequiredFields(dclass, di)
  File "C:\Panda3D-1.6.1\direct\distributed\DistributedObject.py", line 334, in
updateRequiredFields
    dclass.receiveUpdateBroadcastRequired(self, di)
  File "C:\Panda3D-1.6.1\serverClient\newServer.py", line 18, in set_xyz
    self.setPos(x,y,z)
AttributeError: C++ object is not yet constructed, or already destructed.
:task(error): Exception occurred in PythonTask readerPollTask
Traceback (most recent call last):
  File "newServer.py", line 120, in <module>
    w = World()
  File "newServer.py", line 102, in __init__
    self.myClient = Client()
  File "newServer.py", line 63, in __init__
    self.clientRepository = ClientRepository(dcFileNames=["base.dc"])
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 16, in __
init__
    ClientRepositoryBase.__init__(self, dcFileNames = dcFileNames)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepositoryBase.py", line 46, i
n __init__
    self.readDCFile(dcFileNames)
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 286,
in readDCFile
    self.importModule(dcImports, moduleName, importSymbols)
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 383,
in importModule
    module = __import__(moduleName, globals(), locals(), importSymbols)
  File "C:\Panda3D-1.6.1\serverClient\newServer.py", line 121, in <module>
    run()
  File "C:\Panda3D-1.6.1\direct\showbase\ShowBase.py", line 2423, in run
    self.taskMgr.run()
  File "C:\Panda3D-1.6.1\direct\task\TaskNew.py", line 471, in run
    self.step()
  File "C:\Panda3D-1.6.1\direct\task\TaskNew.py", line 429, in step
    self.mgr.poll()
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 579,
in readerPollUntilEmpty
    while self.readerPollOnce():
  File "C:\Panda3D-1.6.1\direct\distributed\ConnectionRepository.py", line 586,
in readerPollOnce
    self.handleDatagram(self.private__di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 140, in h
andleDatagram
    self.handleGenerateWithRequired(di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 170, in h
andleGenerateWithRequired
    distObj = self.generateWithRequiredFields(dclass, doId, di)
  File "C:\Panda3D-1.6.1\direct\distributed\ClientRepository.py", line 210, in g
enerateWithRequiredFields
    distObj.updateRequiredFields(dclass, di)
  File "C:\Panda3D-1.6.1\direct\distributed\DistributedObject.py", line 334, in
updateRequiredFields
    dclass.receiveUpdateBroadcastRequired(self, di)
  File "C:\Panda3D-1.6.1\serverClient\newServer.py", line 18, in set_xyz
    self.setPos(x,y,z)
AttributeError: C++ object is not yet constructed, or already destructed.

This error is getting thrown before any calls to this method, during the generate process.
Any thoughts?

1 Like

Hmm, Iā€™m not able to reproduce the problem. Can you post the exact code that generates this error?

David

1 Like

Actually I have gotten this working. Iā€™m in the process of adding some simple keyboard-based steering and polishing things up some. Once I get it finished Iā€™ll post it here to share.

1 Like

Here is a full example of using a DistributedNode across multiple clients. You need 2 files to make this work. First you need the base.dc file and distributedNodeDemo.py

Enjoy!

base.dc

from direct.distributed import DistributedObject
from direct.distributed import DistributedNode
from distributedNodeDemo import Movable, Avatar

typedef uint32 DoId;

import types;

dclass Movable {
	
	set_y(int16 y) required broadcast ram;
	set_h(int16 h) required broadcast ram;
	set_xyz(int16 x, int16 y, int16 z) required broadcast ram;
	set_hpr(int16 h, int16 p, int16 r) required broadcast ram;

};

dclass Avatar : Movable {

	setName(string text) required broadcast;
	tellPlayer(string message) broadcast;
};

distributedNodeDemo.py

#######################################################################
## This sample program shows how to use a DistributedNode that shows ##
## up on multiple clients and is updated across the network without  ##
## dealing with low level sockets and datagrams hardly at all        ##
##                                                                   ##
## Feel free to make any suggestion/modifications                    ##
## Enjoy!                                                            ##
##                                                                   ##
## -=Zanz=- May 2009                                                 ##
#######################################################################

import direct.directbase.DirectStart
from direct.showbase.ShowBaseGlobal import *
from direct.showbase.DirectObject import DirectObject
from direct.distributed.DistributedObject import *
from direct.distributed.DistributedSmoothNode import *
from direct.distributed.ClientRepository import *
from direct.distributed.ServerRepository import *
from direct.gui.OnscreenText import OnscreenText

font = loader.loadFont("cmss12")

def addTitle(text):
	return OnscreenText(text=text, style=1, fg=(1,1,1,1), font = font,
	pos=(1.3,-0.95), align=TextNode.ARight, scale = .07)

def addInstructions(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1), font = font,
                        pos=(-1.3, pos), align=TextNode.ALeft, scale = .05)

# Our Movable class that can be inherited into subclasses such as the Avatar class
# to allow the subclass to be moved around as a DistributedNode
class Movable(DistributedNode.DistributedNode):
	def __init__(self,cr):
		DistributedNode.DistributedNode.__init__(self,cr)
		# you have to initialize NodePath.__init__() here because it is not called
		# in DistributedNode.DistributedNode.__init__()
		NodePath.__init__(self, 'avatar')
	
	# all the required methods as defined in the base.dc file
	# note: for each method defined in the dc file that begins with
	# set needs a corresponding get method in the class definition
	def set_y(self,y):
		self.setY(self,y)
		
	def get_y(self):
		return self.getY()
		
	def set_xyz(self,x,y,z):
		self.setPos(x,y,z)
		
	def get_xyz(self):
		return tuple((self.getX(),self.getY(),self.getZ()))
		
	def set_h(self,h):
		self.setH(self,h)
		
	def get_h(self):
		return self.getH()
		
	def set_hpr(self,h,p,r):
		self.setHpr(h,p,r)
	
	def get_hpr(self):
		return tuple((self.getH(),self.getP(),self.getR()))

class Avatar(Movable):
	def __init__(self,cr):
		Movable.__init__(self,cr)
		self.cr = cr
		self.name = "unknown"
		self.zone = 1
		s = loader.loadModel('smiley')
		s.reparentTo(self)
		self.reparentTo(render)

	# required fields as defined in the base.dc file
	def setName(self, name):
		self.name = name

	def getName(self):
		return self.name

	def tellPlayer(self, message):
		print "message from "+self.name+">"+message

class MyClientRepository(ClientRepository):
	def __init__(self,dcFileNames):
		ClientRepository.__init__(self, dcFileNames)

	# we need to delete the local instance of the distributed Avatar when the server tells us to
	def handleDelete(self,di):
		doId = di.getUint32()
		do = self.doId2do.get(doId)
		do.delete()

class Server(DirectObject.DirectObject):

	def __init__(self):
		self.serverRepository = ServerRepository(tcpPort=4400,udpPort=4400,dcFileNames=["base.dc"])
		print "Server Repository instantiated"

class Client(DirectObject.DirectObject):

	def __init__(self):
		self.clientRepository = MyClientRepository(dcFileNames=["base.dc"])
		#self.clientRepository.setVerbose(1)
		self.clientRepository.notify.setDebug(0)
		self.host = "127.0.0.1"
		self.port = "4400"
		self.avatar = 0
		self.zone = 1

	def connectToServer(self, host, port):
		if(self.clientRepository.isConnected()): return
		self.clientRepository.connect([URLSpec("http://"+host+":"+port)],
			self.connectSuccess,[],self.connectFail,[])

	def connectSuccess(self):
		print "Connected to server"
		base.taskMgr.add(self.tskMonitorConnection,"tskMonitorConnection")

	def connectFail(self):
		print "Connection to host failed"

	def tskMonitorConnection(self, task):
		if(self.clientRepository.isConnected() == 0):
			print "Connection to server lost"
			return Task.done
		if(self.avatar == 0):
			if(self.clientRepository.haveCreateAuthority()):
				# this is telling the repository to create a distributed instance
				self.avatar = self.clientRepository.createWithRequired("Avatar",1)
				base.camera.setPos(0,-0.5,0.4)
				base.camera.reparentTo(self.avatar)
				base.camera.setH(180)
		return Task.cont

	def setZone(self, task):
		self.clientRepository.sendSetZoneMsg(self.zone)
		return Task.done


class World(DirectObject.DirectObject):

	def __init__(self):

		self.title = addTitle("DistributedNode Demo")
		self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Left")
		self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Right")
		self.inst4 = addInstructions(0.80, "[Up Arrow]: Move Forward")
		self.inst8 = addInstructions(0.75, "[Down Arrow]: Move Backwards")
		self.inst7 = addInstructions(0.70, "[o]: toggle oobe")
		self.inst9 = addInstructions(0.65, "[s]: Start server")
		self.inst10= addInstructions(0.60, "[c]: Connect to server")

		self.oobe = 0
		
		self.myClient = Client()

		self.acceptOnce("c", self.connectToServer)
		self.acceptOnce("s", self.startServer)
		self.accept("arrow_up", self.moveForward)
		self.accept("arrow_down", self.moveBackward)
		self.accept("arrow_left", self.turnLeft)
		self.accept("arrow_right", self.turnRight)
		self.accept("o",self.setOobe)	
		
		base.disableMouse()
		
		
		
	def connectToServer(self):
		self.myClient.connectToServer(self.myClient.host,self.myClient.port)
		# can't send this too soon or it gets lost so send it in the next frame
		base.taskMgr.doMethodLater(0,self.myClient.setZone,"setzone")

	def startServer(self):
		myServer = Server()

	# understand that this is not the best method for steering
	# but is for demonstration purposes and simplicity only
		
	def moveForward(self):
		#send the update to the other clients
		self.myClient.avatar.sendUpdate("set_y",[-1])
		#update our own instance
		self.myClient.avatar.setY(self.myClient.avatar, -1)
		
	def moveBackward(self):
		self.myClient.avatar.sendUpdate("set_y",[+1])
		self.myClient.avatar.setY(self.myClient.avatar, +1)
		
	def turnLeft(self):
		self.myClient.avatar.sendUpdate("set_h",[+12])
		self.myClient.avatar.setH(self.myClient.avatar, +12)
		
	def turnRight(self):
		self.myClient.avatar.sendUpdate("set_h",[-12])
		self.myClient.avatar.setH(self.myClient.avatar, -12)
	
		
	def setOobe(self):
		base.oobe()

w = World()
run()

-=Zanz=-

**EDIT: note that the clients all spawn in the same position so youā€™ll have to move one to see the other :wink: Iā€™ll try to remedy this eventually with an offset or something **

1 Like

Extremely awesome! I am starting to dig through the code and understand it now, but the examples work wonderfully, and appear very well documented.

For anyone else trying the samples out, a word of advice: Start 2 copies of the game. Press ā€˜sā€™ in the first one to make that a server, then ā€˜cā€™ to connect it to the server. Donā€™t try to make any movement on that one.

In the other, press ā€˜cā€™ to connect to the server ( the first one). At that point, in one of the clients, move forward a couple times. You will see the white ball in the other client moving away. Keep on the same client, and turn around, and you can look at each other.

Thanks again, Zanz!

1 Like

Itā€™s also fun to open about 3 or 4 of these windows at once, resize them so they all fit on the screen and move them around to see how the perspective of the different clients change as you move around.
Like this:

Also neat to toggle oobe and look at the scene from birdā€™s eye view. :wink:

1 Like

I have been playing with this, and it is pretty cool. Do you know what the SetZone method is for? Does it filter the passed info somehow?

Thanks!

1 Like

setZone is for network visibility. Think in terms of different rooms. Only clients that have the same zone number receive each otherā€™s updates.

David

1 Like

@Tutunkommon: I would suggest watching those video lectures again on the DistributedObject system to get a good understanding of what zones are and how interest and location work. They explain it pretty in-depth in there.

I am now thinking of the next step. I have been looking through the API at different classes that are distributed and wonder why these classes are left undocumented. Is there noone out there that understands this system enough to write some brief docs on it? At least a brief description of what each of the methods is supposed to do instead of just ā€˜Undocumented functionā€™. I want to combine smoothing with a good steering method so I will most likely be looking at the DistributedSmoothNode and the GravityWalker classes. Iā€™ll try to figure these out and pass along any discoveries here to the forum. Any suggestions by people who know this system or have used these classes would be greatly appreciated. Letā€™s remove the shroud of darkness that covers this system for everyoneā€™s benefit.

1 Like

Weā€™d love to have some good documentation there. Your work here is greatly appreciated.

If youā€™d like to add some pages to the manual about this, it would be awesome. The manual, of course, can be directly edited. Also, you can add text for the ā€œundocumented functionsā€ by putting a doc string in the Python code, or a leading comment in C++ code. Weā€™ll happily accept patches to the code as needed.

David

drwr: could you perhaps post a quick example of how to implement the DistributedSmoothNode? I am playing with it but havenā€™t gotten any smoothing going. Do I need to have a task running to get smooth positions? Iā€™m just not quite sure what to do with this thing yet. Iā€™ll keep experimenting also and try to look at the source for the class. I have changed the base class for my example to a DistributedSmoothNode and called avatar.activeSmoothing(1,0). It complained about not having an attribute named smoother so I assumed it wanted a SmoothMover class here so I added avatar.smoother = SmoothMover()
What other magical settings do i need to start seeing some results? Thanks for your help with this. Once I figure out how the system works Iā€™d be happy to try to document some of it for others and hope that this thread will also be of help to others like myself.

-=Z=-

self.smoother should have been created in generate(), which should have been called when the DistributedObject became visible. Perhaps you have to call generate() explicitly on your local object when you create it.

David