How to separate entities by distance?

I’m making multiplayer prototype. It will have pretty large map for example 512x512 when player is of size 1x1. Each player will see a max of 16x16 so I want to send only regions on which he stands and additional regions next to his position. I previously was generating state for each player and radius from him. Currently I want to generate state for each chunk 16x16 and then send it to selected players.

Case:
Player count: 3000
Map size: 512x512

Previously I was generating state for each player so the worst and the best scenario was to always generate 3000 network states.

When serializing per chunk, bad case has total of 1024 states and best is 1 when all players are in a single chunk.

Solution I came up with is to use ISharedComponentData with int2 as a position.
Each player position is rounded to 0, 16, 32, 48, 64 etc. and if he moves out of current chunk his ISharedComponent changes.

Then when building states I’m using:
EntityManager.GetAllUniqueSharedComponentData<ChunkNetworkSharedPosition>(_cachedSharedComponentList); iterate over it and generate state entity for each value of shared component. Is this good usecase of ISharedComponentData or will it be to slow/I should rethink this system?

One possible options is, DynamicBuffers.
Each chunk is entity. Entities chunks hold players.
When players move to new chunks, you just update buffers accordingly.

You could also build multihashmaps, where key is chunk index and values are players.

I have not yet played with network yet but I guess my first way of doing would be to do a sphere cast on each player and populate a dynamic buffer of entity/distance on each entity. I don’t know about performance but I would batch these cast after the transform systems and use the buffer after for each system that need it.
Based on the movement speed of your entites you can perfomr this batched sphere cast in a wider interval that the actual simulation at least put it in the fixed update group.

@Antypodish How to code something like that? If I have entities with position component and depending on this component value I want to put it in the right dynamic buffer I don’t see how to solve something like that. I could iterate over all entities with position, do some calculation and then how to decide in which entity buffer it should go?
What I mean is: I got bunch of entities, I calculated that half of them should go to buffer A and the others to buffer B how to pass them to this buffers? I cannot do nested foreach. MultiHashMaps is a solution but is it a ECS solution?

@WAYNGames I want to avoid doing calculation per player. If 100 players is on chunk X I don’t need to calculate state/distance for each one of them, I can just calculate state once for this chunk and everybody on this chunk will recive this single state.

What I’m currently doing is:

  1. Job loops thru every entity with NetworkSync component and push serialization result for each object to NativeArray
  2. Now I have a NativeArray with every entity state(for example position).
  3. I have entities with NetworkChunk component containing informations about chunk position and chunk size.
  4. Now I want to push onto each NetworkChunk dynamic buffer coresponding object states by using previously serialized position.

I don’t know how to do 4th step. I could pass this native array to a job iterating over all chunks and in this job I would verify if object position is inside chunk borders but this approach will lead to iterating over NxM entities(for each chunk I would need to iterate over all serialized objects).

Again I’m not even a noob in network stuff but my naive thinking leads me to think I would

  1. have the systems I described in my previous post to define the scope/priority of entity to synchonize for a player (connection)
  2. then have a system to compute the sate of each entity put those in a nativehashmap
  3. lastly have a system that read for that hash map to send the updated state of the most imporatant entity in scope or all of them if it fit the packet size.

We could probaly have a system between 2 and 3 that does the delta compression maybe using some caching mechanisms to avoid doing it multiple time for each player.

Yes it is.
Look forum for multihashmap. Strictly saying NativeMultiHashmap. We got few threads about this subject.
Then of course look into boids example.
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/459cbce0a4f3759fa89a46afd595766164c38bbf/ECSSamples/Assets/Advanced/Boids/Scripts/BoidSystem.cs
EntityComponentSystemSamples/ECSSamples/Assets/Advanced/Boids/Scripts/BoidSystem.cs /

Mind, Boids example is using, IJobNativeMultiHashMapMergedSharedKeyIndices, and this is obsolete and removed with latest Entities packages. We have discussions on the replacement too.

There are multiple ways, in which you can define, in which chunk your player is.
If you have global position, you just do as you did with int2, use its hash as key in NativeHashMap.
Then use values as Entities of chunks.

Other way, is to know neighbors entities chunks of each chunk.
So if chunk got 4 neighbours and player move to the left for example, you grab left neighbour chunk entity.

Either way, once you got neighbour chunk entity, you can access DynamiBuffer of players in this chunk.

@Antypodish Thank you for you help. I manage to do this with hashmap and dynamic buffers - part of it. I manage to append buffers(with command buffer) when entity moves from one chunk to the other but I have a hard time with removing it from previous chunk(dynamic buffer). It seems like I will need to recreate dynamic buffer without removed entity. Is my thinking correct or there is an easier way of doing this?

You can “clear” value from buffer.
That will leave “hole” in it.

Then you can either store empty indexes in buffer, to fill with new values.
Or if you want to compress, defrag and resize the buffer, move indexes from the back of the buffer, to the holes. And resize buffer, to actual number of elements.

Alternatively, if you have all known entities / players in given chunk, you can just use

myBuffer.CopyFrom ( nativeArrayOfKNownPlayers ) ;

But CopyFrom will not work in job right?

Imo using shared component is not recommend because map size is 512 * 512 that means max unique shared components = 262144 and each archetype chunks waste maximum of 16kb + some internal data for archetype and chunks, so that’s 262144 * 16 = 4194304 kb or 4gb so that’s potentially 4 gb of memory wasted. I don’t think ecs is optimized for 262144 unique components. If you are rounding player position to intervals of 16 than you may use SCs but still there will be 16 * 16 = 256 different components and only 4mb will be wasted.

@UsmanMemon Thanks for answer. Im going in direction of stroing entities in dynamic buffers per network zone/chunk. About rounding position. Yes I’m rounding it to intervals of 16 and possibily in the future rounding 16 => 1, 32 =>2 and using short to waste less memory.

As for my previous question about CopyFrom in job. I had problem with something different(native array usage without read only) and missinterpreted it as a problem with dynamic buffer so CopyFrom is actually possible inside ForEach.

Just starting with ecs so sorry for dumb questions. Thread can be closed. Initial problem is solved using hashmaps.