SiO2 EventBus Events

Sorry for maybe asking a dumb question but I’m tracking a few very complicated day-job things at the moment… but can you go into why that reverse lookup is needed?

Thanks. It seemed the best way for a few dozen reasons, as I recall. In the worst cases, it’s no worse than any other approach and in the best cases it is certainly way better.

I need to get hold of all spawned buff entities in the cases where the bonus gets increased/decreased to update and of course when the spell gets removed to remove the buffs.

But this could also have been done the other way… if the buffs know where they came from and/or had a link to where they came from.

You could have an entity set that contained entities with a buff component and a “spell link” component and then update the buffs as needed. That way you’d only do the extra work for buffs that required live updates from their parent or whatever. Regular buffs could still be simple (as you have them now).

I have one more “event”-related example here, if you like.
Many tasks imply regularity at some fixed time period: weapon recharge, “probe object” spawning for collision avoidance, stats refreshing for AI to make appropriate decisions etc. The way I implemented it now is “general counter” entity/component (rate, current percentage, enabled/disabled flag).

Pros:

  • probably easiest/most straightforward to implement. Incrementing system just takes all counters and adds tpf weighted by the rate (if counter is enabled) to update percentage

  • flexibility. I can create as many counters for particular entity as needed by holding just references to them, like

private EntityId chargerId;
private EntityId refresherId;
etc
  • processing uniformity. Wherever I need to check if it’s time to do something, I’m calling something like
Counter charger = ed.getComponent(chargerId, Counter.class);
if (charger.getPercentage() >= 1) ...

Cons:

  • backside of the processing simplicity: it is all bound to single thread. When I have systems processed one by one, I can painlessly have (after I just fired)
Counter newcnt = charger.changePercentage(0);

It is convenient, short and readable to do right where and when it is needed, but obviously I can’t do that if I have incrementing system in another thread. This is serious limitation, and this basically violates “one component - one system that changes it” principle. But, if I want to process this call in that same incrementing system - ok, then we have a “fire event” here, that the system has to be “notified of” somehow.

  • Questionable (since I believe Paul did tremendous optimizations everywhere), but still: massive setComponent() calls. I don’t really always need all the intermediate values outside of incrementing system, most of the time I just need to distinguish between “charged already” / “not yet”, so it might be, that number of calls could be reduced if I track incrementing counters in some map internally, and just set components once percentage exceeded 1. But, again, this can well be unnecessary complication effectively yielding similar overall performance.

So how would you go about making it purer ES way?

Wait… so you are incrementing values with tpf every frame?!?

Yikes.

Why not just set a time to live once and then calculate percentage when you need it based on how much time has passed? One setComponent() per expiry versus one per frame. Much like the Decay component in many of my examples. (Some of which in my own code can also tell you how close they are to getting removed.)

Yep, point taken, but still that ttl has to be set by just one system per, say, “counter type”. So this means, if I want to reset, I can do it based solely on EntityId, which, since it is unique, will guarantee that no other system will ever touch that particular ttl, right?

Well, you just need to take hypothetical thing A and hook it up to hypothetical thing B using the hypothetical process. No problem.

If it’s something I now think it is, it is really nice thing. But I have to try first :slight_smile:

Update: so, now it looks working. Actually what I did is somewhat opposite to the Paul’s Decay solution: my component is set when anything happens (i.e. “fire event”) and then exists until its own reset (next same type of event) or until the entity it belongs to is removed. What amazes me (besides I got rid of incrementing system - thanks to Paul, for that reminder about using system time) is that since entity is created - its EntityId is known to the entity that uses it, and so its component is always what you want to check/set, and so the entity is isolated from any other system (possibly working with the same “type” of entity, counter in this case) just by that EntityId. Some immanent split I didn’t recognize before. And yeah, this clears out any worries about threading, since there’s no common system anymore.

Well the real case which I am about to implement is currently really:
I have multiple systems (Explosions, Weapons, …). Those can hurt any entity by lowering the Health in the HealthComponent.

Now AI and probably also the “death system” and a “experience system” need to know who killed entity x.

The “Problems” with a simple component for that are:

  • Since any System could trigger that damage, this component is added at an arbitrary update loop position. This means relevant systems could already be finished. This implies that the component has to stay for two frames but I have to ensure that it isn’t processed two times.

  • It isn’t so easy/nicely hookable.

  • It’s rather polling than event driven, which means code has to run each frame in the update loop instead of asynchronous in another thread (Like: AI would have a constant 30 FPS Update Thread, regardless of the game FPS)

  • I need a clever way for when multiple damage is taken per round.

