Game Development Community

Networking dynamically created datablocks.

by Lukas Joergensen · in Torque 3D Professional · 01/31/2013 (12:12 pm) · 13 replies

Warning you'll probably hate me for this code


I'm trying to dynamically create a datablock like this:
%bhv1 = new AttractionBehaviour(){
   attractionrange = 10;
   Attraction_offset[0] = "0 0.3 1.5";
   Amount[0] = 2;
   AttractionMode[0] = Attract;
   attractedObjectID[0] = %spell.getSource();
};
%spell.baseEmitter = new GraphEmitterNode(){
   dataBlock = g_defaultNode;
   emitter = GroundedSplashEmitter;
   ParticleBehaviour[0] = %bhv1;
   standAloneEmitter = true;
   position = %spell.getSource().position;
   Grounded = true;
};

However, both the client and the server need to know of this datablock. How can I send the datablock to the client?
Doing it this way:
//server
if(stream->writeFlag(ParticleBHVs[i]))
{
	stream->writeInt(ParticleBHVs[i]->getId(), 32);
}

//client
if ( stream->readFlag() )
{
	SimDataBlock *dptr = 0;
	SimObjectId id = stream->readInt(32);
	Sim::findObject( id, dptr );
	if(dptr)
		ParticleBHVs[i] = (IParticleBehaviour*)dptr;
}
Seems to work.. But it seems so wrong in every way! Also it seems like it only works most of the time. Any ideas?

#1
01/31/2013 (1:04 pm)
I did a search and I don't see how the datablocks are normally downloaded. They are downloaded in phase 1 of a mission download so maybe seeing how that is done will help.

Also, are you actually downloading the datablock here? It looks like just the ID is being sent. Not the actual datablock. That could explain why it does not work sometimes. It is not being downloaded if it is being dynamically created after mission load.

So, either you need to explicitly transfer the datablock (not just the id) or do a command to client with the proper data to have the client build the datablock on the fly.
#2
01/31/2013 (1:09 pm)
Yes I also read that creating datablocks with the new keyword created local-only datablocks...
But then why does it even work lol?
It shouldn't be working at all! Seems like the datablock is downloaded to the client even when instanced with the 'new' keyword.
#3
01/31/2013 (2:50 pm)
Well it would work on a local server/client. But not on a client networked to the server. Now, if it is working in a client server setup with a remote machine, then you got me on that. That would be messed up, unless there is some kind of transfer going on.
#4
01/31/2013 (2:53 pm)
Ah, yes it probably wouldn't work on a remote machine thats the catch!
Well I guess I will have to write a copy of the datablock to the stream then.
#5
01/31/2013 (4:23 pm)
You're correct about it not working on the client machine. A particle from a dynamically created datablock probably wouldn't be visible to the client. Still, I'm intrigued by what you're trying to do. Allow me to brainstorm for a minute.

Both server and client have the ability to create data blocks, right? What if the process was function mediated? Instead of having identical copies of a data block on both the server and client, what if you had copies of the same function that would create datablocks? It might allow you to "fake it" for things like particles. This function would take all of your dynamic attributes as arguments. You would be able to call this function on either server or client, and use a 'commandToClient(or Server)' call to create the same kind of datablock on the other machine by passing all of the same arguments. What I'm trying to say makes since in my head, but I'm having trouble translating that to writing. :p

Anywho, let us know what you come up with.
#6
01/31/2013 (5:26 pm)
Procedural might be the way to go, unless you have a ton of datablocks to generate.
#7
01/31/2013 (7:50 pm)
There is a way to make a client-only datablock for decals and particles - but I can't be more helpful than that at the moment; been a while since I even thought about it, let alone looked at it.
#8
01/31/2013 (10:34 pm)
@Caleb that makes sense from a networking point of view compared to what I had thought of.

What I had thought of was to simply write the data from the datablocks with the packUpdate and then initialize a new datablock on the client side and call unpackUpdate on the new datablock.
#9
02/02/2013 (1:09 am)
The unasked question, of course, is why do you need a dynamically-created datablock? Maybe we can suggest a solution to your problem that involves less tricky workarounds.
#10
02/02/2013 (3:42 am)
@Daniel I need an object that can both be instantiated as static global functions and as local functions.
If I have a StickyBehaviour, which makes particles move relative to the emitter instead of flying around (like using object coordinates instead of world coordinates) it doesn't need to have any special fields and hence only one instance is needed for each object using it, so it is best to just create it like this:
new StickyBehaviour(Sticky_BHV);
But if you have an attracted object behaviour that needs to know individually what object it should be attracted to you'd have to do this:
%bhv = new AttractedObjectBehaviour(){
// bla bla
AttractedObject[0] = %obj
};
#11
02/02/2013 (8:56 am)
It could be a script object. It does not necessarily need to be a datablock. If it is a datablock per object then script objects would work fine. It could be on the client or networked too. Then if the script object needs default data they could have a separate datablock too.

If it helps I created a resource that allows you to save to string for any SimObject. It will generate the script to create the original object stored in a string. This could be packed down and transferred over the wire. It should work on datablocks I think:
www.garagegames.com/community/resources/view/21813

For serialization over it could use a helper function that strips out the extra characters and rebuilds the data on the other end. However, it probably does not matter. Come to think about this you may be able to serialize the object just by using the object stream function right into the network stream. Not sure.
#12
02/03/2013 (9:22 am)
Update: This is what I did and seems to be working.
In the node's packUpdate:
if(stream->writeFlag(saUpdateBits & saBehaviour)){
		for(int i = 0; i < ParticleBehaviourCount; i++)
		{
			if(stream->writeFlag(ParticleBHVs[i]))
			{
				if(stream->writeFlag(ParticleBHVs[i]->isClientOnly()))
				{
					if(stream->writeFlag(((IParticleBehaviour*)ParticleBHVs[i])->getBehaviourType() != behaviourClass::Error))
					{
						stream->writeInt(((IParticleBehaviour*)ParticleBHVs[i])->getBehaviourType(), 3);
						ParticleBHVs[i]->packData(stream);
					}
				}
				else
				{
					stream->writeRangedU32(ParticleBHVs[i]->getId(), DataBlockObjectIdFirst,
						DataBlockObjectIdLast);
				}
			}
		}
	}

And then in the unpackUpdate:
if(stream->readFlag())
	{
		for(int i = 0; i < ParticleBehaviourCount; i++)
		{
			if ( stream->readFlag() )
			{
				if(stream->readFlag())
				{
					if(stream->readFlag())
					{
						U8 type = stream->readInt(3);
						switch (type)
						{
						case behaviourClass::AttractionBehaviour:
							ParticleBHVs[i] = new AttractionBehaviour();
							break;
						default:
							break;
						}
						if(ParticleBHVs[i])
							ParticleBHVs[i]->unpackData(stream);
					}
				}
				else
				{
					SimDataBlock *dptr = 0;
					SimObjectId id = stream->readFlag() ?
						stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0;
					if(	Sim::findObject( id, dptr )	)
						ParticleBHVs[i] = (IParticleBehaviour*)dptr;
				}
			}
		}
	}
Just need to take care of cleaning up after use when the node is destroyed or the behaviour is overridden.
#13
03/09/2013 (5:31 pm)
@Lukas,

I think that I read someplace in the documentation that in a client/sever scenario a Datablock, once set by the server can not be modified. Maybe I misunderstood, in that the Datablock could be modified only by the server. Mind clarifying?

In the very near future I will be doing something similar, where Datablocks, or properties of them, will be initialized or modified by the server and sent out to the game client. Still in the design phase for this function currently, and somewhat held back by a lack of knowledge of the engine/platform.