Network, ECS and Design questions


#1

Good whatever-time-of-day jMonkeys,

my project is in quite a mess currently and tired of tidying it up i decided to move on to some other parts for a couple of days. Thus i started to make some thoughts about networking, ecs, etc.

I have plans to use SimEthereal and Zay-ES and already have looked into the examples some time ago like sim-eth-es which i do understand (always feels like christmas and birthday together when playing around with new libraries)
Now trying to fit my game into the ECS style some questions popped up in my head:

question 1: a models animations are actually view only and not involved in what the server should care about, so how should the client determine the current animations to play? for the model that a specific client controls they could do it locally but how would i go about knowing which animations to play for other players that are in my view? is it reasonable to try to guess animations from known locations and calculate speed etc to decide weather to play walking or running animation? that would be quite limited though or should i add some sort of animation component? or should i seperate it from the ECS yet let it be controlled by the server by say rmi calls or the like?

question 2:i can imagine a HealthSystem, that is interested in all entities that have a health component for example when the healthComponent consists of currentHealth, maxHealth and regenerationPerSecond. So the HealthSystem can continuously regenerate an entities health.
now say i have several sources of damage (so i decide to make Damage an entity) which might have a component like Target that points to the entity that has to take the damage.
How would i do that efficiently? aside from the entitySet watching entities with Health components for the health regeneration, when i have another entitySet watching for entities with DamageTagComponent and TargetComponent, what should that TargetComponent look like? should it be an EntityId so i have to query the entityData for each entityId i find in the changed set to get its health component and deal the damage?

question 3: about threads in networking: for each connection (ie for each channel) on the client one thread is created for reading and one for writing, the clients send() method is threadsafe and message listeners can never be called in parallel even when a message is received via 2 channels simultaneously, thats what i observed, so is that correct?
im wondering about the lines

        // Make sure client MessageListeners are called single-threaded
        // since it could receive messages from the TCP and UDP
        // thread simultaneously.
        synchronized (this) {
            messageListeners.messageReceived(this, m);
        }

in DefaultClient.java on line 498, does it potentially slow down simEthereal dependant on how i implement messageReceived() methods? like should i rather always immediately offload the work from that method? and are rmi calls coming in basically called inside this method too, so all my objects that are called via rmi need to offload their work, too?
On the server, there is 2 threads per channel, regardless of how many clients connect? how does it behave when one client has super low bandwidth, does it stall the thread / channel? i read that there is an internal buffer and when its full it does either block or return some constant, how would i notice that when i try to send some data to a speficic client and that client has low bandwidth but i dont want to spend more than say 1ms to send data to that client so other clients wont have to wait for their messages?
EDIT: i know ConnectorAdapter has a OUTBOUND_BACKLOG and since it has a seperate writer thread per channel those wont interfere, but since i didnt see the thread count increasing on the server when starting a second client, i wonder how the server behanves

and last question: it seems you can register ComponentVisibility instances with the EntityDataWrapper to filter visibility based on some component. But imagine i have 3 more players in my team and several other players that are in enemy teams and i want an entitySet that watches entities with HealthComponent but i want to filter that, however not based on HealthComponent but based on TeamComponent, so i can only see health of those entities that are in my team?

As always, thanks a bunch in advance and many greetings from the shire,
samwise


#2

I’ll address ecs hp systems. The way I have always done it is you have your health component with cur and Max health, and a damage component with targetid and damage. You health system tracks 2 sets, all health’s and all damages. Every frame you group all new damages, add them to a map of targets in case a Target gets hit more than once a frame, and finally find the health that is associated with the target I’d and subtract the damage from the map. Health Regen is it’s own component and system and should apply negative damage periodically.


#3

You can see one approach here:


…which is also compatible with networking because I pulled it out of SpaceBugs originally which is a networked game.

It’s going with the “animation is reactive” approach.

If you are already keeping a set of all entities with health, why would you have to query the ES?
As QSDragon says, this is all in one system, basically.