So the last one at least comes up with the question of: One Entity per Damage/Kill?

But I think I should just start coding instead of trying to overengineer things, it will probably work either way and a passable prototype is better than a sheet of paper with ideas^^

Edit: Another simple example would be a Combat Logging System.
It would have to be a full blown system which keeps track of Entities with DamageComponents, so once per damage, a new entity is created just to be thrown away a frame or two later.
I’ll try it that way, though.

A damge in your case is also an entity with a link to the thing it should apply the damage. I think that would simplify your design and logic I think.

One entity per damage is the only way, really. You will see.

Also, if the only way you ever delete entities is through a decay system that always runs last, then you have a lot of leeway in how you handle other things.

But I also wonder how you will resolve “who killed this entity?” when it was damage from 20 different things…

If I don’t decrease the Health in a system which handles those Damage Entities but immediately, then I know which Damage made it drop under zero. I could even make death a flag to the DamageComponent then (isDeadly).

But then I still have the problem that I need a TTL of 2 Frames, because system A might be run before whatever system did damage. But I will simply use the getAddedEntities, that way each damage entity is only processed once

So just randomly arbitrary. That’s not a feature, to me. “You were killed by an Ant because he just happened to have an earlier entity ID as compared to the Elephant that sat on you…”

Put the damage system first, decay system last. Or put the damage system after the decay system (which would otherwise be last).

There is almost always a way to order systems, especially if the other component update rules are followed. Cycles are rare.

The problem with “I’m just going to break this rule here once in the interest of time” is then it has follow-on effects that end up messing up everything. I have never had a single case where I’ve done that and didn’t regret it later. Not once. And I make that mistake at least once per project (average of five times, I guess… including weighting of 12-15 in earlier prototypes). Every single time, I ended up with less code in the end and a giant hand shaped mark on my forehead.

If I use getComponent() and setComponent() to have live data, I can actually tell who did the last hit. That has to be done in the method which sets the HealthComponent though, not afterwards somewhere.
Or would you know a better way?

The problem is that there are many ways which could inflict damage like physics (fall damage), Weapons, Explosions (I seperated them from weapons), Buffs. The problem isn’t to order the decay system but for example the audio system and the experience sytem would have to be after the damagers (so the damage systems (physics, wep, expl, buff) would have to be before anything else).

I don’t know if that doesn’t lead to problems later, but I guess only time will tell

Do you accumulate all damage or do you set each damage applied instantly?

All damaging occurences, irregardless of source, should create a HealthChange entity. A system then accumulates all health changes and applies them to the given entity they are meant for. This results in one more more deaths: The health system creates a Dead component on the entity.

So, an order of systems could be:

  1. Physics (creates a HealthChange entity)
  2. Weapons (if we’re talking insta-hit weapons, they create HealthChange entity)
  3. Explosions (creates HealthChange entity)
  4. Buffs (creates HealthChange entity)
  5. Health (accumulates all HealthChange entities and may create Dead component (setting some feature - perhaps killed in percentages or just the last HealthChange component to decrease below 0: KilledBy))
  6. Death (processes Dead component, perhaps granting the KilledBy-entity some points for killing another entity, then sets the Decay component with TTL 0)
  7. Decay (processes all entities with Decay component and removes them)

right?

Remember, this is all happening in the same time frame, so there’s no order of components. The processing of damage components are arbitrary and you, as designer, must choose a system for it.

But then the order of those HealthChange Components are arbitrary. However I now understand Pauls hint:

What I cant fix is that (taking your order) weapons would set the killedby before the Explosions so when an Explosion and a weapon are deadly in the same Frame, you have to find an order.

This also applies to my method of set/get components which isnt recommended however at least i know the order of damage properly

As said I would have all those damage in separated entities and have link component to the receiver of that damage. Furthermore in your case I would simply have lastHit component which point always to the last hitman. To accumulate it in one healt component and stuff everything in there sounds awkward to me.

Weapons would not set the KilledBy - they just create some Damage…

The accumulated damage change may end up in a KilledBy value

Again, if you want a system for it - you have to accumulate all hits first, then choose (as designer) the one who hit last. Because all the components are processed in the same time frame - so there’s no inherent order (first/last).

Okay Yes, but weapons would always be first in processing, wherever that might be.

I mean reducing getComponent calls improves Networking Performance but i dont actually need that yet.