Skip to content

Commit f1a5f39

Browse files
committed
feat: add scheduler to Actor and World
This also refactors the `Player.loggedIn` property into an `Actor.active` property so that I can use it in the generic `Actor`-level task processing refactor: move active to base Actor class, create init/destroy methods feat: add a scheduler to Actor feat: add a scheduler to World
1 parent 3eea5ad commit f1a5f39

File tree

4 files changed

+79
-5
lines changed

4 files changed

+79
-5
lines changed

src/engine/world/actor/actor.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { Animation, Graphic, UpdateFlags } from './update-flags';
1313
import { Skills } from './skills';
1414
import { Pathfinding } from './pathfinding';
1515
import { ActorMetadata } from './metadata';
16+
import { Task, TaskScheduler } from '@engine/task';
17+
import { logger } from '@runejs/common';
1618

1719

1820
export type ActorType = 'player' | 'npc';
@@ -52,6 +54,13 @@ export abstract class Actor {
5254
protected randomMovementInterval;
5355
protected _instance: WorldInstance = null;
5456

57+
/**
58+
* Is this actor currently active? If true, the actor will have its task queue processed.
59+
*
60+
* This is true for players that are currently logged in, and NPCs that are currently in the world.
61+
*/
62+
protected active: boolean;
63+
5564
/**
5665
* @deprecated - use new action system instead
5766
*/
@@ -64,6 +73,8 @@ export abstract class Actor {
6473
private _faceDirection: number;
6574
private _bonuses: { offensive: OffensiveBonuses, defensive: DefensiveBonuses, skill: SkillBonuses };
6675

76+
private readonly scheduler = new TaskScheduler();
77+
6778
protected constructor(actorType: ActorType) {
6879
this.type = actorType;
6980
this._walkDirection = -1;
@@ -72,6 +83,22 @@ export abstract class Actor {
7283
this.clearBonuses();
7384
}
7485

86+
/**
87+
* Adds a task to the actor's scheduler queue. These tasks will be stopped when the become inactive.
88+
*
89+
* If the task has a stack type of `NEVER`, other tasks in the same group will be cancelled.
90+
*
91+
* @param task The task to add
92+
*/
93+
public enqueueTask(task: Task): void {
94+
if (!this.active) {
95+
logger.warn(`Attempted to enqueue task for logged out player`);
96+
return;
97+
}
98+
99+
this.scheduler.enqueue(task);
100+
}
101+
75102
public clearBonuses(): void {
76103
this._bonuses = {
77104
offensive: {
@@ -439,6 +466,28 @@ export abstract class Actor {
439466
return true;
440467
}
441468

469+
/**
470+
* Initialise the actor.
471+
*/
472+
protected init() {
473+
this.active = true;
474+
}
475+
476+
/**
477+
* Destroy this actor.
478+
*
479+
* This will stop the processing of its action queue.
480+
*/
481+
protected destroy() {
482+
this.active = false;
483+
484+
this.scheduler.clear();
485+
}
486+
487+
protected tick() {
488+
this.scheduler.tick();
489+
}
490+
442491
public abstract equals(actor: Actor): boolean;
443492

444493
public get position(): Position {
@@ -520,5 +569,4 @@ export abstract class Actor {
520569
public get bonuses(): { offensive: OffensiveBonuses, defensive: DefensiveBonuses, skill: SkillBonuses } {
521570
return this._bonuses;
522571
}
523-
524572
}

src/engine/world/actor/npc.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export class Npc extends Actor {
103103
}
104104

105105
public async init(): Promise<void> {
106+
super.init();
107+
106108
activeWorld.chunkManager.getChunkForWorldPosition(this.position).addNpc(this);
107109

108110
if(this.movementRadius > 0) {
@@ -147,6 +149,7 @@ export class Npc extends Actor {
147149
}
148150

149151
public kill(respawn: boolean = true): void {
152+
this.destroy();
150153

151154
activeWorld.chunkManager.getChunkForWorldPosition(this.position).removeNpc(this);
152155
clearInterval(this.randomMovementInterval);
@@ -158,6 +161,8 @@ export class Npc extends Actor {
158161
}
159162

160163
public async tick(): Promise<void> {
164+
super.tick();
165+
161166
return new Promise<void>(resolve => {
162167
this.walkingQueue.process();
163168
resolve();

src/engine/world/actor/player/player.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ export class Player extends Actor {
130130
private readonly _outgoingPackets: OutboundPacketHandler;
131131
private readonly _equipment: ItemContainer;
132132
private _rights: Rights;
133-
private loggedIn: boolean;
134133
private _loginDate: Date;
135134
private _lastAddress: string;
136135
private firstTimePlayer: boolean;
@@ -172,7 +171,8 @@ export class Player extends Actor {
172171
}
173172

174173
public async init(): Promise<void> {
175-
this.loggedIn = true;
174+
super.init();
175+
176176
this.updateFlags.mapRegionUpdateRequired = true;
177177
this.updateFlags.appearanceUpdateRequired = true;
178178

@@ -277,7 +277,7 @@ export class Player extends Actor {
277277
}
278278

279279
public logout(): void {
280-
if(!this.loggedIn) {
280+
if(!this.active) {
281281
return;
282282
}
283283

@@ -288,6 +288,8 @@ export class Player extends Actor {
288288
activeWorld.playerTree.remove(this.quadtreeKey);
289289
this.save();
290290

291+
this.destroy();
292+
291293
this.actionsCancelled.complete();
292294
this.walkingQueue.movementEvent.complete();
293295
this.walkingQueue.movementQueued.complete();
@@ -297,7 +299,6 @@ export class Player extends Actor {
297299
activeWorld.chunkManager.getChunkForWorldPosition(this.position).removePlayer(this);
298300
activeWorld.deregisterPlayer(this);
299301

300-
this.loggedIn = false;
301302
logger.info(`${this.username} has logged out.`);
302303
}
303304

@@ -371,6 +372,8 @@ export class Player extends Actor {
371372
}
372373

373374
public async tick(): Promise<void> {
375+
super.tick();
376+
374377
return new Promise<void>(resolve => {
375378
this.walkingQueue.process();
376379

src/engine/world/world.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { loadActionFiles } from '@engine/action';
1212
import { ChunkManager, ConstructedRegion, getTemplateLocalX, getTemplateLocalY } from '@engine/world/map';
1313
import { TravelLocations, ExamineCache, parseScenerySpawns } from '@engine/world/config';
1414
import { loadPlugins } from '@engine/plugins';
15+
import { TaskScheduler, Task } from '@engine/task';
1516

1617

1718
export interface QuadtreeKey {
@@ -39,6 +40,7 @@ export class World {
3940
public readonly npcTree: Quadtree<QuadtreeKey>;
4041
public readonly globalInstance = new WorldInstance();
4142
public readonly tickComplete: Subject<void> = new Subject<void>();
43+
private readonly scheduler = new TaskScheduler();
4244

4345
private readonly debugCycleDuration: boolean = process.argv.indexOf('-tickTime') !== -1;
4446

@@ -69,6 +71,19 @@ export class World {
6971
logger.info(`Shutting down world...`);
7072
}
7173

74+
/**
75+
* Adds a task to the world scheduler queue. These tasks will run forever until they are cancelled.
76+
*
77+
* @warning Did you mean to add a world task, rather than an Actor task?
78+
*
79+
* If the task has a stack type of `NEVER`, other tasks in the same group will be cancelled.
80+
*
81+
* @param task The task to add
82+
*/
83+
public enqueueTask(task: Task): void {
84+
this.scheduler.enqueue(task);
85+
}
86+
7287
/**
7388
* Searched for an object by ID at the given position in any of the player's active instances.
7489
* @param actor The actor to find the object for.
@@ -441,6 +456,9 @@ export class World {
441456

442457
public async worldTick(): Promise<void> {
443458
const hrStart = Date.now();
459+
460+
this.scheduler.tick();
461+
444462
const activePlayers: Player[] = this.playerList.filter(player => player !== null);
445463

446464
if(activePlayers.length === 0) {

0 commit comments

Comments
 (0)