Is this the right way to do it?

Hello, I am sill toying around with SpiderMonkey and would probably benefit from some advice.

I created a WorldServer application (Headless) that sends transformation data to a WorldClient.

The client loads a couple of models, attaches them to nodes and and waits for positional/velocity data coming from the server.

First thing I would like to have feedback on :
I assumed that the server might not be able to send timely transformation data to have a smooth rendering on the client (i.e. it might not be able to update the client at every frame).
Based on that assumption I implemented an interpolation of translation and rotation for every object. Interpolation is based on velocity only, not acceleration (so it is a linear interpolation, which might be ok I guess if the gap between two updates is not too high).

[java]

ConcurrentHashMap<Integer,ObjData> objects; // ObjData is pretty obvious, see below.

@Override
public void simpleUpdate(float tpf)
{
Iterator it = objects.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry)it.next();
ObjData od = (ObjData)entry.getValue();
// interpolation
od.rotation= od.rotation.add(od.rotVelocity.mult(tpf));
od.translation = od.translation.add(od.traVelocity.mult(tpf));
// update
Spatial sp = rootNode.getChild(""+(Integer)entry.getKey()); //I know I should improve this :slight_smile:
if (sp!=null)
{
sp.setLocalTranslation(od.translation);
float[] rot = od.rotation.toArray(null);
sp.setLocalRotation(new Quaternion(rot));
}
}
}
[/java]

The server, so far, does not get any input (it is just a test) , so it simply interpolates the real position and rotation in the same way, but in theory I do expect the velocity vectors to be controlled by some input (maybe from another client etc).
I used a ScheduledExecutorService

[java]
ScheduledExecutorService updExec = Executors.newSingleThreadScheduledExecutor();

public void initUpdater(int msecs)
{
updExec.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
long newTime =System.currentTimeMillis();
if (lastUpdate>0)
{
Iterator it = objects.entrySet().iterator();
float tpf = ((float)(newTime-lastUpdate))/1000f;
while (it.hasNext())
{
Map.Entry entry = (Map.Entry)it.next();
ObjData od = (ObjData)entry.getValue();
od.rotation = od.rotation.add(od.rotVelocity.mult(tpf));
od.translation = od.translation.add(od.traVelocity.mult(tpf));
// objects.put((Integer)entry.getKey(), od);
}
server.broadcast(new ObjMessage(new HashMap(objects)));
lastUpdate = newTime;
}
}, msecs, msecs, TimeUnit.MILLISECONDS);
}
[/java]

It actually works (jsurt because JME & SpiderMonkey are so cool :stuck_out_tongue: ), I am sending updates every 300 ms (normally it would be faster, but I am simulating an eventual ,load with many connected clients etc), rendering on the client is quite smooth thanks to the interpolation.

First thing :
Is this scheduled service an appropriate choice? It is basically a scheduled thread, nothing special…

Second thing : I am using a ConcurrentHashMap (thanks pspeed for the hint oin a previous thread here)
ConcurrentHashMap<Integer,ObjData> objects;

however I am not able to pass it to the Message class. I have to pass it as HashMap and convert it back (Instantiating a new object each time).

[java]

@Serializable
public static class ObjMessage extends AbstractMessage
{
HashMap objects;

	public ObjMessage()
	{
		
	}
	
	public ObjMessage(HashMap&lt;Integer,ObjData&gt; objs) // would not accept a concurrentHashMap here
	{
		objects = objs;
	}
	
	public ConcurrentHashMap getObjects()
	{
          return new ConcurrentHashMap(objects);		
	}
}

[/java]

It doesn’t look an efficient way of dealing with the data, is there any way I can remove the HashMap / concurrentHashMap step?
When I created my ObjData class I had to do this :
public class ObjData extends Serializer {

Should I create a wrapper for the CocnurrentHashMap extending the Serializer too?

Overall, am I on the correct path with this approach (threading, interpolation, objdata etc)?

thanks
Francesco

A more efficient way is only to send things as they change rather than resending everything all the time.

Yes, you are definitely right, but I was wondering how to deal with clients that connected at time Tx > T0 or that lost one or more updates.
Probably I could fix the first case by having a separate message that delivers the whole package upon connection and for the second one I might either assume that bad things may happen from time to time and nobody will die for that… or I could try to minimize the risk by sending the whole package at regular intervals maybe (5-10 secs or so)…

Anyways indeed something worth looking into, thanks.

Francesco

First, this is wrong:
public ObjMessage(HashMap<Integer,ObjData> objs) // would not accept a concurrentHashMap here

The comment is completely erroneous without additional information since it’s just plain not true. Something else must have been up.

Second, even if it were true you’d only have to cast it when returning… not create a whole new map.

We should get that basic Java-learning issues out of the way before we tackle more complicated subjects but perhaps some interesting links:
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization

I would caution that writing a real time networked game is nearly impossible for someone still learning Java and game programming. Might be worth concentrating on a single player game first.

Edit: Oh, wait… re: The first part… what was failing? The sending of the message? Yes, that will fail but you must be turning it into a regular hashmap outside of the message, too, then. And all of this copying makes ConcurrentHashMap useless anyway.

You may want to rethink sending maps in messages, anyway. It’s not really a good idea.

Thanks pspeed,
I tried to cast etc obviously first and the maps thing…well, it is just an experiment, I am trying to actually learn the SDK and related methodologies rather than building a multiplayer game.
I apologize if my questions are inappropriate, will try to be more careful about that.

I did some java but I am more comfortable with languages where I can play with bits and bytes, access directly the memory and deal with simple byte streams.

Creating a new map was a (non efficient) way to workaround the failure of sending the msg, a temporary one until I found a better structure.
Was just wondering why it would fail so to avoid the same issue with some other data structure.

Which data structures would you use? Arrays? Lists?
I would use arrays if the plan was to transfer data from the message into my in memory structure, element by element (which is probably going to happen in the end), but it was not the case in the first experiment.

I have a background in 3d algorithms (created a cinematic / dynamic CAD system -no libraries used, all from scratch- when I was attending the university, ages ago, about 30K lines of Pascal and Assembly running on a 80286 with 2Megs, yes MEGS of ram), never programmed games tho.

Will definitely read the links you suggested, thanks

Regards,
Francesco

A map in the message doesn’t make much sense since you will never be really accessing it randomly. Send an array of change events or something… one per change… and then just iterate over them once when received.

And since you were creating a new hashmap for every caller that asked there was no reason to make it a concurrent map because it’s likely never shared. But anyway, it’s the wrong approach.