Panda3D Collision and Networking Example from PyOhio 2012

I learning by going through various tutorials, docs, and youtube, and I came across this presentation for learning how to use Panda3D with simple collision detection and multiplayer client/server networking:

This is referenced in the following post here:

The dropbox link to the source code is 404/not found, and I have done my best to resurrect the final server.py/client.py, but it is incomplete.

I would like to complete/clean it up and use it as a base for building/exploring various simple multiplayer game concepts - please let me know if you can help!

server.py

import direct.directbase.DirectStart
from direct.showbase import DirectObject
from direct.gui.OnscreenText import OnscreenText
# Warning: pandac.PandaModules is deprecated, import from panda3d.core instead
# from pandac.PandaModules import *
from panda3d.core import *
from direct.task import Task
import math, sys, random
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator

PI = 4.0*math.atan(1.0)
DEGREEStoRADIANS = PI / 180.0
RADIANStoDEGREES = 100.0 / PI

MSG_NONE = 0
MSG_AUTH = 1
MSG_POS  = 2
MSG_QUIT = 3
MSG_CQUIT = 4
MSG_CPOS  = 5

# timeout for connecting attempts
timeout = 1000

# props = WindowProperties()
# props.setCursorHidden(True)
# base.win.requestProperties(props)

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


def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1),
                        pos=(-0.0, -0.95), align=TextNode.ACenter, scale = 0.07)


