Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/engine/GameMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ export default class GameMap {
continue;
}
const npcType: NpcType = NpcType.get(id);
const size: number = npcType.size;
const npc: Npc = new Npc(level, absoluteX, absoluteZ, size, size, EntityLifeCycle.RESPAWN, World.getNextNid(), npcType.id, npcType.moverestrict, npcType.blockwalk);
if ((npcType.members && this.members) || !npcType.members) {
const size: number = npcType.size;
const npc: Npc = new Npc(level, absoluteX, absoluteZ, size, size, EntityLifeCycle.RESPAWN, World.getNextNid(), npcType.id, npcType.moverestrict, npcType.blockwalk);
World.addNpc(npc, -1);
}
}
Expand All @@ -134,8 +134,8 @@ export default class GameMap {
continue;
}
const objType: ObjType = ObjType.get(id);
const obj: Obj = new Obj(level, absoluteX, absoluteZ, EntityLifeCycle.RESPAWN, objType.id, count);
if ((objType.members && this.members) || !objType.members) {
const obj: Obj = new Obj(level, absoluteX, absoluteZ, EntityLifeCycle.RESPAWN, objType.id, count);
this.getZone(obj.x, obj.z, obj.level).addStaticObj(obj);
}
}
Expand Down
84 changes: 40 additions & 44 deletions src/engine/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import {
trackCycleBandwidthOutBytes,
trackCycleClientInTime,
trackCycleClientOutTime,
trackCycleInfoTime,
trackCycleLoginTime,
trackCycleLogoutTime,
trackCycleNpcTime,
Expand Down Expand Up @@ -145,8 +146,8 @@ class World {
readonly npcEventQueue: LinkList<NpcEventRequest>;

// debug data
readonly lastCycleStats: number[];
readonly cycleStats: number[];
readonly lastCycleStats: Uint16Array;
readonly cycleStats: Uint16Array;

tickRate: number = World.TICKRATE; // speeds up when we're processing server shutdown
currentTick: number = 0; // the current tick of the game world.
Expand All @@ -171,8 +172,8 @@ class World {
this.locObjTracker = new LinkList();
this.queue = new LinkList();
this.npcEventQueue = new LinkList();
this.lastCycleStats = new Array(12).fill(0);
this.cycleStats = new Array(12).fill(0);
this.lastCycleStats = new Uint16Array(13);
this.cycleStats = new Uint16Array(13);

if (Environment.STANDALONE_BUNDLE) {
if (this.loginThread instanceof Worker) {
Expand Down Expand Up @@ -274,10 +275,7 @@ class World {

for (let i = 0; i < this.vars.length; i++) {
const varsh = VarSharedType.get(i);
if (varsh.type === ScriptVarType.STRING) {
// todo: "null"? another value?
continue;
} else {
if (varsh.type !== ScriptVarType.STRING) {
this.vars[i] = varsh.type === ScriptVarType.INT ? 0 : -1;
}
}
Expand Down Expand Up @@ -358,13 +356,9 @@ class World {
cycle(): void {
try {
const start: number = Date.now();
// the deviation the thread took from sleeping last tick until now.
const drift: number = Math.max(0, start - this.nextTick);

// world processing
// - world queue
// - npc hunt
this.processWorld();

// client input
// - calculate afk event readiness
// - process packets
Expand Down Expand Up @@ -432,20 +426,33 @@ class World {
// - reset invs
this.processCleanup();

// ----

const tick: number = this.currentTick;

if (this.shutdown) {
this.processShutdown();
}

if (tick % World.PLAYER_SAVERATE === 0 && tick > 0) {
// ----
// - increment tick before processing world queues.
// - world queues can't happen on tick 0.
// - logging is technically tick+1 but that's ok idc.
// - tick will ALWAYS be > 0 from here on...

this.currentTick++;
this.nextTick += this.tickRate;

// world processing
// - world queue
// - npc hunt
this.processWorld();

// ----
// - do logging and stuff from here on, no more cycle stuff.

if (this.currentTick % World.PLAYER_SAVERATE === 0) {
// auto-save players every 15 mins
this.savePlayers();
}

if (tick % World.PLAYER_COORDLOGRATE === 0 && tick > 0) {
if (this.currentTick % World.PLAYER_COORDLOGRATE === 0) {
for (const player of this.players) {
player.addSessionLog(LoggerEventType.MODERATOR, 'Server check in');
}
Expand Down Expand Up @@ -486,6 +493,7 @@ class World {
this.lastCycleStats[WorldStat.LOGOUT] = this.cycleStats[WorldStat.LOGOUT];
this.lastCycleStats[WorldStat.LOGIN] = this.cycleStats[WorldStat.LOGIN];
this.lastCycleStats[WorldStat.ZONE] = this.cycleStats[WorldStat.ZONE];
this.lastCycleStats[WorldStat.INFO] = this.cycleStats[WorldStat.INFO];
this.lastCycleStats[WorldStat.CLIENT_OUT] = this.cycleStats[WorldStat.CLIENT_OUT];
this.lastCycleStats[WorldStat.CLEANUP] = this.cycleStats[WorldStat.CLEANUP];
this.lastCycleStats[WorldStat.BANDWIDTH_IN] = this.cycleStats[WorldStat.BANDWIDTH_IN];
Expand All @@ -503,6 +511,7 @@ class World {
trackCycleNpcTime.observe(this.cycleStats[WorldStat.NPC]);
trackCyclePlayerTime.observe(this.cycleStats[WorldStat.PLAYER]);
trackCycleZoneTime.observe(this.cycleStats[WorldStat.ZONE]);
trackCycleInfoTime.observe(this.cycleStats[WorldStat.INFO]);
trackCycleLoginTime.observe(this.cycleStats[WorldStat.LOGIN]);
trackCycleLogoutTime.observe(this.cycleStats[WorldStat.LOGOUT]);

Expand All @@ -514,13 +523,10 @@ class World {
printInfo(`tick ${this.currentTick}: ${this.cycleStats[WorldStat.CYCLE]}/${this.tickRate} ms, ${Math.trunc(process.memoryUsage().heapTotal / 1024 / 1024)} MB heap`);
printDebug(`${this.getTotalPlayers()}/${World.PLAYERS} players | ${this.getTotalNpcs()}/${World.NPCS} npcs | ${this.gameMap.getTotalZones()} zones | ${this.gameMap.getTotalLocs()} locs | ${this.gameMap.getTotalObjs()} objs`);
printDebug(
`${this.cycleStats[WorldStat.WORLD]} ms world | ${this.cycleStats[WorldStat.CLIENT_IN]} ms client in | ${this.cycleStats[WorldStat.NPC]} ms npcs | ${this.cycleStats[WorldStat.PLAYER]} ms players | ${this.cycleStats[WorldStat.LOGOUT]} ms logout | ${this.cycleStats[WorldStat.LOGIN]} ms login | ${this.cycleStats[WorldStat.ZONE]} ms zones | ${this.cycleStats[WorldStat.CLIENT_OUT]} ms client out | ${this.cycleStats[WorldStat.CLEANUP]} ms cleanup`
`${this.cycleStats[WorldStat.WORLD]} ms world | ${this.cycleStats[WorldStat.CLIENT_IN]} ms client in | ${this.cycleStats[WorldStat.NPC]} ms npcs | ${this.cycleStats[WorldStat.PLAYER]} ms players | ${this.cycleStats[WorldStat.LOGOUT]} ms logout | ${this.cycleStats[WorldStat.LOGIN]} ms login | ${this.cycleStats[WorldStat.ZONE]} ms zones | ${this.cycleStats[WorldStat.INFO]} ms infos | ${this.cycleStats[WorldStat.CLIENT_OUT]} ms client out | ${this.cycleStats[WorldStat.CLEANUP]} ms cleanup`
);
}

this.currentTick++;
this.nextTick += this.tickRate;

// ----

setTimeout(this.cycle.bind(this), Math.max(0, this.tickRate - (Date.now() - start) - drift));
Expand Down Expand Up @@ -589,7 +595,7 @@ class World {
const hunt = HuntType.get(npc.huntMode);

if (hunt && hunt.type === HuntModeType.PLAYER) {
npc.huntAll();
npc.huntAll(hunt);
}
}
}
Expand Down Expand Up @@ -626,15 +632,10 @@ class World {
}

if ((!player.target || player.target instanceof Loc || player.target instanceof Obj) && player.faceEntity !== -1) {
player.faceEntity = -1;
player.masks |= player.entitymask;
player.unfocusTargetEntity();
}

if (!player.busy() && player.opcalled) {
player.moveClickRequest = false;
} else {
player.moveClickRequest = true;
}
player.moveClickRequest = !(!player.busy() && player.opcalled);

if (!followingPlayer && player.opcalled && (player.userPath.length === 0 || !Environment.NODE_CLIENT_ROUTEFINDER)) {
player.pathToTarget();
Expand Down Expand Up @@ -988,13 +989,11 @@ class World {
// - convert npc movements
// - compute npc info
private processInfo(): void {
// TODO: benchmark this?
const start: number = Date.now();
for (const player of this.players) {
player.reorient();
player.buildArea.rebuildNormal(); // set origin before compute player is why this is above.

const appearance = player.masks & PlayerInfoProt.APPEARANCE ? player.generateAppearance() : (player.lastAppearanceBytes ?? player.generateAppearance());

// TODO possible optimization to only compute when actually needed?
rsbuf.computePlayer(
player.x,
player.level,
Expand All @@ -1009,7 +1008,7 @@ class World {
player.visibility,
player.isActive,
player.masks,
appearance,
player.masks & PlayerInfoProt.APPEARANCE ? player.generateAppearance() : (player.lastAppearanceBytes ?? player.generateAppearance()),
player.lastAppearance,
player.faceEntity,
player.faceX,
Expand Down Expand Up @@ -1042,6 +1041,7 @@ class World {

for (const npc of this.npcs) {
npc.reorient();
// TODO possible optimization to only compute when actually needed?
rsbuf.computeNpc(
npc.x,
npc.level,
Expand Down Expand Up @@ -1070,6 +1070,7 @@ class World {
npc.graphicDelay
);
}
this.cycleStats[WorldStat.INFO] = Date.now() - start;
}

// - map update
Expand Down Expand Up @@ -1168,21 +1169,21 @@ class World {
}
// Item stock is under min
if (item.count < invType.stockcount[index] && tick % invType.stockrate[index] === 0) {
inv.add(item?.id, 1, index, true, false, false);
inv.add(item.id, 1, index, true, false, false);
inv.update = true;
continue;
}
// Item stock is over min
if (item.count > invType.stockcount[index] && tick % invType.stockrate[index] === 0) {
inv.remove(item?.id, 1, index, true);
inv.remove(item.id, 1, index, true);
inv.update = true;
continue;
}

// Item stock is not listed, such as general stores
// Tested on low and high player count worlds, ever 1 minute stock decreases.
if (invType.allstock && !invType.stockcount[index] && tick % World.INV_STOCKRATE === 0) {
inv.remove(item?.id, 1, index, true);
inv.remove(item.id, 1, index, true);
inv.update = true;
}
}
Expand Down Expand Up @@ -1579,11 +1580,6 @@ class World {
});
}

addPlayer(player: Player): void {
this.newPlayers.add(player);
player.isActive = true;
}

sendPrivateChatModeToFriendsServer(player: Player): void {
this.friendThread.postMessage({
type: 'player_chat_setmode',
Expand Down
1 change: 1 addition & 0 deletions src/engine/WorldStat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const enum WorldStat {
LOGOUT,
LOGIN,
ZONE,
INFO,
CLIENT_OUT,
CLEANUP,
BANDWIDTH_IN,
Expand Down
15 changes: 4 additions & 11 deletions src/engine/entity/NetworkPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,7 @@ export class NetworkPlayer extends Player {
}

writeInner(message: OutgoingMessage): void {
const client = this.client;
if (!client) {
return;
}
const client: ClientSocket = this.client;

const encoder: MessageEncoder<OutgoingMessage> | undefined = ServerProtProvider.ServerProtRepository.getEncoder(message);
if (!encoder) {
Expand All @@ -207,16 +204,12 @@ export class NetworkPlayer extends Player {

buf.pos = 0;

if (client.encryptor) {
buf.p1(prot.id + client.encryptor.nextInt());
} else {
buf.p1(prot.id);
}
buf.p1(prot.id + (client.encryptor?.nextInt() ?? 0));

if (prot.length === -1) {
buf.p1(0);
buf.pos += 1;
} else if (prot.length === -2) {
buf.p2(0);
buf.pos += 2;
}

const start: number = buf.pos;
Expand Down
21 changes: 6 additions & 15 deletions src/engine/entity/Npc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export default class Npc extends PathingEntity {
huntMode: number = -1;
huntTarget: Entity | null = null;
huntrange: number = 0;
spawnTriggerPending: boolean = true;

nextPatrolTick: number = -1;
nextPatrolPoint: number = 0;
Expand Down Expand Up @@ -160,7 +159,7 @@ export default class Npc extends PathingEntity {
if (hunt.nobodyNear !== HuntNobodyNear.PAUSEHUNT || rsbuf.getNpcObservers(this.nid) > 0 || hunt.type === HuntModeType.PLAYER) {
// - hunt npc/obj/loc
if (hunt && hunt.type !== HuntModeType.PLAYER) {
this.huntAll();
this.huntAll(hunt);
}

// Increment huntclock
Expand Down Expand Up @@ -244,11 +243,9 @@ export default class Npc extends PathingEntity {

// https://x.com/JagexAsh/status/1821236327150710829
// https://x.com/JagexAsh/status/1799793914595131463
huntAll(): void {
huntAll(hunt: HuntType): void {
this.huntTarget = null;

const hunt: HuntType = HuntType.get(this.huntMode);

// If a huntrate is defined, this acts as a throttle
if (this.huntClock < hunt.rate - 1) {
return;
Expand All @@ -272,8 +269,7 @@ export default class Npc extends PathingEntity {

// Pick randomly from the hunted entities
if (hunted.length > 0) {
const entity: Entity = hunted[Math.floor(Math.random() * hunted.length)];
this.huntTarget = entity;
this.huntTarget = hunted[Math.floor(Math.random() * hunted.length)];
}
}

Expand All @@ -292,10 +288,7 @@ export default class Npc extends PathingEntity {

for (let i = 0; i < this.vars.length; i++) {
const varn = VarNpcType.get(i);
if (varn.type === ScriptVarType.STRING) {
// todo: "null"? another value?
continue;
} else {
if (varn.type !== ScriptVarType.STRING) {
this.vars[i] = varn.type === ScriptVarType.INT ? 0 : -1;
}
}
Expand Down Expand Up @@ -402,16 +395,14 @@ export default class Npc extends PathingEntity {
clearInteraction(): void {
super.clearInteraction();
this.targetOp = NpcMode.NONE;
this.faceEntity = -1;
this.masks |= NpcInfoProt.FACE_ENTITY;
this.unfocusTargetEntity();
}

resetDefaults(): void {
this.clearInteraction();
const type: NpcType = NpcType.get(this.type);
this.targetOp = type.defaultmode;
this.faceEntity = -1;
this.masks |= this.entitymask;
this.unfocusTargetEntity();

const npcType: NpcType = NpcType.get(this.type);
this.huntMode = npcType.huntmode;
Expand Down
11 changes: 9 additions & 2 deletions src/engine/entity/PathingEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,14 @@ export default abstract class PathingEntity extends Entity {
this.orientationZ = CoordGrid.fine(this.z - 1, this.length);
}

/**
* Unfocus from a possible targeted pathing entity.
*/
unfocusTargetEntity(): void {
this.masks |= this.entitymask;
this.faceEntity = -1;
}

/**
* Try to focus back on a possible target.
* This is needed because the target can move.
Expand Down Expand Up @@ -623,8 +631,7 @@ export default abstract class PathingEntity extends Entity {
this.faceZ = -1;

if (!this.target && this.faceEntity !== -1) {
this.masks |= this.entitymask;
this.faceEntity = -1;
this.unfocusTargetEntity();
}
}

Expand Down
Loading