Networking Connection Unclear in Documentation

My goal was to create a simple multi-player connection.

I read the documentation but I’m struggling to make sense of it.
I have ‘cube.gltf’ and server.py / client.py and a file called direct.dc all in the same folder

I’ve been using chatGPT and ran what felt like thousands of iterations.
the following is the furthest I got.

here is the Server.py file:

#!/usr/bin/env python3

from direct.showbase.ShowBase import ShowBase
from direct.distributed.DistributedObject import DistributedObject
from direct.distributed.DistributedNodeAI import DistributedNodeAI
from direct.distributed.DistributedObjectAI import DistributedObjectAI
from direct.distributed.ClientRepository import ClientRepository
from panda3d.core import load_prc_file_data, NetDatagram, Datagram, ConnectionWriter, ConnectionReader, PointerToConnection, QueuedConnectionManager, QueuedConnectionListener, QueuedConnectionReader, QueuedConnectionListener
from panda3d.core import QueuedConnectionManager, QueuedConnectionListener, ConnectionWriter, QueuedConnectionReader, PointerToConnection, NetAddress
from panda3d.core import Vec3

# Disable audio
load_prc_file_data("", "audio-library-name null")

class MyAIRepository(ClientRepository):
    def __init__(self):
        ClientRepository.__init__(self, dcFileNames=['direct.dc'])

class MyDistributedObject(DistributedNodeAI):
    def __init__(self, air):
        DistributedNodeAI.__init__(self, air)

        self.accept('arrow_left', self.move, [-1, 0, 0])
        self.accept('arrow_right', self.move, [1, 0, 0])
        self.accept('arrow_up', self.move, [0, 1, 0])
        self.accept('arrow_down', self.move, [0, -1, 0])

    def move(self, x, y, z):
        self.setX(self.getX() + x)
        self.setY(self.getY() + y)
        self.setZ(self.getZ() + z)
        self.sendUpdate('setPos', [self.getX(), self.getY(), self.getZ()])

class MyDistributedObjectAI(DistributedObjectAI):
    def __init__(self, air):
        DistributedObjectAI.__init__(self, air)

    def sendGameData(self, data):
        print(data)

    def b_setLocation(self, x, y, z):
        # Add your implementation here
        pass

# Initialize the Panda3D server
worldServer = ShowBase(windowType='none')

# Set up the server repository
worldServer.cr = MyAIRepository()
worldServer.cr.isClient = 0

# Create the DistributedObject on the server
worldServer.distributedObject = MyDistributedObject(worldServer.cr)

# Initialize the AI (AIRepository)
air = MyAIRepository()

# Create the DistributedObjectAI on the server
myDistributedObjectAI = MyDistributedObjectAI(air)
myDistributedObjectAI.generate()
myDistributedObjectAI.b_setLocation(0, 0, 0)
myDistributedObjectAI.announceGenerate()

# Run the Panda3D server
worldServer.run()

Here is the client.py file:

#!/usr/bin/env python3

from panda3d.core import load_prc_file_data, Vec3, URLSpec
from direct.showbase.ShowBase import ShowBase
from direct.distributed.ClientRepository import ClientRepository
from direct.distributed.DistributedSmoothNode import DistributedSmoothNode

# Configure Panda3D to use the null audio library
load_prc_file_data("", "audio-library-name null")

class MyMainClientRepository(ClientRepository):
    def __init__(self):
        dcFileNames = ['direct.dc']

        # Set up the main client repository
        ClientRepository.__init__(
            self,
            dcFileNames=dcFileNames,
            threadedNet=True)

        # Set the IP or hostname of the server we want to connect to
        hostname = '192.168.1.227'
        tcpPort = 9099

        # Build the URL from the server hostname and port.
        self.url = URLSpec('http://{}:{}'.format(hostname, tcpPort))

        # Attempt a connection to the server
        self.connect([self.url],
                     successCallback=self.connectSuccess,
                     failureCallback=self.connectFailure)

    def connectSuccess(self):
        # Successfully connected.
        base.cr = MyGameClientRepository()
        base.cr.isClient = 1

    def connectFailure(self, statusCode, statusString):
        # Connection failed.
        print("Failed to connect to the server. Status code: {}, Status string: {}".format(statusCode, statusString))

class MyGameClientRepository(ClientRepository):
    def __init__(self):
        dcFileNames = ['direct.dc']

        # Set up the game client repository
        ClientRepository.__init__(
            self,
            dcFileNames=dcFileNames,
            threadedNet=True)

        # Set the IP or hostname of the server we want to connect to
        hostname = '192.168.1.227'
        tcpPort = 9099

        # Build the URL from the server hostname and port.
        self.url = URLSpec('http://{}:{}'.format(hostname, tcpPort))

        # Attempt a connection to the server
        self.connect([self.url],
                     successCallback=self.connectSuccess,
                     failureCallback=self.connectFailure)

    def connectSuccess(self):
        # Successfully connected.
        self.gotCreateReady()

    def gotCreateReady(self):
        # Ready to enter the world.
        # Expand your interest to include any other zones.
        base.localAvatar = MyDistributedSmoothNode()
        base.localAvatar.reparentTo(render)

class MyDistributedSmoothNode(DistributedSmoothNode):
    def __init__(self):
        DistributedSmoothNode.__init__(self)
        self.setName('localAvatar')
        self.startSmooth()

# Initialize Panda3D
base = ShowBase()

# Set up the main client repository
base.cr = MyMainClientRepository()

# Run the Panda3D application
base.run()

Besides a file called cube.gltf (my model) I have this file called direct.dc

from panda3d.direct import DCFile

typedef struct {
    string value_a;
    uint8 value_b;
    int8 value_c/100;
} gameDataModel;

dclass DGameObject : DistributedNode {
    sendUpdateToAiGame(gameDataModel data);
};

dclass DGameObjectAI : DistributedObject {
    setAnounceGenerate(string helloMsg) broadcast ram;
    messageRoundtripToClient(gameDataModel data) clsend;
    sendGameData(gameDataModel data) clsend;
};

dclass MyDistributedObject : DistributedNode {
    // Add methods and fields as needed
};

dclass MyDistributedObjectAI : DistributedObject {
    // Add methods and fields as needed
};

Does anybody out there have a working script or any way I can get this puppy to purrr.
Thank you in advance.

I think you should complain to the creators of the ChatGPT. It is not clear from your example what you want to achieve.

1 Like

Oh no I know how to code, and I’ve read the documentation thoroughly, Im just struggling to get it working.

I had it originally as a connection using the asynchronous twisted server. but couldn’t get a cube to move on the server. so I abandoned the twisted connection and right now I just want to get cubes moving from two different clients, in the simplest way possible so I can move forward from there.

do you know how to do that? I mean the script I have showing use what it says in the doc.

use ‘airbase’ / distributed object etc… the documentation just feels a bit vague, like what am I missing here?

I’m not sure I can understand the “thought” of chatGPT. The documentation suggests such a solution, if you again mean creating a server-client.

server.py

from panda3d.core import QueuedConnectionManager
from panda3d.core import QueuedConnectionListener
from panda3d.core import QueuedConnectionReader
from panda3d.core import ConnectionWriter
from panda3d.core import PointerToConnection
from panda3d.core import NetAddress
from panda3d.core import NetDatagram
from panda3d.core import Datagram

from direct.task import Task

cManager = QueuedConnectionManager()
cListener = QueuedConnectionListener(cManager, 0)
cReader = QueuedConnectionReader(cManager, 0)
cWriter = ConnectionWriter(cManager, 0)
cWriter.setRawMode(True)

activeConnections = []

tcpSocket = cManager.openTCPServerRendezvous(9099, 1000)
cListener.addConnection(tcpSocket)

server_message = Datagram(b'Hello, welcome to the server.')

def tskListenerPolling(taskdata):
    if cListener.newConnectionAvailable():
        rendezvous = PointerToConnection()
        netAddress = NetAddress()
        newConnection = PointerToConnection()
        if cListener.getNewConnection(rendezvous,netAddress,newConnection):
            newConnection = newConnection.p()
            activeConnections.append(newConnection)
            cReader.addConnection(newConnection)
    return taskdata.cont

def tskReaderPolling(taskdata):
    if cReader.dataAvailable():
        datagram = NetDatagram()
        if cReader.getData(datagram):
            msg = datagram.getMessage()
            print(msg)
    return taskdata.cont

def message(taskdata):
    for aClient in activeConnections:
        cWriter.send(server_message, aClient)
    return taskdata.cont

taskMgr = Task.TaskManager()
taskMgr.add(message, "Broadcast a message to all clients", -38)
taskMgr.add(tskListenerPolling, "Poll the connection listener", -39)
taskMgr.add(tskReaderPolling, "Poll the connection reader", -40)
taskMgr.run()

client.py

from panda3d.core import QueuedConnectionManager
from panda3d.core import QueuedConnectionReader

from panda3d.core import ConnectionWriter
from panda3d.core import Datagram
from panda3d.core import NetDatagram

from direct.task import Task

cManager = QueuedConnectionManager()
cReader = QueuedConnectionReader(cManager, 0)