class PyOhio(DirectObject.DirectObject):
    def __init__(self):
        self.Clients = {}
        self.PlayerList = {}
        self.Models = {}
        self.CollSpheres = {}
        self.CollNodes = {}

        self.speed = 0.25

        self.myID = "server"
        self.serverAddress = "192.168.0.7"
        self.port = 10101

        self.title = addTitle("SIMPLE GAME TESTING NETWORKS")
        self.inst1 = addInstructions(0.95, "[ESC]: Quit")
        self.inst2 = addInstructions(0.90, "[arrow_up]: Move Positive Y")
        self.inst3 = addInstructions(0.85, "[arrow_down]: Move Negative Y")
        self.inst4 = addInstructions(0.80, "[arrow_right]: Move Positive X")
        self.inst5 = addInstructions(0.75, "[arrow_left]: Move Negative X")

        base.setBackgroundColor(0, 0, 0)

        self.parent = loader.loadModel("models/misc/sphere")
        self.parent.setPos(0, 0, 0)

        x = 0
        for i in range(100):
            theta = x;
            self.placeholder2 = render.attachNewNode("Placeholder2")
            self.placeholder2.setPos(50.0*math.cos(theta), 50.0*math.sin(theta), 0.0)
            red = 0.6 + (random.random() * 0.4)
            green = 0.6 + (random.random() * 0.4)
            blue = 0.6 + (random.random() * 0.4)
            self.placeholder2.setColorScale(red, green, blue, 1.0)
            self.parent.instanceTo(self.placeholder2)
            x = x + 0.06

        self.chaser = loader.loadModel("models/misc/sphere")
        self.chaser.reparentTo(render)
        self.chaser.setPos(0, 0, 0)
        self.chaser.setHpr(0, 0, 0)
        self.chaser.setScale(1.0)
        self.chaser.setColorScale(1.0, 0.0, 0.0, 1.0)

        base.disableMouse()
        base.camera.setPos(0.0, 0.0, 250.0)
        base.camera.setHpr(0.0, -90.0, 0.0)

        # create collision nodes for fighter and parent
        self.chasercnode = self.chaser.attachNewNode(CollisionNode('cnode2'))
        self.parentcnode = self.parent.attachNewNode(CollisionNode('cnode1'))
        self.chasercnode.node().addSolid(CollisionSphere(0, 0, 0, 1.0))
        self.parentcnode.node().addSolid(CollisionSphere(0, 0, 0, 2.0))

        # Set a CollisionHandlerPusher()
        self.pusher = CollisionHandlerPusher()
        # Pass collisionNodePath and modelNodePath
        self.pusher.addCollider(self.chasercnode, self.chaser)

        # setup traverser for fromObjects
        self.traverser = CollisionTraverser()
        base.cTrav = self.traverser
        # Any collision geometry under render that's not defined as 'fromObject' will be a 'toObject'
        self.traverser.traverse(render)
        # make collisionNode a 'fromObject' with the Handler
        self.traverser.addCollider(self.chasercnode, self.pusher)

        # self.parentcnode.show()
        # self.fightercnode.show()
        base.cTrav.showCollisions(render)

        # set key bindings

        # use the following to see all events:
        # base.messenger.toggleVerbose()
        self.accept('arrow_left', self.negX, [1])
        self.accept('arrow_left-up', self.negX, [0])
        self.accept('arrow_right', self.posX, [1])
        self.accept('arrow_right-up', self.posX, [0])
        self.accept('arrow_down', self.negY, [1])
        self.accept('arrow_down-up', self.negY, [0])
        self.accept('arrow_up', self.posY, [1])
        self.accept('arrow_up-up', self.posY, [0])

        self.accept('escape', self.quit)

        self.initNetwork()

    def negX(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvNegX, 'moveNegX')
        else:
            taskMgr.remove('moveNegX')
            self.acceptOnce('arrow_left', self.negX, [1])
            self.acceptOnce('arrow_left-up', self.negX, [0])

    def posX(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvPosX, 'movePosX')
        else:
            taskMgr.remove('movePosX')
            self.acceptOnce('arrow_right', self.posX, [1])
            self.acceptOnce('arrow_right-up', self.posX, [0])

    def negY(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvNegY, 'moveNegY')
        else:
            taskMgr.remove('moveNegY')
            self.acceptOnce('arrow_down', self.negY, [1])
            self.acceptOnce('arrow_down-up', self.negY, [0])

    def posY(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvPosY, 'movePosY')
        else:
            taskMgr.remove('movePosY')
            self.acceptOnce('arrow_up', self.posY, [1])
            self.acceptOnce('arrow_up-up', self.posY, [0])

    def mvNegX(self, task):
        self.chaser.setX(self.chaser, -1 * self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvPosX(self, task):
        self.chaser.setX(self.chaser, self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvNegY(self, task):
        self.chaser.setY(self.chaser, -1 * self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvPosY(self, task):
        self.chaser.setY(self.chaser, self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def quit(self):
        sys.exit()

    def initNetwork(self):
        print(self.myID + " : Network INIT")
        self.cManager = QueuedConnectionManager()
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.cReader = QueuedConnectionReader(self.cManager, 0)
        self.cWriter = ConnectionWriter(self.cManager, 0)
        self.startServer()

    def startServer(self):
        self.tcpSocket = self.cManager.openTCPServerRendezvous(self.port, timeout)
        self.cListener.addConnection(self.tcpSocket)

        taskMgr.add(self.listenTask, 'serverListenTask', -40)
        taskMgr.add(self.readTask, 'serverReadTask', -39)

        # add yourself to the Player list
        self.PlayerList[self.myID] = [0, 0, 0, 0, 0, 0]

        taskMgr.add(self.updateWorld, 'updateWorldTask')

        print(self.myID + " : Server Started")

    def listenTask(self, task):
        if self.cListener.newConnectionAvailable():
            rendervous = PointerToConnection()
            netAddress = NetAddress()
            newConnection = PointerToConnection()
            if self.cListener.getNewConnection(rendervous, netAddress, newConnection):
                newConnection = newConnection.p()
                # tell the reader that there's a new connection
                self.cReader.addConnection(newConnection)
                self.lastConnection = newConnection
                print(self.myID + " : " + str(netAddress) + " : got a connection")
            else:
                print(self.myID + " : getNewConnection returned false")
        return Task.cont

    def readTask(self, task):
        while 1:
            (datagram, pkg, msgID) = self.nonBlockingRead(self.cReader)
            if msgID == MSG_NONE:
                break
            else:
                self.datagramhandler(pkg, msgID)
        return task.cont

    def nonBlockingRead(self, gcr):
        if self.cReader.dataAvailable():
            datagram = NetDatagram()
            if self.cReader.getData(datagram):
                pkg = PyDatagramIterator(datagram)
                msgID = pkg.getUint16()
            else:
                pkg = None
                msgID = MSG_NONE
        else:
            datagram = None
            pkg = None
            msgID = MSG_NONE
        return (datagram, pkg, msgID)

    def datagramhandler(self, pkg, msgID):
        if (msgID == MSG_AUTH):
            PlayerID = pkg.getString()
            self.Clients[self.lastConnection] = PlayerID
            X = pkg.getFloat64()
            Y = pkg.getFloat64()
            Z = pkg.getFloat64()
            H = pkg.getFloat64()
            P = pkg.getFloat64()
            R = pkg.getFloat64()
            StatList = [X, Y, Z, H, P, R]
            self.PlayerList[PlayerID] = StatList

            print(str(self.PlayerList))
            print(str(self.Clients))

            self.updateClients()

        elif msgID == MSG_CPOS:
            PlayerID = pkg.getString()
            if PlayerID != self.myID:
                X = pkg.getFloat64()
                Y = pkg.getFloat64()
                Z = pkg.getFloat64()
                H = pkg.getFloat64()
                P = pkg.getFloat64()
                R = pkg.getFloat64()
                StatList = [X, Y, Z, H, P, R]
                self.PlayerList[PlayerID] = StatList

                dta = PyDatagram()
                dta.addUint16(MSG_CPOS)
                dta.addString(PlayerID)

                num = len(StatList)
                for i in range(num):
                    dta.addFloat64(StatList[i])

                for Client in self.Clients:
                    if (Client != PlayerID):
                        self.cWriter.send(dta, Client)
        elif (msgID == MSG_QUIT):
            PlayerID = pkg.getString()
            self.Models[PlayerID].detachNode()
            del self.Models[PlayerID]
            del self.PlayerList[PlayerID]
            dta = PyDatagram()
            dta.addUint16(MSG_CQUIT)
            dta.addString(PlayerID)
            for Client in self.Clients:
                self.cWriter.send(dta, Client)
        else:
            print("Don't understand the message id : " + str(msgID))

    def updateServer(self):
        # print(self.myID + " : updateServer()")
        dta = PyDatagram()
        dta.addUint16(MSG_CPOS)
        dta.addString(self.myID)
        stats = self.PlayerList[self.myID]
        num = len(stats)
        for i in range(num):
            dta.addFloat64(stats[i])
        for Client in self.Clients:
            self.cWriter.send(dta, Client)
        # print(self.myID + " : updateServer() Clients: " + str(self.Clients), "PList: " + str(self.PlayerList))
        print(self.myID + " : updateServer() Clients: " + str(self.Clients), "PList: " + str(self.PlayerList))

    def updateClients(self):
        # print(self.myID + " : updateClients()")
        dta = PyDatagram()
        NUM = len(self.PlayerList)
        dta.addUint16(MSG_POS)
        dta.addUint16(NUM)

        for s in self.PlayerList:
            dta.addString(s)
            stats = self.PlayerList[s]
            num = len(stats)
            for i in range(num):
                dta.addFloat64(stats[i])
        for Client in self.Clients:
            self.cWriter.send(dta, Client)
        print(self.myID + " : updateClients() Clients: " + str(self.Clients), "PList: " + str(self.PlayerList))

    def updateWorld(self, task):
        # print(self.myID + " : updateWorld()")
        for i in self.PlayerList:
            if i in self.Models:
                if i != self.myID:
                    data = self.PlayerList[i]
                    self.Models[i].setX(data[0])
                    self.Models[i].setY(data[1])
                    self.Models[i].setZ(data[2])
                    self.Models[i].setH(data[3])
                    self.Models[i].setP(data[4])
                    self.Models[i].setR(data[5])
            elif i.strip() != '':
                if i != self.myID:
                    self.Models[i] = loader.loadModel("models/misc/sphere")
                    self.Models[i].setColorScale(0.0, 0.0, 1.0, 1.0)
                    self.CollSpheres[i] = CollisionSphere(0, 0, 0, 3.5)
                    self.CollNodes[i] = self.Models[i].attachNewNode(CollisionNode(i))
                    self.CollNodes[i].node().addSolid(self.CollSpheres[i])
                    self.Models[i].reparentTo(render)
                    data = self.PlayerList[i]
                    self.Models[i].setX(data[0])
                    self.Models[i].setY(data[1])
                    self.Models[i].setZ(data[2])
                    self.Models[i].setH(data[3])
                    self.Models[i].setP(data[4])
                    self.Models[i].setR(data[5])
        self.updateServer()
        return Task.cont


s = PyOhio()
base.run()

client.py

import direct.directbase.DirectStart
from direct.showbase import DirectObject
from direct.gui.OnscreenText import OnscreenText
# Warning: pandac.PandaModules is deprecated, import from panda3d.core instead
# from pandac.PandaModules import *
from panda3d.core import *
from direct.task import Task
import math, sys, random
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator

PI = 4.0*math.atan(1.0)
DEGREEStoRADIANS = PI / 180.0
RADIANStoDEGREES = 100.0 / PI

MSG_NONE = 0
MSG_AUTH = 1
MSG_POS  = 2
MSG_QUIT = 3
MSG_CQUIT = 4
MSG_CPOS  = 5

# timeout for connecting attempts
timeout = 1000

# props = WindowProperties()
# props.setCursorHidden(True)
# base.win.requestProperties(props)

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


def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1),
                        pos=(-0.0, -0.95), align=TextNode.ACenter, scale = 0.07)


class PyOhio(DirectObject.DirectObject):
    def __init__(self):
        self.PlayerList = {}
        self.Models = {}
        self.CollSpheres = {}
        self.CollNodes = {}

        self.speed = 0.25
        self.myID = "client"
        self.serverAddress = "192.168.0.7"
        self.port = 10101

        self.title = addTitle("SIMPLE GAME TESTING NETWORKS")
        self.inst1 = addInstructions(0.95, "[ESC]: Quit")
        self.inst2 = addInstructions(0.90, "[arrow_up]: Move Positive Y")
        self.inst3 = addInstructions(0.85, "[arrow_down]: Move Negative Y")
        self.inst4 = addInstructions(0.80, "[arrow_right]: Move Positive X")
        self.inst5 = addInstructions(0.75, "[arrow_left]: Move Negative X")

        base.setBackgroundColor(0, 0, 0)

        self.parent = loader.loadModel("models/misc/sphere")
        self.parent.setPos(0, 0, 0)

        x = 0
        for i in range(100):
            theta = x;
            self.placeholder2 = render.attachNewNode("Placeholder2")
            self.placeholder2.setPos(50.0*math.cos(theta), 50.0*math.sin(theta), 0.0)
            red = 0.6 + (random.random() * 0.4)
            green = 0.6 + (random.random() * 0.4)
            blue = 0.6 + (random.random() * 0.4)
            self.placeholder2.setColorScale(red, green, blue, 1.0)
            self.parent.instanceTo(self.placeholder2)
            x = x + 0.06

        self.chaser = loader.loadModel("models/misc/sphere")
        self.chaser.reparentTo(render)
        # TODO add random starting position
        self.chaser.setPos(5.0, 5.0, 0)
        self.chaser.setHpr(0, 0, 0)
        self.chaser.setScale(1.0)
        self.chaser.setColorScale(1.0, 0.0, 0.0, 1.0)

        base.disableMouse()
        base.camera.setPos(0.0, 0.0, 250.0)
        base.camera.setHpr(0.0, -90.0, 0.0)

        # create collision nodes for fighter and parent
        self.chasercnode = self.chaser.attachNewNode(CollisionNode('cnode2'))
        self.parentcnode = self.parent.attachNewNode(CollisionNode('cnode1'))
        self.chasercnode.node().addSolid(CollisionSphere(0, 0, 0, 1.0))
        self.parentcnode.node().addSolid(CollisionSphere(0, 0, 0, 2.0))

        # Set a CollisionHandlerPusher()
        self.pusher = CollisionHandlerPusher()
        # Pass collisionNodePath and modelNodePath
        self.pusher.addCollider(self.chasercnode, self.chaser)

        # setup traverser for fromObjects
        self.traverser = CollisionTraverser()
        base.cTrav = self.traverser
        # Any collision geometry under render that's not defined as 'fromObject' will be a 'toObject'
        self.traverser.traverse(render)
        # make collisionNode a 'fromObject' with the Handler
        self.traverser.addCollider(self.chasercnode, self.pusher)

        # self.parentcnode.show()
        # self.fightercnode.show()
        base.cTrav.showCollisions(render)

        # set key bindings

        # use the following to see all events:
        # base.messenger.toggleVerbose()
        self.accept('arrow_left', self.negX, [1])
        self.accept('arrow_left-up', self.negX, [0])
        self.accept('arrow_right', self.posX, [1])
        self.accept('arrow_right-up', self.posX, [0])
        self.accept('arrow_down', self.negY, [1])
        self.accept('arrow_down-up', self.negY, [0])
        self.accept('arrow_up', self.posY, [1])
        self.accept('arrow_up-up', self.posY, [0])

        self.accept('escape', self.quit)

        self.initNetwork()

        taskMgr.add(self.updateWorld, 'updateWorldTask')

    def negX(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvNegX, 'moveNegX')
        else:
            taskMgr.remove('moveNegX')
            self.acceptOnce('arrow_left', self.negX, [1])
            self.acceptOnce('arrow_left-up', self.negX, [0])

    def posX(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvPosX, 'movePosX')
        else:
            taskMgr.remove('movePosX')
            self.acceptOnce('arrow_right', self.posX, [1])
            self.acceptOnce('arrow_right-up', self.posX, [0])

    def negY(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvNegY, 'moveNegY')
        else:
            taskMgr.remove('moveNegY')
            self.acceptOnce('arrow_down', self.negY, [1])
            self.acceptOnce('arrow_down-up', self.negY, [0])

    def posY(self, keyDown):
        if keyDown:
            taskMgr.add(self.mvPosY, 'movePosY')
        else:
            taskMgr.remove('movePosY')
            self.acceptOnce('arrow_up', self.posY, [1])
            self.acceptOnce('arrow_up-up', self.posY, [0])

    def mvNegX(self, task):
        self.chaser.setX(self.chaser, -1 * self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvPosX(self, task):
        self.chaser.setX(self.chaser, self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvNegY(self, task):
        self.chaser.setY(self.chaser, -1 * self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def mvPosY(self, task):
        self.chaser.setY(self.chaser, self.speed)

        X = self.chaser.getX()
        Y = self.chaser.getY()
        Z = self.chaser.getZ()
        H = self.chaser.getH()
        P = self.chaser.getP()
        R = self.chaser.getR()
        self.PlayerList[self.myID] = [X, Y, Z, H, P, R]
        self.updateServer()
        return Task.cont

    def quit(self):
        # tell the server that we are disconnecting
        pkg = PyDatagram()
        pkg.addUint16(MSG_QUIT)
        pkg.addString(self.myID)
        self.cWriter.send(pkg, self.connection)
        sys.exit()

    def initNetwork(self):
        print(self.myID + " : Network INIT")
        self.cManager = QueuedConnectionManager()
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.cReader = QueuedConnectionReader(self.cManager, 0)
        self.cWriter = ConnectionWriter(self.cManager, 0)
        self.connection = self.cManager.openTCPClientConnection(self.serverAddress, self.port, 1000)

        self.cReader.addConnection(self.connection)

        taskMgr.add(self.readTask, "serverReaderPollTask", -39)

        print(self.myID + " : sending auth")
        self.sendMsgAuth()

    def readTask(self, task):
        while 1:
            (datagram, pkg, msgID) = self.nonBlockingRead(self.cReader)
            if msgID == MSG_NONE:
                break
            else:
                self.datagramhandler(pkg, msgID)
        return task.cont

    def nonBlockingRead(self, qcr):
        if self.cReader.dataAvailable():
            datagram = NetDatagram()
            if self.cReader.getData(datagram):
                pkg = PyDatagramIterator(datagram)
                msgID = pkg.getUint16()
            else:
                pkg = None
                msgID = MSG_NONE
        else:
            datagram = None
            pkg = None
            msgID = MSG_NONE
        return (datagram, pkg, msgID)

    def datagramhandler(self, pkg, msgID):
        if msgID == MSG_POS:
            num = pkg.getUint16()
            for i in range(num):
                PlayerID = pkg.getString()
                X = pkg.getFloat64()
                Y = pkg.getFloat64()
                Z = pkg.getFloat64()
                H = pkg.getFloat64()
                P = pkg.getFloat64()
                R = pkg.getFloat64()
                StatList = [X, Y, Z, H, P, R]
                self.PlayerList[PlayerID] = StatList
            print(self.myID + " : datagramhandler(MSG_POS)" + str(self.PlayerList))

        elif msgID == MSG_CPOS:
            PlayerID = pkg.getString()
            X = pkg.getFloat64()
            Y = pkg.getFloat64()
            Z = pkg.getFloat64()
            H = pkg.getFloat64()
            P = pkg.getFloat64()
            R = pkg.getFloat64()
            StatList = [X, Y, Z, H, P, R]
            self.PlayerList[PlayerID] = StatList
            print(self.myID + " : datagramhandler(MSG_CPOS)" + str(self.PlayerList))

        elif msgID == MSG_CQUIT:
            PlayerID = pkg.getString()
            self.Models[PlayerID].detachNode()
            del self.Models[PlayerID]
            del self.PlayerList[PlayerID]
            print(self.myID + " : datagramhandler(MSG_CQUIT)" + PlayerID)

        else:
            print("Don't understand the message id : " + str(msgID))

    def sendMsgAuth(self):
        pkg = PyDatagram()
        pkg.addUint16(MSG_AUTH)
        pkg.addString(self.myID)
        pkg.addFloat64(self.chaser.getX())
        pkg.addFloat64(self.chaser.getY())
        pkg.addFloat64(self.chaser.getZ())
        pkg.addFloat64(self.chaser.getH())
        pkg.addFloat64(self.chaser.getP())
        pkg.addFloat64(self.chaser.getR())
        self.cWriter.send(pkg, self.connection)

    def updateServer(self):
        dta = PyDatagram()
        dta.addUint16(MSG_CPOS)
        dta.addString(self.myID)
        stats = self.PlayerList[self.myID]
        num = len(stats)
        for i in range(num):
            dta.addFloat64(stats[i])
        self.cWriter.send(dta, self.connection)

    # def updateClients(self):
    #     dta = PyDatagram()
    #     NUM = len(self.PlayerList)
    #     dta.addUint16(MSG_POS)
    #     dta.addUint16(NUM)
    #
    #     for s in self.PlayerList:
    #         dta.addString(s)
    #         stats = self.PlayerList[s]
    #         num = len(stats)
    #         for i in range(num):
    #             dta.addFloat64(state[y])
    #     for Client in self.Clients:
    #         self.cWriter.send(dta, Client)

    def updateWorld(self, task):
        for i in self.PlayerList:
            if i in self.Models:
                if i != self.myID:
                    data = self.PlayerList[i]
                    self.Models[i].setX(data[0])
                    self.Models[i].setY(data[1])
                    self.Models[i].setZ(data[2])
                    self.Models[i].setH(data[3])
                    self.Models[i].setP(data[4])
                    self.Models[i].setR(data[5])
                else:
                    data = self.PlayerList[i]
                    self.chaser.setX(data[0])
                    self.chaser.setY(data[1])
                    self.chaser.setZ(data[2])
                    self.chaser.setH(data[3])
                    self.chaser.setP(data[4])
                    self.chaser.setR(data[5])
            elif i.strip() != '':
                if i != self.myID:
                    self.Models[i] = loader.loadModel("models/misc/sphere")
                    self.Models[i].setColorScale(0.0, 0.0, 1.0, 1.0)
                    self.CollSpheres[i] = CollisionSphere(0, 0, 0, 3.5)
                    self.CollNodes[i] = self.Models[i].attachNewNode(CollisionNode(i))
                    self.CollNodes[i].node().addSolid(self.CollSpheres[i])
                    self.Models[i].reparentTo(render)
                    data = self.PlayerList[i]
                    self.Models[i].setX(data[0])
                    self.Models[i].setY(data[1])
                    self.Models[i].setZ(data[2])
                    self.Models[i].setH(data[3])
                    self.Models[i].setP(data[4])
                    self.Models[i].setR(data[5])
        return Task.cont


s = PyOhio()
base.run()