Hello I am having an issue with the server when i connect more than 2 clients…
#SERVER.PY
from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectGui import *
from direct.task import Task
import sys
import player
import random
######################################3
##
## Config
##
PORT = 7198
######################################3
##
## Defines
##
## Quote Yellow: This are server opcodes. It tells the server
## or client what pkt it is receiving. Ie if the pkt starts
## with 3, the server knows he has to deal with a chat msg
MSG_NONE = 0
C_COORDINATE_SEND = 1
S_COORDINATE_SEND = 2
C_DISCONNECT_REQ = 3
S_NEWPLAYER_SEND = 4
###################################################################
##
## Creating a dictionary for the clients. Thats how we can adress
## them later on. For now they are adressed by their IP, but that
## isn't the best solution. What if 2 clients have the same IP?
## You can find of course any other working solution that will avoid
## this. Feel free to distribute it! As for the Demo its OK as is.
##
PLAYERS = {}
class Server(DirectObject):
def __init__(self):
## If you press Escape @ the server window, the server will quit.
self.accept("escape", self.quit)
self.lastConnection = None
# Create network layer objects
# Deals with the basic network stuff
self.cManager = QueuedConnectionManager()
# Listens for new connections and queue's them
self.cListener = QueuedConnectionListener(self.cManager, 0)
# Reads data send to the server
self.cReader = QueuedConnectionReader(self.cManager, 0)
# Writes / sends data to the client
self.cWriter = ConnectionWriter(self.cManager,0)
# open a server socket on the given port. Args: (port,timeout)
self.tcpSocket = self.cManager.openTCPServerRendezvous(PORT, 1)
# Tell the listener to listen for new connections on this socket
self.cListener.addConnection(self.tcpSocket)
# Start Listener task
taskMgr.add(self.listenTask, "serverListenTask",-40)
# Start Read task
taskMgr.add(self.readTask, "serverReadTask", -39)
taskMgr.add(self.repeatTask, 'repeatTask',-38)
def repeatTask(self,task):
for key,p in PLAYERS.items():
self.ProcessPlayer(p)
return Task.cont
def listenTask(self, task):
"""
Accept new incoming connections from the client
"""
# Run this task after the dataLoop
# If there's a new connection Handle it
if self.cListener.newConnectionAvailable():
rendezvous = PointerToConnection()
netAddress = NetAddress()
newConnection = PointerToConnection()
if self.cListener.getNewConnection(rendezvous,netAddress,newConnection):
newConnection = newConnection.p()
# tell the Reader that there's a new connection to read from
self.cReader.addConnection(newConnection)
p = player.Player()
p.ip = netAddress.getIpString()
p.client = newConnection
p.name = str(random.randint(0,5000))
p.playerId = random.randint(0,50000)
for key,p2 in PLAYERS.items():
while p2.playerId == p.playerId:
p.playerId = random.randint(0,50000)
print p.playerId
PLAYERS[p.client] = p
self.lastConnection = newConnection
print "Got a connection!"
else:
print "getNewConnection returned false"
return Task.cont
def readTask(self, task):
"""
If there's any data received from the clients,
get it and send it to the handlers.
"""
while 1:
(datagram, data, msgID) = self.nonBlockingRead(self.cReader)
if msgID is MSG_NONE:
# got nothing todo
break
else:
# Got a datagram, handle it
self.handleDatagram(data, msgID,datagram.getConnection())
return Task.cont
def nonBlockingRead(self,qcr):
"""
Return a datagram iterator and type if data is available on the
queued connection reader
"""
if self.cReader.dataAvailable():
datagram = NetDatagram()
if self.cReader.getData(datagram):
data = PyDatagramIterator(datagram)
msgID = data.getUint16()
else:
data = None
msgID = MSG_NONE
else:
datagram = None
data = None
msgID = MSG_NONE
# Note, return datagram to keep a handle on the data
return (datagram, data, msgID)
def handleDatagram(self, data, msgID, client):
"""
Check if there's a handler assigned for this msgID.
Since we dont have case statements in python,
I'm using a dictionary to avoid endless elif statements.
"""
########################################################
##
## Of course you can use as an alternative smth like this:
## if msgID == CMSG_AUTH: self.msgAuth(msgID, data, client)
## elif...
if msgID in Handlers.keys():
Handlers[msgID](msgID,data,client)
else:
print "Unknown msgID: %d" % msgID
print data
return
def receiveCoordinates(self, msgID, data, client):
#########################################################
##
## Okay... The client sent us some Data. We need to extract
## the data the same way it was placed into the buffer.
## Its like "first in, first out"
##
p = PLAYERS[client]
p.x = data.getFloat64()
p.y = data.getFloat64()
p.z = data.getFloat64()
print "ip: %s" %p.ip
print "X: %s" %p.x
print "Y: %s" %p.y
print "Z: %s" %p.z
## Now that we have the username and pwd, we need to
## determine if the client has the right user/pwd-combination.
## this variable will be sent later to the client, so lets
## create/define it here.
'''
## Creating a buffer to hold the data that is going to be sent.
pkg = PyDatagram()
## The first Bytes we send to the client in that paket will be
## the ones that classify them as what they are. Here they mean
## "Hi Client! I am an Auth Response from the server."
## How does the client know that? Well, because both have a
## definition saying "SMSG_AUTH_RESPONSE = 2"
## Due to shorter Network Pakets Yellow used Numbers instead
## of the whole Name. So you will see a 2 in the paket if you
## catch it somewhere...
pkg.addUint16(SMSG_AUTH_RESPONSE)
## Now we are sending, if the auth was
## successfull ("1") or not ("0" or "2")
pkg.addUint32(flag)
## Now lets send the whole story...
self.cWriter.send(pkg,client)
'''
def msgDisconnectReq(self, msgID, data, client):
pkg = PyDatagram()
pkg.addUint16(SMSG_DISCONNECT_ACK)
self.cWriter.send(pkg,client)
for key, p in PLAYERS.items():
if(PLAYERS[client].playerId in p.playersNearby):
p.playersNearby.remove(PLAYERS[client].playerId)
del PLAYERS[client]
self.cReader.removeConnection(client)
def quit(self):
self.cManager.closeConnection(self.tcpSocket)
sys.exit()
def sendNewPlayer(self, p, otherPlayer):
pkg = PyDatagram()
pkg.addUint16(S_NEWPLAYER_SEND)
pkg.addUint16(otherPlayer.playerId)
pkg.addString(otherPlayer.name)
self.cWriter.send(pkg,p.client)
def sendCoordinates(self, p, otherPlayer):
pkg = PyDatagram()
pkg.addUint16(S_COORDINATE_SEND)
pkg.addUint16(otherPlayer.playerId)
pkg.addFloat64(otherPlayer.x)
pkg.addFloat64(otherPlayer.y)
pkg.addFloat64(otherPlayer.z)
self.cWriter.send(pkg,p.client)
def ProcessPlayer(self,p):
print p.name
for key, otherPlayer in PLAYERS.items():
if p.name != otherPlayer.name:
if otherPlayer.playerId not in p.playersNearby:
p.playersNearby.append(otherPlayer.playerId)
self.sendNewPlayer(p, otherPlayer)
for otherPlayerId in p.playersNearby:
self.sendCoordinates(p, otherPlayer)
# create a server object
serverHandler = Server()
#install msg handlers
## For detailed information see def handleDatagram(self, data, msgID, client):
Handlers = {
C_COORDINATE_SEND : serverHandler.receiveCoordinates,
C_DISCONNECT_REQ : serverHandler.msgDisconnectReq,
}
## The loop again... otherwise the program would run once and thats it ;)
run()
#CLIENT.PY
from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectGui import *
from direct.task import Task
import sys
import cplayer
######################################3
##
## Config
##
IP = '127.0.0.1'
PORT = 7198
######################################3
##
## Defines
##
## Quote Yellow: This are server opcodes. It tells the server
## or client what pkt it is receiving. Ie if the pkt starts
## with 3, the server knows he has to deal with a chat msg
MSG_NONE = 0
C_COORDINATE_SEND = 1
S_COORDINATE_SEND = 2
C_DISCONNECT_REQ = 3
S_NEWPLAYER_SEND = 4
OTHER_PLAYERS = []
class Client(DirectObject):
def __init__(self):
#self.accept("escape", self.sendMsgDisconnectReq)
# Create network layer objects
## This is madatory code. Don't ask for now, just use it ;)
## If something is unclear, just ask.
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(IP, PORT,1)
self.cReader.addConnection(self.Connection)
# Start tasks
taskMgr.add(self.readTask, "serverReaderPollTask", -39)
self.sendMsgAuth()
########################################
##
## Addition:
## If in doubt, don't change the following. Its working.
## Here are the basic networking code pieces.
## If you have questions, ask...
##
def readTask(self, task):
while 1:
(datagram, data, msgID) = self.nonBlockingRead(self.cReader)
if msgID is MSG_NONE:
break
else:
self.handleDatagram(data, msgID)
return Task.cont
def nonBlockingRead(self,qcr):
"""
Return a datagram iterator and type if data is available on the
queued connection reader
"""
if self.cReader.dataAvailable():
datagram = NetDatagram()
if self.cReader.getData(datagram):
data = PyDatagramIterator(datagram)
msgID = data.getUint16()
else:
data = None
msgID = MSG_NONE
else:
datagram = None
data = None
msgID = MSG_NONE
# Note, return datagram to keep a handle on the data
return (datagram, data, msgID)
def sendMsgDisconnectReq(self):
#####################################################
##
## This is not used right now, but can be used to tell the
## server that the client is disconnecting cleanly.
pkg = PyDatagram()
## Will be a short paket... we are just sending
## the Code for disconnecting. The server doesn't
## need more information anyways...
pkg.addUint16(C_DISCONNECT_REQ)
self.send(pkg)
def handleDatagram(self, data, msgID):
if msgID in Handlers.keys():
Handlers[msgID](msgID,data)
else:
print "Unknown msgID: %d" % msgID
print data
return
def sendMsgAuth(self):
#########################################################
##
## This handles the sending of the auth request.
##
## 1st. We need to create a buffer
pkg = PyDatagram()
## 2nd. We put a UInt16 type Number in it. Here its CMSG_AUTH
## what means that the corresponding Value is "1"
pkg.addUint16(C_COORDINATE_SEND)
## 3rd. We add the coordinates to the buffer after the UInt.
x=123
y=456
z=78910
pkg.addFloat64(x)
pkg.addFloat64(y)
pkg.addFloat64(z)
## Now that we have a Buffer consisting of a Number and 2 Strings
## we can send it.
self.send(pkg)
def send(self, pkg):
self.cWriter.send(pkg, self.Connection)
def quit(self):
self.cManager.closeConnection(self.Connection)
sys.exit()
def receiveCoordinates(self, msgID, data):
#########################################################
##
## Okay... The client sent us some Data. We need to extract
## the data the same way it was placed into the buffer.
## Its like "first in, first out"
##
id = data.getUint16()
p = None
for p2 in OTHER_PLAYERS:
if p2.playerId == id:
p = p2
if p != None:
p.x = data.getFloat64()
p.y = data.getFloat64()
p.z = data.getFloat64()
#print "X: %s" %p.x
#print "Y: %s" %p.y
#print "Z: %s" %p.z
def receiveNewPlayer(self, msgID, data):
#########################################################
##
## Okay... The client sent us some Data. We need to extract
## the data the same way it was placed into the buffer.
## Its like "first in, first out"
##
id = data.getUint16()
print id
name = data.getString()
print "New Player Connected! ID: %s" %id
print "New Player Name: %s" %name
p = cplayer.CPlayer()
p.name = name
p.playerId = id
OTHER_PLAYERS.append(p)
######################################################################
##
## OK! After all of this preparation lets create a Instance of the
## Client Class created above. Call it as you wish. Make sure that you
## use the right Instance name in the dictionary "Handlers" as well...
##
aClient = Client()
######################################################################
##
## That is the second piece of code from the
## def handleDatagram(self, data, msgID): - Method. If you have
## trouble understanding this, please ask.
##
Handlers = {
S_COORDINATE_SEND : aClient.receiveCoordinates,
S_NEWPLAYER_SEND : aClient.receiveNewPlayer,
}
#######################################################################
##
## As Examples for other Instance names:
##
## justAExample = Client()
##
## Handlers = {
## SMSG_AUTH_RESPONSE : justAExample.msgAuthResponse,
## SMSG_CHAT : justAExample.msgChat,
## SMSG_DISCONNECT_ACK : justAExample.msgDisconnectAck,
## }
########################################################################
##
## We need that loop. Otherwise it would run once and then quit.
##
run()
#PLAYER.py
class Player():
x = None
y = None
z = None
name = None
client = None
ip = None
playerId = None
playersNearby = []
#CPLAYER.PY
class CPlayer():
x = None
y = None
z = None
name = None
playerId = None
Help would be greatly appreciated!