Distributed Networking

Ah, that doesn’t work so well for a non-MMO, where players change hosts every match or two. Players wouldn’t be happy having to restart the game after every 10-15 minute match.

So there’s no clean-up built into the repositories. Do they contain circular references? I suppose I can test that myself. If it comes down to it, I may have to add clean-up functionality to them. If I do so, where would I submit the code for inclusion in the project?

So I figured the first step to cleaning up a client (after it has disconnected from a server with a sendDisconnect call) would be to eliminate all of the DistributedObjects and DistributedNodes it’s aware of.

I looked up all the objects of the class Player by using ClientRepository.getObjectsOfClass(Player) and got the expected number of items in the dictionary that was returned. I iterated through the dictionary calling ClientRepository.deleteObject(DoId) on every DoId in it.

Here’s the rub: doing that gets me a response from the del method of the Player class for the instance that was generated on a different client, but the local instance doesn’t give me the same response, meaning it’s still sitting around. Something is still referencing it.

As far as the local clean up on the Player class, it’s being called for both. All of the Player instance’s variables are set to None, and the method to remove it from the dictionary that retains a reference to it is also being called correctly and correctly removing the reference to it. I don’t have any further local references.

It seems to me that the Distributed system is still holding onto a reference to the locally created Player instance, but not the Player instance created by a different client. I further verified this theory by adding a third client. With three clients involved, I get two del messages, but not a third.

Do you have any idea what else I need to do to clean up the locally created instance of Player?

Here’s another question:

I’ve been adding a method I call setNetPrepare to all of my distributed objects. This method takes all of the initialization values for the object as arguments, like the object’s name, the team it’s associated with, the name of the player that owns it, etc. It also performs necessary tasks, like placing a reference to the instance of the object into the appropriate place, so the instance can later be acted upon. This method is called right after the object is created, to initialize it, and has the keywords broadcast and ram applied to it.

When a new client connects to the server, the setNetPrepare method is re-broadcast, causing the object to be re-initialized on all the clients. For reasons I honestly don’t know, this didn’t manifest itself as a problem until I started messing around with 3 clients instead of 2, but it clearly is a problem. I’ve gotten around it by forcing my distributed objects to verify they have not been initialized already before initializing, but I’m betting that there’s a better fix here.

I started using the setNetPrepare method because I couldn’t figure out another way to initialize a distributed object with the right values. What I want is to pass in values to the object at creation time, and have clients connecting later receive those values when they create the object because it is in a zone they’re interested in, but not have the initialization re-broadcast to clients that have already performed it. Is there a better way to do that?

In order to get around the clean up problems with the distributed system I’ve split my game into two parts. The first part is main menu type stuff, and it calls the second part, which is the meat of the game, as a subprocess. This way I can use sys.exit to clean everything up without fully exiting the game.

I haven’t tested yet how this will work with pdeploy, so I don’t know if it will be ulitmately viable. Even it does end up being viable, I’d rather not have to do it this way in the final product, so if it’s possible to answer the clean up questions I posted earlier I’d appreciate it.

While I’ve been waiting for a response, I’ve been digging through the code for the distributed system to find my own answers, and I haven’t come up with much. I’m pretty sure that CConnectionRepository.shutdown() is the final arbiter of the clean up operations because, through an intermediary or two, it gets added to base.finalExitCallbacks in the ClientRepository. Unfortunately, since CConnectionRepository is a C++ class I can’t see what that method does, and probably wouldn’t fully understand it or be able to utilize it if I could.

My apologies for the slow response. I’ve been very busy lately and haven’t had a chance to delve into the answers to in-depth questions like this. I’ll try to get to it soon.

David

It’s okay, I’m just glad to know that you’re willing to look into it. With the reply you gave to my post about pdeploy, packp3d, and subprocesses in this thread: [url]Deploying with pdeploy and packp3d when using subprocess], I have to conclude that the suprocess idea to allow clean up via sys.exit isn’t going to work unless I packed up each part of the game separately and installed them both, which would mean finding a way to either create my own installer or tie the two installers together, and that’s a whole new monster to deal with since I have no experience in such things.

Hopefully, with your help, I can come up with another solution.

Reading over this thread, it sure seems like I’m recreating a lot of what distributed networking will do. It’s been a good learning experience though :slight_smile: Please keep the thread alive. I have enough network capability to move on and wait for that tutorial someday soon.

Have you had a chance to look for the answers to these questions?

Here’s an easier question to help me get started on breaking this stuff down on my own.

How do I delete an instance of a class that inherits from DistributedNode, and is created through clientrepository.createDistributedObject?

