diff --git a/src/core/deligator.ts b/src/core/deligator.ts new file mode 100644 index 0000000..cd95cd3 --- /dev/null +++ b/src/core/deligator.ts @@ -0,0 +1,59 @@ +import type { ICommand, IDeligator, IDeligatorOptions, ISnapshot } from "@interfaces"; +import { Game } from "@core" + +/** + * Deligator allows you to execute commands in source engine for perfomance boost + */ +export class Deligator implements IDeligator { + private readonly options: IDeligatorOptions; + + private game!: Game; + private isSourceInited = false + + private async activateSource() { + try { + const source = await fetch(this.options.source as URL, { + headers: { + "Content-Type":"Application/Json" + } + }) + const snapshot = await source.json() as ISnapshot + + this.game = Game.fromSnapshot(snapshot) + this.onFullLoad() + } catch { + throw new Error('[Deligator]: Cant fetch source') + } + } + + private onFullLoad() { + if (!this.game.options.commandBusOptions?.usingCommangQueue) return; + + this.game.use((cmd, next, game) => { + if (game.commandQueueLength < this.options.triggerOn) return next() + else return this.deligate(cmd) + }) + } + + public constructor(options: IDeligatorOptions) { + this.options = options + + if (!(this.options.source instanceof URL)) { + this.game = this.options.source + this.isSourceInited = true + this.onFullLoad() + } + else this.activateSource() + } + + /** + * Deligate cmd to source engine + * @param cmd - Cmd to deligate + * @returns { void } + */ + public deligate(cmd: ICommand): void { + if (!this.isSourceInited) return; + + this.game.dispatch(cmd) + } +} \ No newline at end of file diff --git a/src/core/game.ts b/src/core/game.ts index 96eb3f9..42afa18 100644 --- a/src/core/game.ts +++ b/src/core/game.ts @@ -596,4 +596,25 @@ export class Game implements IGame { public get currentTick() { return this._currentTick } + + /** + * Returns a actually command queue length, undefined if queue not enabled + * @returns { number } - Actual queue length + */ + public get commandQueueLength(): number { + return this.commandQueue.length; + } + + /** + * Load game as static method from snapshot + * @param snapshot - Snapshot to load + * @returns { Game } - Game from snapshot with live entities + */ + public static fromSnapshot(snapshot: ISnapshot, onLoad?: (game: Game) => void): Game { + const engine = new Game() + + engine.load(snapshot, onLoad) + + return engine; + } } diff --git a/src/core/index.ts b/src/core/index.ts index c50bd59..f77f535 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,3 +1,4 @@ export * from "./game.js" export * from "./manager.js" -export * from "./undo-manager.js" \ No newline at end of file +export * from "./undo-manager.js" +export * from "./deligator.js" \ No newline at end of file diff --git a/src/interfaces/core/engine/classes-engine.interfaces.ts b/src/interfaces/core/engine/classes-engine.interfaces.ts index 3894827..9a8f250 100644 --- a/src/interfaces/core/engine/classes-engine.interfaces.ts +++ b/src/interfaces/core/engine/classes-engine.interfaces.ts @@ -300,4 +300,8 @@ export interface IGameMap { * @returns { Entity[] } - Array of entities founded in quad on applying effect */ readonly applyEffectToQuad: (quad: Quad, effect: IGameEffect, duration: number, excludeId?: number) => Entity[]; +} + +export interface IDeligator { + readonly deligate: (cmd: ICommand) => void; } \ No newline at end of file diff --git a/src/interfaces/core/engine/engine-options.intefaces.ts b/src/interfaces/core/engine/engine-options.intefaces.ts index 2ee3382..4d8ae56 100644 --- a/src/interfaces/core/engine/engine-options.intefaces.ts +++ b/src/interfaces/core/engine/engine-options.intefaces.ts @@ -1,4 +1,4 @@ -import type { UndoManager } from "@core"; +import type { Game, UndoManager } from "@core"; import type { IEntityManager, IGameMap } from "@interfaces"; import type { GlobalStore } from "@store"; @@ -68,4 +68,21 @@ export interface ICommandBusOptions { readonly maxCommandsPerTick?: number; } -export interface IInitGameOptions extends Partial {} \ No newline at end of file +export interface IInitGameOptions extends Partial {} + +export interface IDeligatorOptions { + /** + * Delegator, who will execute this command + */ + readonly source: URL | Game; + + /** + * Deligator will be executed auto, when this command in observe engine has been saved + */ + readonly triggerOn: number; + + /** + * Engine to observe commands + */ + readonly observe: Game; +} \ No newline at end of file diff --git a/test/gametest.ts b/test/gametest.ts index 308070e..cbae700 100644 --- a/test/gametest.ts +++ b/test/gametest.ts @@ -1,5 +1,5 @@ import { CommandType, FactoryKeys, GameObjectEnum } from "@enums"; -import createGame from "@"; +import createGame, { Deligator } from "@"; import type { IAttackData, IClient, IItemPickedUpErrorData, IMovedData, IObjectCreatedCollisionData, IObjectCreatedErrorData, IServer, IUseValidationResult, IUseVisibilityContext } from "@interfaces"; import type { CreateChestMetadata, CreateItemMetadata, CreateTowerMetadata, NetworkCallback, Position, Quad } from "@types"; import { BASE_SEARCH_RADIUS, USE_VALIDATION_EVENT_PREFIX, USE_VISIBILITY_EVENT } from '@const' @@ -24,6 +24,12 @@ game.registerPlugin(new GraphicPlugin({ } })) +const deligator = new Deligator({ + observe: game, + source: game, + triggerOn: 5 +}) + const asyncPlugin = new AsyncPlugin(500) game.registerPlugin(asyncPlugin) @@ -314,6 +320,15 @@ game.dispatch({ } }) +deligator.deligate({ + tick: game.currentTick, + type: CommandType.USE_ITEM, + entityId: player.id, + data: { + + } +}) + useLink(player, player_second, { maxDistance: 1, autoUnlinkOn: ["childOutOfRange", "parentDeleted", "parentKilled"], killChild: false, deleteChild: false, enableMiddleware: true }) console.log(useValidation(player_second, CommandType.MOVE, {