Skip to content

Commit cdef803

Browse files
abcxffc86ec23b-fef1-4979-b2fa-b9adc351b8ccCopilot
authored
Fix hash grid (#189)
* fix: fix dup returns in hash grid * fix: de-dup camera ticks * No need for instanceof check * Update src/Physics/HashGrid.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 3d83b0e commit cdef803

3 files changed

Lines changed: 36 additions & 36 deletions

File tree

src/Entity/Misc/Mothership.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ClientInputs } from "../../Client";
2020
import { tps } from "../../config";
2121
import { Color, Tank, Stat, ColorsHexCode, ClientBound, TeamFlags } from "../../Const/Enums";
2222
import GameServer from "../../Game";
23-
import ArenaEntity, { ArenaState } from "../../Native/Arena";
23+
import { ArenaState } from "../../Native/Arena";
2424
import { CameraEntity } from "../../Native/Camera";
2525
import { AI, AIState, Inputs } from "../AI";
2626
import Live from "../Live";

src/Native/Manager.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,23 @@ export default class EntityManager {
179179
this.collisionManager.forEachCollisionPair(this.handleCollision);
180180

181181
for (let id = 0; id <= this.lastId; ++id) {
182-
const entity = this.inner[id] as ObjectEntity;
182+
const entity = this.inner[id];
183183

184184
// Alternatively, Entity.exists(entity), though this is probably faster.
185185
if (!entity || entity.hash === 0) continue;
186186

187-
if (entity.isPhysical) {
188-
entity.applyPhysics();
189-
}
190-
191-
if (!entity.isChild) {
187+
if (entity.cameraData) { // Cameras are ticked later
188+
continue;
189+
} else if (ObjectEntity.isObject(entity)) {
190+
if (entity.isPhysical) {
191+
entity.applyPhysics();
192+
}
193+
194+
if (!entity.isChild) {
195+
entity.tick(tick);
196+
}
197+
} else {
198+
// Not an object entity (arena)
192199
entity.tick(tick);
193200
}
194201
}

src/Physics/HashGrid.ts

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default class HashGrid implements CollisionManager {
4141
private hashMap: number[][] = [];
4242
private gameLeftX: number = 0;
4343
private gameTopY: number = 0
44-
private collisionPairsSeen = new Uint32Array(MAX_ENTITY_COUNT * MAX_ENTITY_COUNT / 32)
44+
private collisionPairsSeen = new Uint32Array(MAX_ENTITY_COUNT * (MAX_ENTITY_COUNT - 1) / 2 / 32)
4545

4646
public constructor(game: GameServer) {
4747
this.game = game;
@@ -50,7 +50,7 @@ export default class HashGrid implements CollisionManager {
5050
public preTick(tick: number): void {
5151
const widthInCells = (this.game.arena.width + (CELL_SIZE - 1)) >> CELL_SHIFT;
5252
const heightInCells = (this.game.arena.height + (CELL_SIZE - 1)) >> CELL_SHIFT;
53-
this.hashMul = widthInCells >> CELL_SHIFT;
53+
this.hashMul = widthInCells;
5454
this.hashMap = Array(widthInCells * heightInCells);
5555
this.queryIdMap.fill(0);
5656
this.lastQueryId = 0;
@@ -65,7 +65,7 @@ export default class HashGrid implements CollisionManager {
6565
}
6666

6767
public insert(entity: ObjectEntity) {
68-
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert entity outside of tick");
68+
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert() entity outside of tick");
6969
const { sides, size, width } = entity.physicsData.values;
7070
const { x, y } = entity.positionData.values;
7171
const isLine = sides === 2;
@@ -98,7 +98,7 @@ export default class HashGrid implements CollisionManager {
9898
halfWidth: number,
9999
halfHeight: number
100100
): PackedEntitySet {
101-
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert entity outside of tick");
101+
if (this.isLocked) throw new Error("HashGrid is locked! Cannot retrieve() entity outside of tick");
102102
const result = this.resultSet;
103103
result.clear();
104104

@@ -140,7 +140,7 @@ export default class HashGrid implements CollisionManager {
140140
halfHeight: number,
141141
predicate: (entity: ObjectEntity) => boolean
142142
): ObjectEntity | null {
143-
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert entity outside of tick");
143+
if (this.isLocked) throw new Error("HashGrid is locked! Cannot getFirstMatch() outside of tick");
144144

145145
const startX = (centerX - halfWidth - this.gameLeftX) >> CELL_SHIFT;
146146
const startY = (centerY - halfHeight - this.gameTopY) >> CELL_SHIFT;
@@ -180,9 +180,9 @@ export default class HashGrid implements CollisionManager {
180180

181181
// No longer used
182182
public retrieveEntitiesByEntity(entity: ObjectEntity): PackedEntitySet {
183-
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert entity outside of tick");
183+
if (this.isLocked) throw new Error("HashGrid is locked! Cannot retrieveEntitiesByEntity() outside of tick");
184184
const { sides, size, width } = entity.physicsData.values;
185-
const { x, y } = entity.positionData;
185+
const { x, y } = entity.positionData.values;
186186
const isLine = sides === 2;
187187
const halfWidth = isLine ? size / 2 : size;
188188
const halfHeight = isLine ? width / 2 : size;
@@ -192,7 +192,7 @@ export default class HashGrid implements CollisionManager {
192192
public forEachCollisionPair(
193193
callback: (entityA: ObjectEntity, entityB: ObjectEntity) => void
194194
): void {
195-
if (this.isLocked) throw new Error("HashGrid is locked! Cannot insert entity outside of tick");
195+
if (this.isLocked) throw new Error("HashGrid is locked! Cannot forEachCollisionPair() entity outside of tick");
196196

197197
const collisionsSeen = this.collisionPairsSeen;
198198
collisionsSeen.fill(0);
@@ -213,27 +213,20 @@ export default class HashGrid implements CollisionManager {
213213
const entityB = this.game.entities.inner[eidB] as ObjectEntity;
214214
if (!entityB || entityB.hash === 0) continue;
215215

216-
if (eidA < eidB) {
217-
// Prevent extra-cell duplicates
218-
const pairHash = (eidA << MAX_ENTITY_ID_BITS) | eidB;
219-
const pairHashIndex = pairHash >>> 5;
220-
const pairHashBit = 1 << (pairHash & 31);
221-
if ((collisionsSeen[pairHashIndex] & pairHashBit) !== 0) continue;
222-
collisionsSeen[pairHashIndex] |= pairHashBit;
223-
224-
// Ensure (x, y) -> x.id < y.id
225-
callback(entityA, entityB);
226-
} else {
227-
// Prevent extra-cell duplicates
228-
const pairHash = (eidB << MAX_ENTITY_ID_BITS) | eidA;
229-
const pairHashIndex = pairHash >>> 5;
230-
const pairHashBit = 1 << (pairHash & 31);
231-
if ((collisionsSeen[pairHashIndex] & pairHashBit) !== 0) continue;
232-
collisionsSeen[pairHashIndex] |= pairHashBit;
233-
234-
// Ensure (x, y) -> x.id < y.id
235-
callback(entityB, entityA);
236-
}
216+
// Ensure eidA < eidB for triangular matrix indexing
217+
const [idA, idB] = eidA < eidB ? [eidA, eidB] : [eidB, eidA];
218+
const [entA, entB] = eidA < eidB ? [entityA, entityB] : [entityB, entityA];
219+
220+
// Triangular matrix index: row * (row - 1) / 2 + col, where row > col
221+
const triangularIndex = Math.floor(idB * (idB - 1) / 2) + idA;
222+
const arrayIndex = triangularIndex >>> 5;
223+
const bitIndex = triangularIndex & 31;
224+
const bitMask = 1 << bitIndex;
225+
226+
if ((collisionsSeen[arrayIndex] & bitMask) !== 0) continue;
227+
collisionsSeen[arrayIndex] |= bitMask;
228+
229+
callback(entA, entB);
237230
}
238231
}
239232
}

0 commit comments

Comments
 (0)