That’s the way I’d do it… one “Health System” that keeps a set of all things with health, another set of all damage applying entities, maybe another set of all regen applying entities, etc… essentially, it is the only system that modifies the health component. (Because as a general rule, two systems should not be modifying the health component of the same entity.)

The interesting thing about this approach is if you want ongoing health drain then that’s just a damage entity with a long decay. Also, regen is just ‘negative damage’ in a sense. It’s all the same in the end. Every ‘frame’, you apply all of the health adjustments to the health components. If it were me, I’d probably go through the adjustments once and accumulate the +/- into some kind of per entity accumulators and then make a pass through that to do the final adjustment to health. It avoids lots of churn when entity X takes 5 damage, 5 damage, 5 undamage, 1 damage, 1 undamage, 5 undamage, etc… you just get the final score and apply the adjustment if not zero.

Yes, you should always seek to not do much work in a network message handler… but then doing a lot of work in a network message handler is a sign of a potential design problem anyway… the client should be ‘receive state -> apply state’ not ‘receive command -> do lots of work’. It’s a sign that the server is offloading work to clients.

This is from memory, but the channel threads read the data and push it onto queues. Those threads are only responsible for managing the socket selector. Messages aren’t handled on those threads.

That seems weird. But I suppose the dispatching of messages is probably handled by a fixed set of threads. I don’t have time to reboot my brain with the code right now… but that’s sort of what I remember.

Bogging down a message listener will not prevent other messages from coming in but they might prevent those messages from getting handled. Your not going to block the sockets, though.

…that’s not what ComponentVisibility is for. If you want “all entities with a HealthComponent and a specific TeamComponent(x)” the regular entity sets give you that filtering already.

ComponentVisibility is a HARD filter on a particular component based on data external to that component… like zone. It’s designed to handle the sim-ethereal case where we don’t want clients to see every BodyPosition in the world, only those around them… but the only way to efficiently know “those around them” is to ask the zone manager. Otherwise, every client would see all 5 million BodyPositions in the world even if they were only going to display the 10 around them.

“Just show me the 3 guys health on my team” has way simpler solutions.


#4

Holy moly thank you both for your answers!

@QSDragon that makes perfect sence and when i read that last sentence i was like ‘wow thats smart’ because i was wondering already whether i would need an additional DamageSystem and whether this system and the HealthSystem would both have to work on the same components basically which would be bad design but thinking of healthRegen as negative Damage eliminates that need

@pspeed sorry i missed that demo, ill have a look at it and make sure i get it, thanks for the link
and i guess ill go with that approach, have 1 set for all entities with healthComponents and a set with all entities considered Damage while thinking of HealthRegen as negative Damage, accumulate it each frame and apply that. In case one day i need to explicitly distinguish between Damage and HealthRegen i can still divide it

About the messageReceived thing: allrightie thats basically what i wanted to hear, didnt yet have a specific computationally expensive operation in mind just wanted to make sure its somewhat critical for performance so i remember it when i write methods called from there

and about the componentVisibility: i guess i can just query the entityData for all entities with Team and Health components and filter them based on the teamComponents teamID or something. but the reason i was wondering is: if someone decompiles the game, changes the source to query for all entities with Team and Health components and NOT filter them to print the enemies health and base their focus on the enemy with lowest health for example, how could i prevent that, i thought thats exactly what componentVisibility is for compared to componentFilter?


#5

Well, yeah, I guess if health is a super-secret private thing then you can do it the more complicated way.


#6

well health was probably not a good example, i just wanted to make sure when i want to reduce traffic or calculations etc, i can use ComponentFilters and when i want final control over which components are visible for which client then i use ComponentVisibility, so thats good to know, thanks

Now that i started with some network related implementations i came across the chunks synchronization problem. well problem is the wrong word since light calculations etc are totally deterministic meaning when the client gets the newly placed block, they can update their world to the same state as the servers state themselves.
However, to speedup block placements, can SimEthereal be used for other things than positions / rotations? like could i have 1 shared object per chunk that would have the position of the chunk and as rotation no real rotation but some last-block-update-bits?
since my chunks are 32x32x32 blocks, 15 bits are needed for the blocks position and 32 bits for the block, could i synchronize these 47 bits using SimEthereal or is there some sideeffects?

