Skip to content

Network Play

Jon edited this page Jan 8, 2018 · 7 revisions

This is mostly hypothetical, some experiments have been done many months ago before getting back burnered.

This is how I see it working:

Some Rules/Goals:

  • Don't trust the client
  • Don't send the client more information than what the player should legally have access to (harder than it sounds)
  • Don't send information that the client can figure out by processing on it's own end. (we don't all have cheap unlimited T1 pipes)

Previous Attempt:

There is experimental code in https://github.com/Pulsar4xDevs/Pulsar4x/tree/ECS-Comms However this is very old code before the move to eto.forms. much of the generic network code had been done in that branch though, some of which could be re-used when the issue is re-visited.

The idea was a player would log onto the server using the faction ID and password, the client would then request info that it did not have, i.e. starsystem info required to calculate orbits for known starsystems other known intel etc. the major problem with this is that ecs's datastructure is not really set up for this, an extremely simple example is the NameDB. many entities have a NameDB, lets take a random planet. one faction names it "Dirt" another names it "Terra" another names it "EnemyHome". The NameDB for the planet entity stores all these and will return a name for a given faction. Sending the whole NameDB for this planet to the client breaks the second and third rules.
It would allow a client to display the names of other factions to the player, it would offer some tidbits to teh player if s/he were able to get that info from the client data, the player would know that their homeworld had been stumbled across by a stealthy ship from another faction, without ever knowing the faction had entered the starsystem for example.
While this may not seem like a huge intel win for the given player in this simple case, other cases could provide a massive boon.

To solve this problem, data would need to be sanitized before being sent to the client. figuring how to do this without writing a ton of boilerplate code could be a problem.

Current thoughts on future implementation:

As of this writing we're attempting to implement a message pump. With this setup, the separation between the ecslib and the viewmodels make a good place to insert network code. the client (Viewmodel & Views) would connect to the ecslib, and pull updates via the messagepump. they would send messages too the messagepump to make changes to the faction. We'd probably look copying some of the ecslib processors over to the client so that it can update itself instead of requesting information each tick. (i.e. orbits) The previously mentioned problem with data needing to be sanitized will be solved by ensuring datablobs are written so that they only contain data that a faction can legally know. (ie nameDB will no longer contain the names other factions have given the entites)

Current Work:

In the MajorRefactor branch I began implementing the code from the old ECS-Comms branch back in.

Some features added:

IGetValueHash interface

this interface is used instead of overriding GetHash(). it's currently implemented on ProtoEntity and some datablobs. it's designed as a hierarchical data compare, ie, Faction -> StarSystem -> Ship/ColonyEntitys -> Datablobs. possibly/probibly I'll only send datablobs with this interface across the network. basically the idea of this is:

  • Get a hash of the data the client has
  • Request a hash from the server.
  • Compare the two.
  • If it doesn't match, compare hashes of objects down the tree.
  • Once we found the smallest item (ie, a datablob) where the hashes don't match, we request that data from the server.

This will allow a client to save local data, then update on connecting to a server later, without having to download everything. Problems: Need to organize the datastructure a bit more hierarchical. Need to change how ownership works, currently there is not a particularly efficient way to get everything that a faction owns from a manager. How the client should go about getting data:

  • Connect to the server
  • Connect/login to the faction.
  • Request hash of faction.
  • Request hash of known systems/managers
  • Request Hash of Owned Entities in those managers.
  • Request Hash of datablobs in those entities.
  • Request Data it doesn't have.

Other stuff that needs working out/streamlining: Ownership need to be set when creating an entity. Changes to Owned entities need to be sent to the client. Though maybe not all - for example the client could create a new ship locally when one is finished being constructed, as long as the guid were given.

Anything random should be sent by the server - IE the client shouldn't be able to game it by advancing in time ahead of the server to see how a dice roll comes out.

Other notes: I had the idea that for generated systems, we could just send the seed. this sounds like a great idea at first, until you realize that the player shouldn't know about most of that data, unless he's fully explored the system, thus breaking the second rule.

Fog of war makes networking hard. at the moment each faction has a copy of each actual entity that it knows about. although it's not a full copy of the entity (see Sensors and FogOfWar pages) it does have a copy of any movementDB (ie OrbitDB, TranslateDB etc). for ships this makes a lot of sense, you want to be able to see what a ship you've detected is doing, if it's orbiting, moving, etc, you want to see that on the clients map. the data you get may not be 100 percent correct either (currently we are getting an accurate picture but that may change). however for stars, planets moons, and most importantly comets and asteroids (because there are so many of them) which the player should fairly quickly be able to get accurate orbit data on, we've got a copy(or several depending on the number of factions that know about it) of an entity that's doing exactly the same thing, which will be putting an unneccisary load on the server (though clients are all good, since they have only the copy). so, TODO: figure out a way to have a reference of a datablob that does not get processed. We could maybe allow two entities to share a datablob, which would solve the problem, but could introduce some hard to find bugs ie. if we're processing the datablob by doing: manager.GetEntitesWithDatablob<OrbitDB>() then processing each of those entites, then we'll be processing the referenced datablob multiple times. for orbits this (probilby, from memory) wont cause problems since position is worked out from the epoch, however it's still processing it multiple times defeating the whole purpose. other datablobs where it's adding to it's current state will cause problems, which would likely be hard to debug. If the processor uses Manager.GetAllDatablobsOfType<OrbitDB>() then most of these problems go away, maybe we should consider removing GetAllEntites ? this could still cause problems though, if we need access to other datablobs and we're doing:

list<OrbitDB> listOfOrbits = manager.GetAllDatablobsOfType<OrbitDB>();
foreach(var orbitDB in lostOfOrbits)
{
    Entity entity = orbitDB.OwningEntity();
    //this is where things break down, if we allow entites to share datablobs, the above code would need to return a list
}

Clone this wiki locally