im trying to build a Gamplay Ability System using Entities and Netode.
in some cases the player can cast a spell on a ghost by entity reference, it could be a simple AI or an opposing player, but the big problem here is that the entity references are different for every player.
so is there a way to identify and access a ghost by some unique id across the network?
[EDIT]
i just found out about GhostComponent.GhostId.
thanks @Jawsarn ,
but i am not trying to sync components or fields through server and clients,
I am trying to synchronize the ghost entities.
EG: Let’s say my ability system has an ability that should have a Player / NPC entity as a target. sending the entity will lead to a bad referral for the server and clients as it only exists in the current world.
Aha, so you’re sending the information over some other means than component serialization as RPC or Command stream I assume? Such case ghostID seems simplest. Good luck with the system, I’m working on a GAS as well^^
Thats interesting!
I’m still working on something else at the moment, but I’ll definitely implement it using DOTS very soon.
just out of curiosity, have you already a design for it’s implementation ?
how are you going to implement the ability and its Actions, Effects and Clues ?
Not really. This is my side project and I started out something similar of the DOTSSample, but did some research and are now trying to get a similar interface of how UE uses it, but with DOTS as backbone. Also very bruteforcy at first to get all components in there and then to optimize it more.
Abilities & Effects are entity prefabs. If you’re familiar with UE terms, I have Attributes and Tags as ScriptableObjects. Attributes are generated into code & systems. I have not arrived at Cues yet, so we’ll see about that. I guess it’s a bit hard to explain in detail of all the systems involved here ^^
You can stil use an Entity field for that. It is serialized across the network and resolved in each to the corresponding one in each world.
Underneath we use the ghost id for that. The only problem is that the reference is a weak one. If the client didn’t received the ghost yet (and so the ghost id is not present yet) it will result in a Entity.Null (and never resolved later).
For robust handling of a target for a spell/ability etc, using the ghost id and resolving it your self is probably fine. There are still some corner cases to handle (like that the entity may never spawn on the server for example, if the target entity is despawned before the client receive it) but at that point I suppose the server will send back an update target value for the ability.
Thank you very much!
how does the netcode systems resolve those entities by their ghostIds, are they stored in some collection ?
[edit]
using the entity as a direct weak reference will meet my needs as in most cases the target entities are Ai / Players spawned at the start of the game.
but i am really interested to see how do you sync an entity reference between server / clients worlds.
I just tried an example and ghost entities ids are not synchronized across servers and clients worlds, but the GhostComponent.ghostid is properly synced.
I tested it and only GhostComponent.ghostIds are synchronized, but Ghost entity indexes aren’t, which makes sense to me.
this is very interesting if the netcode package really syncs the ghost entity references, because I a not aware of any API in the Entities package that allows creating entities with a specific ID and how would you do if an entity index is already used on the server but required as predicted by the client.
also, can you please give any reference/comment on how the netcode package resolves ghostIds ? like are you storing them in some collection NativeHashMap<GhostId, Entity> ?
Ghost entity indexes cannot be synchronised. client and server have a completely different entities set etc. And it is not important either. What matter is being able to map from one to other.
We are using the GhostId to map in between server and client entity (and viceversa). On the server and client we have a map (see GhostSendSystem and GhostReceiveSystem) that map ghost-id to entities.
if u write something like
public struct MyComponent : IComponentData
{
[GhostField] public Entity ghostReference;
}
When the Entity ghostReference is serialised, internally this is translated into the corresponding ghost-id. When deserialized, the ghost-id is resolved and the matching entity (client or server world) is retrieved.
As you may notice there is a caveat (that we are going to resolve in near future): if the ghostReference Entity has not been received yet by the client, for example, at the moment of the the resolution, this will result in a Entity.Null (this is why we say this is a “weak” reference).
Of course, when a new snapshot is received and the entity is there, then is resolved to a existing instance.
@ yes, this is why! It is annoying but avoidable in two ways:
Interpolated entity somehow “partially” fix this because they resolve the Entity reference every time the component is updated. So if a ghost arrive, it will become valid.
But predicted ghost does not do the same for example (so inconsistent behaviour) and also there is no way to actually know if
Is null because is not spawned locally
Is null because is null on the server
At least being able to discern in between these two is the expected minimal improvement.