Instancing (currently unused)

This commit is contained in:
2025-03-25 00:42:27 -04:00
parent ad73736517
commit de3d653446
2 changed files with 100 additions and 9 deletions

View File

@@ -1,5 +1,8 @@
import Logging from "@proxnet/undead-logging";
import Profile from "../profiles.ts"; import Profile from "../profiles.ts";
const log = new Logging("Instances");
enum IntegratedRoomScene { enum IntegratedRoomScene {
Calibration = "f5fbd9c9-e853-4036-9d48-5f68e861af04", Calibration = "f5fbd9c9-e853-4036-9d48-5f68e861af04",
DormRoom = "76d98498-60a1-430c-ab76-b54a29b7a163", DormRoom = "76d98498-60a1-430c-ab76-b54a29b7a163",
@@ -48,7 +51,7 @@ enum IntegratedRoomScene {
DodgeballVR = "3d474b26-26f7-45e9-9a36-9b02847d5e6f", DodgeballVR = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
} }
interface RoomInstance { export interface RoomInstance {
roomInstanceId: number, roomInstanceId: number,
roomId: number, roomId: number,
@@ -67,21 +70,98 @@ interface RoomInstance {
} }
const instancePlayers: Map<RoomInstance, Set<Profile>> = new Map(); const instancePlayers: Map<RoomInstance, Set<Profile>> = new Map();
/**
* `Map<roomId (number), RoomInstance>`
*/
const instanceMap: Map<number, Set<RoomInstance>> = new Map(); const instanceMap: Map<number, Set<RoomInstance>> = new Map();
class InstancesBase { class InstancesBase {
async #ensureSubSet(roomId: number) { getAllInstances() {
const subMap = instanceMap.get(roomId); return new Set([...instanceMap.values()].flatMap(set => [...set]));
if (!subMap) instanceMap.set(roomId, new Set());
} }
// get all instances getAllRoomInstances(roomId: number) {
// get all instances for a room let instances = instanceMap.get(roomId);
// do not put instance categorization here (instance searching, or "matchmaking"); put that in MatchmakingBase if (!instances) {
instances = new Set();
instanceMap.set(roomId, instances);
}
return instances;
}
getInstancePlayers(instance: RoomInstance): Set<Profile> {
let players = instancePlayers.get(instance);
if (!players) {
players = new Set();
instancePlayers.set(instance, players);
}
return players;
}
clearEmptyInstances(instances: Set<RoomInstance>, roomId?: number) {
const beforeCount = instances.size;
for (const instance of instances) {
const profiles = this.getInstancePlayers(instance);
if (profiles.size === 0) {
log.i(`Instance ${instance.roomInstanceId} empty, deleting`);
instancePlayers.delete(instance);
this.getAllRoomInstances(instance.roomId).delete(instance);
}
}
const afterCount = instances.size;
log.d(`Cleared ${roomId !== undefined ? `room ${roomId}` : 'all'} empty instances.\n Instances before: ${beforeCount}\n Instances after: ${afterCount}`);
}
clearAllEmptyInstances() {
this.clearEmptyInstances(this.getAllInstances());
}
clearAllRoomEmptyInstances(roomId: number) {
this.clearEmptyInstances(this.getAllRoomInstances(roomId), roomId);
}
updateGlobalInstancesIsFull() {
for (const instance of this.getAllInstances()) {
instance.isFull = this.getInstancePlayers(instance).size >= instance.maxCapacity;
}
}
updateSingleInstanceIsFull(instance: RoomInstance) {
const profiles = this.getInstancePlayers(instance);
if (profiles.size >= instance.maxCapacity) instance.isFull = true;
else instance.isFull = false;
}
instanceCanFitPlayer(instance: RoomInstance) {
return this.getInstancePlayers(instance).size < instance.maxCapacity;
}
// add, remove, check for, get profile(s) in instances // add, remove, check for, get profile(s) in instances
// synchronize profile.#instance with the profile's current instance (profile.setInstance(RoomInstance)?) // synchronize profile.#instance with the profile's current instance (profile.setInstance(RoomInstance)?)
setPlayerInstance(player: Profile, instance: RoomInstance) {
const currentInstance = player.getInstance();
if (currentInstance === instance) return;
if (currentInstance) {
this.getInstancePlayers(currentInstance).delete(player);
this.updateSingleInstanceIsFull(currentInstance);
}
if (this.instanceCanFitPlayer(instance)) {
this.getInstancePlayers(instance).add(player);
player.setInstance(instance);
this.updateSingleInstanceIsFull(instance);
} else log.w(`Instance ${instance.roomInstanceId} is full. Cannot add player ${player.getId()}`);
}
playerIsInInstance(player: Profile, instance: RoomInstance) {
const profiles = this.getInstancePlayers(instance);
return profiles.has(player);
}
} }

View File

@@ -4,6 +4,7 @@ import { Config } from "../config.ts";
import { AuthType } from "./users.ts"; import { AuthType } from "./users.ts";
import * as JsonWebToken from "@gz/jwt"; import * as JsonWebToken from "@gz/jwt";
import { TokenBaseFormat } from "../apiutils.ts"; import { TokenBaseFormat } from "../apiutils.ts";
import { RoomInstance } from "./live/instances.ts";
const config = Config.getConfig(); const config = Config.getConfig();
@@ -180,15 +181,25 @@ class Profile {
#id: number; #id: number;
#instance: RoomInstance | null = null;
constructor(id: number) { constructor(id: number) {
this.#id = id; this.#id = id;
} }
setInstance(instance: RoomInstance) {
this.#instance = instance;
}
getInstance() {
return this.#instance;
}
getId() { getId() {
return this.#id; return this.#id;
} }
async getIsDev() { async getIsOperator() {
return (await Redis.Database.sismember(Redis.buildKey(Redis.KeyGroups.Operators), this.#id.toString())) >= 1; return (await Redis.Database.sismember(Redis.buildKey(Redis.KeyGroups.Operators), this.#id.toString())) >= 1;
} }
@@ -203,7 +214,7 @@ class Profile {
const payload: ProfileTokenFormat = { const payload: ProfileTokenFormat = {
iss: config.web.publichost, iss: config.web.publichost,
sub: this.#id, sub: this.#id,
role: (await this.getIsDev()) ? 'developer' : 'user', role: (await this.getIsOperator()) ? 'developer' : 'user',
exp: Math.round(Date.now() / 1000) + Math.round(config.auth.timeout * 60 * 60), exp: Math.round(Date.now() / 1000) + Math.round(config.auth.timeout * 60 * 60),
typ: AuthType.Game typ: AuthType.Game
}; };