well the basic question is how to synchonize the block updates the fastest way (since clients still need to mesh them so id like to speedup both parts), but using simEthereal to synchronize position based data other than rotation would be handy


#7

No, SimEthereal is not designed for this. I’m not sure why it’s really needed anyway. Block placement is never ever going to be a “twitch” thing.

For the player edits, make them locally at the same time you send them to the server. For the player they will feel instant. Everything else comes from the server.

Side story: in Mythruna the server decides if a block change is real or not. So sometimes you will make a change locally and the server will immediately revert it for whatever reason. An interesting side effect of this is that I was able to add a “nerf” command for griefers that reverts their changes and then doesn’t tell them about it. It was so funny watching this one guy jumping around thinking he was destroying the building I was making while I was happily continuing to build it.

(The guys who would just walk around the world to find the coolest looking stuff and turn it into swiss cheese… one step away from being a sociopath, I think.)


#8

thats awesome, i’ll do it the same way, thanks :smiley:


#9

well, so i’ve some across 2 more questions. obviously i did not yet get the ECS idea fully and am still stuck in OOP designs.

question 1: a player can select one of 4 characters before they join the game. all those characters have 3 abilities. most characters have “active abilities” that is, they need to be actively triggered by pressing a button. however one character has a “passive ability” that grants that character a buff during the whole game.
now when i want to design the HUD, i want that button on screen for the passive ability to not be clickable, while usually all characters abilities are clickable.

solution idea 1: each player has a CharacterType component similar to the ObjectType in the sim-eth-es example. on the clientside i could then check that component and depending on which type add an individual CharacterAAbilityPanel or AnotherCharacterAbilityPanel or whatever, explicitly designed for that character. with that characterType i could also lookup the actual type of the ability and query for that specific ability to check its cooldown etc. but i would have to split code in that i would need separate gui panels for each character

solution idea 2: have one Abilities component, that holds 6 fields, int ability1ID, int ability2ID, int ability3ID, long lastUsedAbility1, long lastUsedAbility2, lastUsedAbility3. the client would query for that component, could use the abilityIDs to check which icon to display or wether that ability is activatable and also have the cooldown directly in this component (optionally i could split it up in Ability1, Ability2 and Ability3 components that would hold 1 int for the id and one float for the cooldown)

is any of those 2 reasonable or are there more ECS styled ways?

question 2: about the damageSystem, i currently have a DamageType component, similiar to the ObjectType also, to distinguish between fallDamage / magicalDamage / physicalDamage etc.
I can totally see how to set those damages and sum them up and apply them. however, my characters can have shield in addition to their health while mobs for example dont. would i need 1 entitySet for all entities with Health and then query the EntityData each time to see if it also has a shieldComponent? because when i have one set with all entities with health and another set with all entities with health and shield, then the set with the health entities also contains entities that have shield. so i would then have to check the health and shield set first, if i cannot find the target entity in it i have to search the health set?

thanks a lot for any input in advance i appreciate it a lot


#10

From what I can see from your description I think these abilities are entities themselves, sort of like weapons. Then you could simply have components for active/passive and cooldown on the ability entities themselves. Client will just look for entities that are abilities and have the player entity as their parent/owner (which is another component)

Alternatively you could store your character info in a serialized fashion and you would store the key to your character info, similar to how you store the key to spatial assets. The character info would have how to render the ability ui.

For damage, just query the set that is garunteed to have your info. When you’re going through and accumulating all the damage for the entity then at some point you’ll have to check if it has a shield. This depends on how exactly shields effect incoming damage but yes you will be viewing the same entity multiple times, you’ll of course attempt to keep the total number of times you view a single entity as low as possible but it’s perfectly fine to do so.


#11

doh, yes, modeling abilities as entities makes perfet sence. i’ll follow that approach, thanks
and thanks also for telling me my damage system approach is valid, I’ll see how far i can get but maybe i’ll come back with another question in a couple of days… :smiley: