Beispiel #1
0
        private void PartitionEntity(Entity entity)
        {
            EntityPos pos = entity.LocalPos;

            int lgx       = (int)(pos.X / gridSizeInBlocks) % partitionsLength;
            int lgz       = (int)(pos.Z / gridSizeInBlocks) % partitionsLength;
            int gridIndex = lgz * partitionsLength + lgx;

            long nowInChunkIndex3d = MapUtil.Index3dL((int)pos.X / chunkSize, (int)pos.Y / chunkSize, (int)pos.Z / chunkSize, chunkMapSizeX, chunkMapSizeZ);

            EntityPartitionChunk partition = null;

            if (!Partitions.TryGetValue(nowInChunkIndex3d, out partition))
            {
                Partitions[nowInChunkIndex3d] = partition = new EntityPartitionChunk();
            }

            bool didChange = false;

            GridAndChunkIndex indexes;

            if (GridIndexByEntityId.TryGetValue(entity.EntityId, out indexes))
            {
                if (indexes.ChunkIndex != nowInChunkIndex3d)
                {
                    EntityPartitionChunk oldpartition = null;
                    Partitions.TryGetValue(indexes.ChunkIndex, out oldpartition);
                    oldpartition.Entities[indexes.GridIndex].Remove(entity);
                    didChange = true;
                }
                if (indexes.GridIndex != gridIndex)
                {
                    partition.Entities[indexes.GridIndex].Remove(entity);
                    didChange = true;
                }
            }
            else
            {
                didChange = true;
            }


            if (didChange && gridIndex >= 0) // Grindindex can be negative when a entity is outside world bounds
            {
                partition.Entities[gridIndex].Add(entity);
                GridIndexByEntityId[entity.EntityId] = new GridAndChunkIndex(lgz * partitionsLength + lgx, nowInChunkIndex3d);
            }
        }
Beispiel #2
0
        private void PartitionEntity(Entity entity)
        {
            EntityPos pos = entity.SidedPos;

            int lgx       = ((int)pos.X / gridSizeInBlocks) % partitionsLength;
            int lgz       = ((int)pos.Z / gridSizeInBlocks) % partitionsLength;
            int gridIndex = lgz * partitionsLength + lgx;

            long nowInChunkIndex3d = MapUtil.Index3dL((int)pos.X / chunkSize, (int)pos.Y / chunkSize, (int)pos.Z / chunkSize, chunkMapSizeX, chunkMapSizeZ);

            EntityPartitionChunk partition;

            if (!Partitions.TryGetValue(nowInChunkIndex3d, out partition))
            {
                Partitions[nowInChunkIndex3d] = partition = new EntityPartitionChunk();
            }

            partition.Entities[gridIndex].Add(entity);
        }
