How to connect the Lz.
fixed this link again
webhost was deleting the file, who knows why
anyways it on rapidshare now so i shouldnt have to fix it again.
Heres a direct link:
have problem to download
edit: already download it. But have no time to try it yet.
Great piece, but I am running into basic problems with data being received. I noticed whenever the 2nd client connects to the server data being sent from the client is fine (happens quickly) but the data received starts taking a lot of time (after some tests it exp goes up in time). What could be the problem with that? I’ve gone over the code many times to try and pinpoint a bottleneck with the data but right now it looks like everything is by the book with queuedmanager and I don’t see why it would be doing this. Thanks for any input.
Ok so after some testing on timing, I figured out that data being sent from the client to the server is basically being backed up and the server is unable to process it quick enough. The data being passed is x,y,z,h,p,r and any chat that is sent. The update speed is just above 0.1 seconds. After I changed the update speed of the client to .2 seconds it works for 2 players fine but my guess is anything after that I would have to increase the update speed of the client to compensate for many users. Any tips on how to achieve faster update speeds without the loss of actual game play speed from connected clients?
Reduce the amount of work the server has to do for each data point? It certainly shouldn’t take 0.1 seconds to process a single data point; if it’s really taking that long, you’ll need to reconsider what your server is doing.
David
Ya its better now, I really didn’t think closing out the panda3d window would make that much of a difference but it does. Now its just a matter of creating the disconnect piece for server/client and start the actual game mechanics.
how if the character model not at the client side?
anyone still has the game?
Hello Everyone,
I coded this up quite a long time ago, but managed to find it.
If anyone is still looking for it, the source code is here:
https://github.com/Mozoby/Panda3D-Multiplayer-Chat
Enjoy!
Hey, this looks cool. I tried downloading it and got the server side running but when I ran the client, I’m getting this error:
Traceback (most recent call last):
File "C:\Panda3D\projects\mpchat\client\client.py", line 30, in tskReaderPolling
playerRegulator.ProcessData(self.datagram, m,chatClass)
File "C:\Panda3D\projects\mpchat\client\client.py", line 74, in ProcessData
for i in range(self.num):
TypeError: range() integer end argument expected, got float.
:task(error): Exception occurred in PythonTask Poll the connection reader
Traceback (most recent call last):
File "main.py", line 50, in <module>
run()
File "C:\Panda3D\direct\showbase\ShowBase.py", line 52, in legacyRun
builtins.base.run()
File "C:\Panda3D\direct\showbase\ShowBase.py", line 2972, in run
self.taskMgr.run()
File "C:\Panda3D\direct\task\Task.py", line 503, in run
self.step()
File "C:\Panda3D\direct\task\Task.py", line 461, in step
self.mgr.poll()
File "C:\Panda3D\projects\mpchat\client\client.py", line 30, in tskReaderPolling
playerRegulator.ProcessData(self.datagram, m,chatClass)
File "C:\Panda3D\projects\mpchat\client\client.py", line 74, in ProcessData
for i in range(self.num):
TypeError: range() integer end argument expected, got float.
Any ideas? I’m using 1.9 dev.
Thanks.
The error indicates that the “range” function expects an integer, and is being given a float instead; somehow, “self.num” is being assigned a floating-point value rather than an integer one.
I don’t know the project in question, so I’m not in a position to speculate on how “self.num” is ending up with a floating-point value, but you may be able to find the cause by hunting through the code, looking at lines that assign values to “self.num”.
I tried changing it to integer just to check it out, but still throwing some errors…
Well, what errors are you getting?
First I noticed that the client connects to a different address (192.168.x.x) so I changed it to localhost or 127.0.0.1, this is now the error I’m getting with the float range error
Hmm… Taking a quick look at the code to which you previously linked, I see that it does indeed appear to be loading a floating-point number into “self.num”–and does so in more than one place–which seems rather odd given that “self.num” appears to be an integer count… o_0
Regarding the address, I’m not sure that 192.168.x.x wasn’t correct: while 127.0.0.1 may be the “localhost” address, 192.168.x.x might well be your computer’s address on your LAN (even if that consists of just one computer and a router)–if I recall correctly, LAN addresses tend to start with “192.168”. It may well be that the multiplayer system requires that packets are handled by the router at least, and so might not work with the “localhost” address–but that’s just a guess.
However, I’m afraid that I’m not very familiar with multiplayer code, so I don’t feel that I’m in much of a position to help further.
Ok got it. Yes, I see that 192.168.x.x is also local but it’s weird because it’s the same ip address format (127.0.0.1). Most multiplayer lan/net code can be simulated over lan, that is at least from my experience. Anyways, I can circle back on this next time when I got a chance.
Thanks Thaumaturge. I appreciate your help anyways
Hey Wini,
Sorry for the bad error experience. I should’ve invested some time to make sure this still worked.
As it is, someone (or you?) made a pull request on Github. I’ve merged the changes and all should be working as expected now.
Greetings everyone I wanted to let you know of a few changes I made to this client server code.
All the changes I made are in the client.py file. The main issues I wanted to address were threefold:
1.The client would crash if no server connection was established. This has been fixed.
2.The Chat interface was not working as intended. Pressing the [t] key was meant to open up the chat but was not properly setting the focus to the chat entry. This has been fixed and the code for placement of the chat has been changed to pin it to the lower left corner regardless of screen resolution.
3.The camera was, in my open, not practical. The code for moving the camera has been replaced with code that allows the user to toggle between 3 camera views. The default is a third person shoulder level PoV, the second is a first person PoV, and the third is a free camera PoV ( hold RMB to zoom in and out, hold MMB to change angle of view, press [c] key to toggle between views ).
3a. Auto Run is now available by pressing the period key [.] on either the standard keyboard or the numpad.
posted below is the code. Let me know what you think.
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
from direct.actor.Actor import Actor
from direct.task.Task import Task
from direct.gui.DirectGui import *
import sys
class Client(DirectObject):
def __init__(self,p,i):
self.cManager = QueuedConnectionManager() #Manages connections
self.cReader = QueuedConnectionReader(self.cManager, 0) #Reads incoming Data
self.cWriter = ConnectionWriter(self.cManager,0) #Sends Data
self.port = p #Server's port
self.ip = i #server's ip
self.Connection = self.cManager.openTCPClientConnection(self.ip,self.port,3000) #Create the connection
if self.Connection:
self.cReader.addConnection(self.Connection) # receive messages from server
else:
print 'connection failed'
def tskReaderPolling(self,m,playerRegulator,chatClass):#this function checks to see if there is any data from the server
if self.cReader.dataAvailable():
self.datagram=NetDatagram() # catch the incoming data in this instance
# Check the return value; if we were threaded, someone else could have
# snagged this data before we did
if self.cReader.getData(self.datagram):
playerRegulator.ProcessData(self.datagram, m,chatClass)
self.datagram.clear()
return Task.cont
class Terrain(GeoMipTerrain):
def __init__(self):
self.terrain = GeoMipTerrain("mySimpleTerrain")
self.terrain.setHeightfield(Filename("heightmap.png"))
self.terrain.setColorMap(Filename("terrain.bmp")) #pjb comment this line out if you want to set texture directly
#myTexture = loader.loadTexture("terrain.bmp") #pjb UNcomment this line out if you want to set texture directly
self.terrain.setBlockSize(32)
self.terrain.setBruteforce(True)
#self.terrain.setNear(40)
#self.terrain.setFar(100)
self.terrain.setFocalPoint(base.camera)
self.terrain.getRoot().setSz(100)
self.time = 0
self.elapsed = 0
self.terrain.getRoot().reparentTo(render)
self.terrain.generate()
#self.terrain.getRoot().setTexture(myTexture) #pjb UNcomment this line out if you want to set texture directly
#taskMgr.doMethodLater(5, self.updateTerrain, 'Update the Terrain')
#taskMgr.add(self.updateTerrain, "update")
def updateTerrain(self,task):
self.elapsed = globalClock.getDt()
self.time += self.elapsed
if (self.time > 5):
self.terrain.update()
self.time = 0
return Task.again
class PlayerReg(DirectObject): #This class will regulate the players
def __init__(self):
self.playerList = []
self.numofplayers = 0
def ProcessData(self,datagram, m,chatClass):
#process received data
self.iterator = PyDatagramIterator(datagram)
self.type = self.iterator.getString()
if (self.type == "init"):
print "initializing"
#initialize
m.setPlayerNum(self.iterator.getUint8())
self.num = self.iterator.getFloat64()
for i in range(int(self.num)):
if (i != m.playernum):
self.playerList.append(Player())
self.playerList[i].username = self.iterator.getString()
self.playerList[i].load()
self.playerList[i].currentPos['x'] = self.iterator.getFloat64()
self.playerList[i].currentPos['y'] = self.iterator.getFloat64()
self.playerList[i].currentPos['z'] = self.iterator.getFloat64()
print "player ", str(i), " initialized"
else:
self.playerList.append(Player())
self.numofplayers = self.num
if (self.type == "update"):
self.num = self.iterator.getFloat64()
if (self.num > self.numofplayers):
for i in range(int(self.numofplayers)):
self.playerList[i].currentPos['x'] = self.iterator.getFloat64()
self.playerList[i].currentPos['y'] = self.iterator.getFloat64()
self.playerList[i].currentPos['z'] = self.iterator.getFloat64()
self.playerList[i].currentPos['h'] = self.iterator.getFloat64()
self.playerList[i].currentPos['p'] = self.iterator.getFloat64()
self.playerList[i].currentPos['r'] = self.iterator.getFloat64()
for i in range(int(self.numofplayers),int(self.num)):
if (i != m.playernum):
self.playerList.append(Player())
self.playerList[i].load()
self.playerList[i].currentPos['x'] = self.iterator.getFloat64()
self.playerList[i].currentPos['y'] = self.iterator.getFloat64()
self.playerList[i].currentPos['z'] = self.iterator.getFloat64()
self.playerList[i].currentPos['h'] = self.iterator.getFloat64()
self.playerList[i].currentPos['p'] = self.iterator.getFloat64()
self.playerList[i].currentPos['r'] = self.iterator.getFloat64()
else:
self.playerList.append(Player())
self.numofplayers = self.num
else:
for i in range(int(self.numofplayers)):
self.playerList[i].currentPos['x'] = self.iterator.getFloat64()
self.playerList[i].currentPos['y'] = self.iterator.getFloat64()
self.playerList[i].currentPos['z'] = self.iterator.getFloat64()
self.playerList[i].currentPos['h'] = self.iterator.getFloat64()
self.playerList[i].currentPos['p'] = self.iterator.getFloat64()
self.playerList[i].currentPos['r'] = self.iterator.getFloat64()
if (self.type == "chat"):
self.text = self.iterator.getString()
chatClass.setText(self.text)
def updatePlayers(self,m):
if (self.numofplayers != 0):
for k in range(int(self.numofplayers)):
#As long as the player is not the client put it where the server says
if(k != m.playernum):
self.playerList[k].model.setPosHpr(self.playerList[k].currentPos['x'],self.playerList[k].currentPos['y'],self.playerList[k].currentPos['z'],self.playerList[k].currentPos['h'],self.playerList[k].currentPos['p'],self.playerList[k].currentPos['r'])
return Task.cont
class Me(DirectObject):
def __init__(self, terrainClass):
self.model = Actor("models/ninja", {"walk":"models/ninja"})
self.actorHead = self.model.exposeJoint(None, 'modelRoot','Joint8')
#self.model.setScale(4)
self.playernum = None
self.timeSinceLastUpdate = 0
self.model.reparentTo(render)
self.model.setScale(0.5)
self.isMoving = False
self.AnimControl=self.model.getAnimControl('walk')
self.AnimControl.setPlayRate(0.05)
self.model.setBlend(frameBlend=1)
self.model.setPos(244,188,0)
#STORE TERRAIN SCALE FOR LATER USE#
self.terrainScale = terrainClass.terrain.getRoot().getSz()
base.camera.reparentTo(self.model)
def setPlayerNum(self,int):
self.playernum = int
def move(self, keyClass, terrainClass):
#self.meTerrainHeight = terrainClass.terrain.getElevation(self.model.getX(),self.model.getY()) * self.terrainScale
#self.camTerrainHeight = terrainClass.terrain.getElevation(camera.getX(),camera.getY()) * self.terrainScale
self.camDummy = self.model.attachNewNode("camDummy")
self.camDummy.setZ(5)
self.elapsed = globalClock.getDt()
#base.camera.lookAt(self.actorHead)
if (keyClass.keyMap["left"]!=0):
self.model.setH(self.model.getH() + self.elapsed*300)
print str(self.model.getY()), str(self.model.getX())
if (keyClass.keyMap["right"]!=0):
self.model.setH(self.model.getH() - self.elapsed*300)
if (keyClass.keyMap["forward"]!=0):
self.model.setY(self.model, (self.elapsed*40))
if (keyClass.keyMap["back"]!=0):
self.model.setY(self.model, -(self.elapsed*40))
if (keyClass.keyMap["forward"]!=0) or (keyClass.keyMap["left"]!=0) or (keyClass.keyMap["right"]!=0):
if self.isMoving is False:
self.model.loop("walk", fromFrame = 1, toFrame = 11)
self.isMoving = True
else:
if self.isMoving:
self.model.stop()
self.model.pose("walk",5)
self.isMoving = False
self.meTerrainHeight = terrainClass.terrain.getElevation(self.model.getX(),self.model.getY()) * self.terrainScale
self.model.setZ(self.meTerrainHeight )
#CAMERA CONTROL#
#base.camera.reparentTo(self.model)
base.camera.lookAt(self.camDummy)
base.camLens.setNear(.1)
if (keyClass.keyMap["cam"]==1):
#base.camera.setZ(5)
#base.camera.setY(1)
base.disableMouse()
base.camera.setPosHpr(0,2,5,0,0,0)
elif (keyClass.keyMap["cam"]==2):
#base.camera.setPosHpr(0,-30,10,0,0,0)
base.enableMouse()
else:
base.disableMouse()
base.camera.setPosHpr(0,-30,10,0,0,0)
#base.camera.setZ(10)
#base.camera.setY(-30)
"""
self.camvec = self.model.getPos() - base.camera.getPos()
if (self.camTerrainHeight > self.meTerrainHeight):
camera.setZ(self.camTerrainHeight + 5)
else:
camera.setZ(self.meTerrainHeight + 5)
self.camvec.setZ(0)
self.camdist = self.camvec.length()
self.camvec.normalize()
if (self.camdist > 20):
base.camera.setPos(base.camera.getPos() + self.camvec*(self.camdist-20))
self.camdist = 20.0
if (self.camdist < 10):
base.camera.setPos(base.camera.getPos() - self.camvec*(10-self.camdist))
self.camdist = 10.0
"""
return Task.cont
class World(DirectObject): #This class will control anything related to the virtual world
def __init__(self):
self.timeSinceLastUpdate = 0
def UpdateWorld(self,meClass,clientClass):
#get the time since the last framerate
self.elapsed = globalClock.getDt()
#add it to the time since we last set our position to where the server thinks we are
#add the elapsed time to the time since the last update sent to the server
self.timeSinceLastUpdate += self.elapsed
if (self.timeSinceLastUpdate > 0.1):
self.datagram = PyDatagram()
self.datagram.addString("positions")
self.datagram.addFloat64(meClass.model.getX())
self.datagram.addFloat64(meClass.model.getY())
self.datagram.addFloat64(meClass.model.getZ())
self.datagram.addFloat64(meClass.model.getH())
self.datagram.addFloat64(meClass.model.getP())
self.datagram.addFloat64(meClass.model.getR())
try:
clientClass.cWriter.send(self.datagram,clientClass.Connection)
except:
print "No connection to the server. You are in stand alone mode."
return Task.done
self.timeSinceLastUpdate = 0
return Task.cont
class Keys(DirectObject):
def __init__(self):
self.isTyping = False
self.keyMap = {"left":0, "right":0, "forward":0, "back":0, "cam":0 ,"right":0,"autoRun":0}
self.accept("escape", sys.exit)
self.accept("arrow_left", self.setKey, ["left",1])
self.accept("arrow_right", self.setKey, ["right",1])
self.accept("arrow_up", self.setKey, ["forward",1])
self.accept("arrow_down", self.setKey, ["back",1])
self.accept("arrow_left-up", self.setKey, ["left",0])
self.accept("arrow_right-up", self.setKey, ["right",0])
self.accept("arrow_up-up", self.setKey, ["forward",0])
self.accept("arrow_down-up", self.setKey, ["back",0])
self.accept("c", self.toggleCam)
self.accept(".", self.autoRun)
#self.accept("a", base.oobe)
def setKey(self, key, value):
if not self.isTyping:
self.keyMap[key] = value
def autoRun(self):
if not self.keyMap["autoRun"]:
self.setKey("autoRun", 1)
self.setKey("forward" , 1)
else:
self.setKey("autoRun", 0)
self.setKey("forward" , 0)
def toggleCam(self):
if self.keyMap["cam"] == 1:
self.setKey("cam",2)
elif self.keyMap["cam"] == 0:
self.setKey("cam",1)
else:
self.setKey("cam",0)
class Player(DirectObject):
def __init__(self):
self.currentPos = {'x':244,'y':188,'z':0,'h':0,'p':0,'r':0} #stores rotation too
self.isMoving = False
self.username = ""
def load(self):
self.model = Actor("models/ninja", {"walk":"models/ninja"})
self.model.reparentTo(render)
self.model.setScale(0.5)
self.isMoving = False
self.AnimControl=self.model.getAnimControl('walk')
self.AnimControl.setPlayRate(0.05)
self.model.setBlend(frameBlend=1)
class chatRegulator(DirectObject):
def __init__(self,clientClass,keysClass):
self.maxMessages = 14
self.messageList = []
self.client = clientClass
self.keys = keysClass
#for gui debug
self.accept("p", self.getWidgetTransformsF)
#Create GUI
#self.frame =
self.chatInput = DirectEntry(initialText = "Press 't' or click here to chat",
cursorKeys = 1,
numLines = 1,
command = self.send,
focusInCommand=self.handleTpress,
focusOutCommand = self.resetText,
focus = 0,
width = 20)
#self.chatInput.setPos(-1.31667,0,-0.97)
self.chatInput.setScale(0.05)
self.chatInput.reparentTo(base.a2dBottomLeft)
self.chatInput.setPos(.05,0,.05)
self.messages = []
self.txt = []
for k in range(14):
self.txt.append(OnscreenText(mayChange = 1))
self.messages.append(DirectLabel(activeState = 1, text = "hi"))
#self.messages[k].setScale(0.0498732)
#self.messages[k].setPos(-1.31667,0,-0.9)
self.accept("t",self.handleTpress)
self.accept("control-t",self.resetText)
self.calls = 0
def handleTpress(self):
if not self.keys.isTyping:
self.clearText()
def clearText(self):
self.chatInput.enterText('')
self.keys.isTyping = True
self.chatInput["focus"]=True
def resetText(self):
self.chatInput.enterText('')
self.keys.isTyping = False
#def leaveText(self):
# self.keys.isTyping = False
def send(self,text):
self.datagram = PyDatagram()
self.datagram.addString("chat")
self.datagram.addString(text)
self.client.cWriter.send(self.datagram,self.client.Connection)
def setText(self,text):
self.index = 0
#put the messages on screen
self.messageList.append(text)
if (len(self.messageList)>14):
self.messageList.reverse()
del self.messageList[14]
self.messageList.reverse()
for k in self.messageList:
self.text(k,(-.95,( -.8 + (.06 * self.index ) )),self.index)
self.index += 1
def getWidgetTransformsF(self):
for child in aspect2d.getChildren():
print child, " position = ", child.getPos()
print child, " scale = ", child.getScale()
def text(self,msg,position,index):
self.txt[index].destroy()
self.txt[index] = OnscreenText(text = msg, pos = position,fg=(1,1,1,1),align = TextNode.ALeft, scale = .05,mayChange = 1)
self.txt[index].reparentTo(base.a2dBottomLeft)
self.txt[index].setPos(.05,.15+.05*index)
Awesome work grishnak!
Please consider sending a PR to the github repo! More than happy to get it merged so everyone can benefit from these improvements.