faster game state

This commit is contained in:
azykov@mail.ru
2026-06-13 18:34:54 +03:00
parent 004ec827d3
commit 6a16d39d88
3 changed files with 66 additions and 37 deletions
+38 -26
View File
@@ -1,6 +1,6 @@
import { makeAutoObservable, reaction, toJS } from "mobx";
import { autorun, makeAutoObservable, toJS } from "mobx";
import type { WorldState } from "./worldState";
import type { Game, Pos3, RuntimeGameScene, ScriptContext, ScriptCoroutineContext } from "../types";
import type { Game, GameObjectInstance, Pos3, RuntimeGameScene, ScriptContext, ScriptCoroutineContext } from "../types";
import type { CameraProps } from "@react-three/fiber";
import type { ScriptGameEventBus } from "../scripts/eventBus";
import { GameFactory } from "../model/gameFactory";
@@ -18,12 +18,31 @@ export class GameState {
private coroutines = new ScriptCoroutines();
private eventBus: ScriptGameEventBus;
private _buildGameObjects() {
return Object.fromEntries(
Object.entries(this.scene.objects).map(([id, obj]) => [id, {
id: obj.id,
typeId: obj.typeId,
physics: obj.physics,
gravityScale: obj.gravityScale,
position: toJS(obj.position),
rotation: toJS(obj.rotation),
scale: toJS(obj.scale),
version: obj.version,
linearVelocity: toJS(obj.linearVelocity),
angularVelocity: toJS(obj.angularVelocity),
} satisfies GameObjectInstance])
) as unknown as RuntimeGameScene['objects'];
}
private startAutoSave() {
return reaction(
() => (this.asGame),
(data) => this.saveData(data),
{ delay: 5000 }
);
return autorun(() => {
GameFactory.save({
paused: this.isPaused,
time: this.time,
scene: { playerObjectId: this.scene.playerObjectId, objects: this._buildGameObjects() },
});
}, { delay: 5000 });
}
// @ts-expect-error -- for future use
@@ -46,12 +65,16 @@ export class GameState {
this.isFirstTick = true;
this.scene = game.scene;
this._stopAutoSave = this.startAutoSave();
makeAutoObservable(this);
this._stopAutoSave = this.startAutoSave();
this.eventBus = GameFactory.evalGameScripts(rawWorld, this.scene, this.coroutines);
}
public dispose(): void {
this._stopAutoSave();
}
public emitEvent(event: string, context: Omit<ScriptContext, 'world' | 'scene' | keyof ScriptCoroutineContext> = {}) {
this.eventBus.emit(
event,
@@ -63,14 +86,6 @@ export class GameState {
);
}
public get asGame(): Game {
return {
paused: toJS(this.isPaused),
time: toJS(this.time),
scene: toJS(this.scene),
};
}
public setGame(value: Game) {
this.isPaused = value.paused;
this.time = value.time;
@@ -101,16 +116,13 @@ export class GameState {
// });
// }
private saveData(data: Game): void {
console.log('Saving game...');
const stack = new Error('Saving game...').stack!.split('\n').slice(1);
const { ...debug } = toJS(data);
console.dir({ stack, debug });
GameFactory.save(data);
}
public save(): void {
this.saveData(this.asGame);
console.log('Saving game...');
GameFactory.save({
paused: this.isPaused,
time: this.time,
scene: { playerObjectId: this.scene.playerObjectId, objects: this._buildGameObjects() },
});
}
public resume(): void {
@@ -138,4 +150,4 @@ export class GameState {
this.emitEvent('gameStart');
}
}
}
}
+1
View File
@@ -44,6 +44,7 @@ export class RootState {
return;
this.game.pause();
this.game.dispose();
this.game = undefined;
}
+27 -11
View File
@@ -1,4 +1,4 @@
import { makeAutoObservable, reaction, toJS } from "mobx";
import { autorun, makeAutoObservable, toJS } from "mobx";
import { WorldFactory } from "../model/worldFactory";
import { type ObjectInstance, type ObjectType, type World, type VoxelType, type RuntimeWorld } from "../types";
import { state } from "./rootState";
@@ -10,8 +10,32 @@ import { populateRuntimeWorld } from "../utils/runtime";
export class WorldState {
public data: RuntimeWorld = WorldFactory.create();
private _buildSave(): World {
const d = this.data;
return {
objectTypes: toJS(d.objectTypes),
voxelTypes: toJS(d.voxelTypes),
editorCamera: toJS(d.editorCamera),
gameRules: toJS(d.gameRules),
initialScene: {
playerObjectId: d.initialScene.playerObjectId,
objects: Object.fromEntries(
Object.entries(d.initialScene.objects).map(([id, obj]) => [id, {
id: obj.id,
typeId: obj.typeId,
physics: obj.physics,
gravityScale: obj.gravityScale,
position: toJS(obj.position),
rotation: toJS(obj.rotation),
scale: toJS(obj.scale),
} satisfies ObjectInstance])
),
},
};
}
private startAutoSave() {
return reaction(() => toJS(this.data), (data) => this.saveData(data), { delay: 500 });
return autorun(() => WorldFactory.save(this._buildSave()), { delay: 500 });
}
private withoutAutoSave(fn: () => void) {
@@ -103,16 +127,8 @@ export class WorldState {
state.worldEditor.resetSelection();
}
private saveData(data: World): void {
console.log('Saving world...');
const stack = new Error('Saving world...').stack!.split('\n').slice(1);
const { voxelTypes: _vts, initialScene: _is, ...debug } = toJS(data);
console.dir({ stack, debug });
WorldFactory.save(toJS(this.data));
}
public save(): void {
this.saveData(this.data);
WorldFactory.save(this._buildSave());
}
public getObjectTypeById(id: string): ObjectType | undefined {