Beispiel #3
0
        private void Event_OnEntityDespawn(Entity entity, EntityDespawnReason reason)
        {
            GridAndChunkIndex indexes;

            if (GridIndexByEntityId.TryGetValue(entity.EntityId, out indexes))
            {
                GridIndexByEntityId.Remove(entity.EntityId);
            }
            else
            {
                return;
            }

            EntityPartitionChunk partition = null;

            if (Partitions.TryGetValue(indexes.ChunkIndex, out partition))
            {
                partition.Entities[indexes.GridIndex].Remove(entity);
            }
        }
        /// <summary>
        /// Same as <see cref="WalkEntities(Vec3d, double, API.Common.Action{Entity})"/> but does no exact radius distance check, walks all entities that it finds in the grid
        /// </summary>
        /// <param name="centerPos"></param>
        /// <param name="radius"></param>
        /// <param name="callback"></param>
        public void WalkEntityPartitions(Vec3d centerPos, double radius, API.Common.Action <Entity> callback)
        {
            int mingx = (int)((centerPos.X - radius) / gridSizeInBlocks);
            int maxgx = (int)((centerPos.X + radius) / gridSizeInBlocks);

            /*int mingy = (int)((centerPos.Y - radius) / gridSizeInBlocks);
            *  int maxgy = (int)((centerPos.Y + radius) / gridSizeInBlocks);*/

            int mincy = (int)((centerPos.Y - radius) / chunkSize);
            int maxcy = (int)((centerPos.Y + radius) / chunkSize);

            int mingz = (int)((centerPos.Z - radius) / gridSizeInBlocks);
            int maxgz = (int)((centerPos.Z + radius) / gridSizeInBlocks);


            int                  cxBefore = -99, cyBefore = -99, czBefore = -99;
            IWorldChunk          chunk          = null;
            EntityPartitionChunk partitionChunk = null;

            int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks;
            //int gridYMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks;
            int gridZMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks;

            int cyTop = api.World.BlockAccessor.MapSizeY / chunkSize;

            for (int gridX = mingx; gridX <= maxgx; gridX++)
            {
                for (int cy = mincy; cy <= maxcy; cy++)
                //for (int gridY = mingy; gridY <= maxgy; gridY++)
                {
                    for (int gridZ = mingz; gridZ <= maxgz; gridZ++)
                    {
                        if (gridX < 0 || cy < 0 || gridZ < 0 || gridX >= gridXMax || cy >= cyTop || gridZ >= gridZMax)
                        {
                            continue;
                        }

                        int cx = gridX * gridSizeInBlocks / chunkSize;
                        //int cy = gridY * gridSizeInBlocks / chunkSize;
                        int cz = gridZ * gridSizeInBlocks / chunkSize;

                        long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ);

                        if (cx != cxBefore || cy != cyBefore || cz != czBefore)
                        {
                            chunk = api.World.BlockAccessor.GetChunk(cx, cy, cz);
                            Partitions.TryGetValue(index3d, out partitionChunk);
                        }
                        if (chunk == null || chunk.Entities == null || partitionChunk == null)
                        {
                            continue;
                        }

                        cxBefore = cx;
                        cyBefore = cy;
                        czBefore = cz;

                        int lgx = gridX % partitionsLength;
                        int lgz = gridZ % partitionsLength;

                        List <Entity> entities = partitionChunk.Entities[lgz * partitionsLength + lgx];
                        for (int i = 0; i < entities.Count; i++)
                        {
                            callback(entities[i]);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// This performs a entity search inside a spacially partioned search grid thats refreshed every 16ms.
        /// This can be a lot faster for when there are thousands of entities on a small space. It is used by EntityBehaviorRepulseAgents to improve performance, because otherwise when spawning 1000 creatures nearby, it has to do 1000x1000 = 1mil search operations every frame
        /// A small search grid allows us to ignore most of those during the search.
        /// </summary>
        /// <param name="centerPos"></param>
        /// <param name="radius"></param>
        /// <param name="callback">Return false to stop the walk</param>
        public void WalkEntities(Vec3d centerPos, double radius, ActionConsumable <Entity> callback)
        {
            int mingx = (int)((centerPos.X - radius) / gridSizeInBlocks);
            int maxgx = (int)((centerPos.X + radius) / gridSizeInBlocks);
            //int mingy = (int)((centerPos.Y - radius) / gridSizeInBlocks);
            //int maxgy = (int)((centerPos.Y + radius) / gridSizeInBlocks);

            int mincy = (int)((centerPos.Y - radius) / chunkSize);
            int maxcy = (int)((centerPos.Y + radius) / chunkSize);

            int mingz = (int)((centerPos.Z - radius) / gridSizeInBlocks);
            int maxgz = (int)((centerPos.Z + radius) / gridSizeInBlocks);

            double radiusSq = radius * radius;

            long                 indexBefore    = -1;
            IWorldChunk          chunk          = null;
            EntityPartitionChunk partitionChunk = null;

            int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks;
            //int gridYMax = api.World.BlockAccessor.MapSizeY / gridSizeInBlocks;
            int gridZMax = api.World.BlockAccessor.MapSizeZ / gridSizeInBlocks;

            int cyTop = api.World.BlockAccessor.MapSizeY / chunkSize;

            for (int gridX = mingx; gridX <= maxgx; gridX++)
            {
                //for (int gridY = mingy; gridY <= maxgy; gridY++)
                for (int cy = mincy; cy <= maxcy; cy++)
                {
                    for (int gridZ = mingz; gridZ <= maxgz; gridZ++)
                    {
                        if (gridX < 0 || cy < 0 || gridZ < 0 || gridX >= gridXMax || cy >= cyTop || gridZ >= gridZMax)
                        {
                            continue;
                        }

                        int cx = gridX * gridSizeInBlocks / chunkSize;
                        //int cy = gridY * gridSizeInBlocks / chunkSize;
                        int cz = gridZ * gridSizeInBlocks / chunkSize;

                        long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ);

                        if (index3d != indexBefore)
                        {
                            chunk = api.World.BlockAccessor.GetChunk(cx, cy, cz);
                            Partitions.TryGetValue(index3d, out partitionChunk);
                            indexBefore = index3d;
                        }

                        if (chunk == null || chunk.Entities == null || partitionChunk == null)
                        {
                            continue;
                        }

                        int lgx = gridX % partitionsLength;
                        int lgz = gridZ % partitionsLength;

                        List <Entity> entities = partitionChunk.Entities[lgz * partitionsLength + lgx];

                        for (int i = 0; i < entities.Count; i++)
                        {
                            double distSq = entities[i].SidedPos.SquareDistanceTo(centerPos);
                            if (distSq <= radiusSq && !callback(entities[i]))
                            {
                                return;
                            }
                        }
                    }
                }
            }
        }
        private void WalkEntities(Vec3d centerPos, double radius, ActionConsumable <Entity> callback, RangeTestDelegate onRangeTest)
        {
            int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks - 1;
            int cyTop    = api.World.BlockAccessor.MapSizeY / chunkSize - 1;
            int gridZMax = api.World.BlockAccessor.MapSizeZ / gridSizeInBlocks - 1;

            int mingx = (int)GameMath.Clamp((centerPos.X - radius) / gridSizeInBlocks, 0, gridXMax);
            int maxgx = (int)GameMath.Clamp((centerPos.X + radius) / gridSizeInBlocks, 0, gridXMax);

            int mincy = (int)GameMath.Clamp((centerPos.Y - radius) / chunkSize, 0, cyTop);
            int maxcy = (int)GameMath.Clamp((centerPos.Y + radius) / chunkSize, 0, cyTop);

            int mingz = (int)GameMath.Clamp((centerPos.Z - radius) / gridSizeInBlocks, 0, gridZMax);
            int maxgz = (int)GameMath.Clamp((centerPos.Z + radius) / gridSizeInBlocks, 0, gridZMax);

            double radiusSq = radius * radius;

            long                 indexBefore    = -1;
            IWorldChunk          chunk          = null;
            EntityPartitionChunk partitionChunk = null;

            for (int gridX = mingx; gridX <= maxgx; gridX++)
            {
                int cx  = gridX * gridSizeInBlocks / chunkSize;
                int lgx = gridX % partitionsLength;

                for (int gridZ = mingz; gridZ <= maxgz; gridZ++)
                {
                    int cz  = gridZ * gridSizeInBlocks / chunkSize;
                    int lgz = gridZ % partitionsLength;
                    lgz = lgz * partitionsLength + lgx;

                    for (int cy = mincy; cy <= maxcy; cy++)
                    {
                        long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ);

                        if (index3d != indexBefore)
                        {
                            indexBefore = index3d;
                            chunk       = api.World.BlockAccessor.GetChunk(cx, cy, cz);
                            if (chunk == null || chunk.Entities == null)
                            {
                                continue;
                            }
                            Partitions.TryGetValue(index3d, out partitionChunk);
                        }
                        else if (chunk == null || chunk.Entities == null)
                        {
                            continue;
                        }

                        if (partitionChunk == null)
                        {
                            continue;
                        }

                        List <Entity> entities = partitionChunk.Entities[lgz];

                        for (int i = 0; i < entities.Count; i++)
                        {
                            if (onRangeTest(entities[i], centerPos, radiusSq) && !callback(entities[i]))
                            {
                                return;
                            }
                        }
                    }
                }
            }
        }