cWriter = ConnectionWriter(cManager,0)
cWriter.setRawMode(True)

client_message = Datagram(b"Hi I'm shenko")

myConnection = cManager.openTCPClientConnection("127.0.0.1", 9099, 3000)

if myConnection:
    cReader.addConnection(myConnection)
    if cReader.dataAvailable():
        datagram = NetDatagram()
        if cReader.getData(datagram):
            msg = datagram.getMessage()
            print(msg)

def message(taskdata):
    if myConnection:
        cWriter.send(client_message, myConnection)
    return taskdata.again

taskMgr = Task.TaskManager()
taskMgr.add(message, "Sending a message to the server", 40)
taskMgr.run()

This example does not use datagrams, but uses raw mode, but you can easily modify it according to this page. Transmitting Data — Panda3D Manual

The above example receives a message from the server, and starts a DDoS attack on it :slight_smile:

1 Like

I’m more of a white hat kind of guy not really trying to DDOS my servers lol.

I managed to send datagram but I’m more interested in how do I move a cube if the cube represents my player?

do I use ‘accept’ to grab keyboard input on the client.py side and then send that to
the server.py via datagram?

or like mentioned here in;
client-repositories & by utilizing a ‘direct.dc’ file

do I use the direct.dc file with ‘broadcast’ option for the cubes position?

broadcast:

copy pasted from panda doc page:
(By default only the owner of the object will receive update messages. With the broadcast tag all copies of the object will receive the message. This will be the most frequently used tag in most cases, leave it off when you only want the owner of the object to receive a message such as a private chat, receiving gold, etc.)

the more I read the documention the deaper I seem to sink into a rabbit hole.

I seem to understand that you have Distributed Networking However, these high-level methods have been added recently, if I’m not mistaken by the user: @wolf I think he will be able to answer your questions…

As for moving the cube, you can simply transfer a string with coordinates, on the client or server side you can easily convert this string by the split() method to a number.

1 Like

I try it, thanxs for the help

1 Like

You can also use this approach, I think this way you can reduce traffic.

1 Like

I just checked your code and while it’s partially fine, its structure and where things are done is a bit odd.

Like, you’re accepting the movement keys (which you’ve done correct with the accept method) on the server but that’s usually done on the client side.

The DC file seem to be fine from what I see.

What you do miss is a Server repository which you need to set up the server to handle incomming connections on that port you specified in the client.

The best probably would be if you check out the samples that are in the git repository of Panda3D and either use them as a starting point or learn from them how things are set up and write your own code accordingly.

I’d also love to hear what specifically is unclear in the manual so that I can improve it.

2 Likes

The most interesting thing is that I remembered that there are examples of the network, so I searched for them in the SDK, but they are not there. Why not add them?, if the documentation is available, then these examples should also be available in the usual place.

https://docs.panda3d.org/1.10/python/programming/networking/distributed/dc-definition-files#distributed-class-definition

At the moment, the information does not correspond to reality. For example, it claims that direct.dc is located here direct/distributed/, but in fact it is missing, which is misleading.

@shenko I created an example with datagrams.

1 Like

I’m not quite sure what you mean by examples in the SDK. Do you mean the SDK documentation should contain examples or do you mean examples that are in the SDK documentation should move to the manual? If it’s the later, could you link me those examples please?

As for the direct.dc, it should be there and if not, it may be a shipping bug since, looking at the repository it actually is there:

I mean the SDK distribution. Specifically, I am Windows, in my examples folder, there is no network example, accordingly there is no direct.dc file. I emphasize once again that I mean the release version 1.10.13 and not the master branch.

To clarify what I mean by the SDK example, this is the sample folder that is available after installation Panda3D.

Ah, yes, the networking samples are missing there, that’s right. Will clarify why it’s not packed in the release. As for the direct.dc, that is there in the release tag, at least it’s in the same folder as in the master branch. I’m not sure though if it’s shipped with the windows installation, that’s something I currently can’t check. But it definitely would be better to have this file in a more visible place than deeply burrowed in the source tree. Maybe it can be attached/linked to in the manual.

sorry the ‘accept’ I had it on client side before I was just banging on chatGPT over and over hoping the stupid machine would spit out at least one working version just out of frustration. I just posted the last garbage output from chatGPT. By copy pasting large parts of the manual into the prompt it sorta started to put out something close to what I wanted but never worked. It did help a little in helping me understand tho so im not mad at it.

really all I wanted was working code to start from your link to the github is actually exactly what I wanted.

Link to working player moving on server

Anyways. with working examples I should be ok from here.

thanx @wolf (and @serega-kkz )

1 Like