I’ve created two versions of my Shot class, which I use for all of the different kinds of projectiles in the game. One version inherets from DistributedNode and gets propogated over the network, and one version doesn’t inherit from anything and doesn’t get propogated over the network.

Both versions have the same method for cleaning up their own content, called destroy. It removes the NodePaths, the collision traverser, ends the movement task, etc.

The version that inherits from DistributedNode also has a method called delete, which calls DistributedNode.delete(), which I took from the Net example you made and you pointed me to.

When I call the destroy method on the version that inherits from nothing, the class instance gets removed from memory and the del method is called.

When I call the destroy and delete methods on the version that inherits from DistributedNode, the del method is never called.

I did some further testing with getrefcount, and I found that the DistributedNode version has one extra reference to it somewhere after delete and destroy have been called. (I printed getrefcount from inside the class’s task, and it reported 5 references. 1 for the temporary reference for the getrefcount call, 3 references for the running task, and 1 more.)

I’m not sure where this extra reference is being kept, or what I’m supposed to do to get rid of it.

PS: I know that a running task implies 3 references because of the following program, which prints out 5 for 250 frames and then prints out 2 until closed.

import direct.directbase.DirectStart
from sys import getrefcount

class World:
	def __init__(self):
		self.tmrc = TaskManagerRefCount(self)
		
	def removeTMRC(self):
		self.tmrc = None
		
	def startRefCountTMRC(self):
		taskMgr.add(self.refCountTMRCTask, "Print Ref Count of TMRC from World")
		
	def refCountTMRCTask(self, task):
		print(getrefcount(self.tmrc))
		return task.cont

class TaskManagerRefCount:
	def __init__(self, world):
		taskMgr.add(self.printRefCountTask, "Print Ref Count Task")
		self.count = 0
		self.world = world
	
	def printRefCountTask(self, task):
		print(getrefcount(self), self.count)
		self.count += 1
		if(self.count == 250):
			#self.world.removeTMRC()
			self.world.startRefCountTMRC()
			return task.done
		
		return task.cont
		
w = World()
run()

I’m sorry for the long delay in replying–I haven’t forgotten about this thread, but I’ve been so busy lately, I haven’t had a chance to answer any questions that require more than a few seconds of thought. And this question does. :wink:

I’ll come back to it shortly, I promise.

David

I can’t really move ahead until this is solved, so I’ll be here waiting until you do have the time. Not trying to rush, just letting you know I haven’t forgotten it either.

Seeing that 1.8.0 was recently released gives me an idea of what’s been keeping you busy.

Much as I’d like to take credit for the 1.8 release, that’s almost entirely thanks to rdb.

I’ve been busy with work responsibilities, which aren’t related to Panda at all.

David

Ah, okay.

Have you had a chance to look into the answers for any of these questions, yet?

When came back and looked over this thread, I noticed your post about:

clientRepository.sendDeleteMsg(obj.getDoId())

I must have overlooked it or something, to my own folly. I did some testing with it and I’ve got my Shot class calling it’s del method on both the host and the client now, which is pretty nice.

With that figured out, I’m pretty sure I can clean up all of the distributed objects and distributed nodes a copy of the game has put into the computer’s memory. I still can’t remove the ServerRepository or ClientRepository, as far as I know.

That begs the question: if I can clean up all of the distributed objects and nodes, do I need to clean up the repositories or should I just reduce, reuse, and recycle like they taught me in grade school?

Unfortunately, I’ll have to come back to this game later to answer that question. I’ve got a non-networked project that I’m working on and need to get onto Steam fast to ensure my fiscal survival.

Hey guys,
I have been away from panda for quite some time and have been looking to get back into it. I would like to try to get my example code working on the current panda build. I will take some time in the near future and try to get my old project back up and running. Cheers for updating the docs and such. Let me see if I can find all my old source code. 8)

Haven’t updated the docs yet. I’m still working to get my game company started before I can come back to this. Looking forward to seeing your network code, though.

Well it looks like I found all of my old code so I will spend some time trying to get it running and post my progress on here.

Well the old code I have tried so far all works on the newest build of panda including the basic networking piece. I am able to start a server instance and connect 2 clients to it that can see each other and switch zones and drop bouncing pellets. I will attempt to get the piece working that uses the roaming ralph model and does smoothing and animation next.

It’s nice to see that the code still works.

note: this code is based on David’s example code

I thought the code would not run without at least some minor adjustments but it loaded and ran just fine on 1.7.2. I should be able to pick up where I left off. The smoothing and animation for distributed Ralph work just as I remember. Here is a screen shot. Sorry for the gigantic screen shot (it’s my dual monitor setup). :smiley: