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 Iāll try to remedy this eventually with an offset or something **