Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
e4ad15a
feat: A bit more progress on the 530 branch.
Dec 15, 2025
90fad39
fix: Attempt to appease the Lint.
Dec 15, 2025
ce840b7
fix: Lint please for the love of all that is holy
Dec 15, 2025
4c6a944
feat: A tad more progress on 530 protocol.
Dec 16, 2025
8b89dc1
fix: Corrected order of entries in ObjRevealEncoder packet.
Dec 19, 2025
fdbc49f
chore: Minor progress on decoders.
Jan 25, 2026
9b0db0e
Update OpObjEHandler.ts
FlenarnDev Jan 26, 2026
7ec4952
Update OpLocEHandler.ts
FlenarnDev Jan 26, 2026
e520aa9
Update OpObjEDecoder.ts
FlenarnDev Jan 26, 2026
7ac0fc3
feat: Use JavaRandom for RuneScript commands
Pazaz Dec 22, 2025
c3f7806
fix: Safety guard on random(negative)
Pazaz Dec 29, 2025
c1e48af
fix: support categories in ai triggers (#40)
Indio3 Sep 2, 2025
3ef5729
fix: Clear waypoints on respawned NPCs
Pazaz Nov 5, 2025
4cae592
fix: update npc stats on changetype (#46)
Indio3 Sep 26, 2025
680059e
fix: Hardcoded stake logging
Pazaz Oct 24, 2025
bb90c5d
fix: Redundant NPC removal/re-additions on respawn
Pazaz Nov 5, 2025
d2a23fe
fix: playAnimation priority
Pazaz Nov 13, 2025
96966e7
fix: Clear temp invs when reloading
Pazaz Sep 8, 2025
cd3ab48
fix: use chebyshev distance for radius dist in map_findsquare (#51)
Indio3 Nov 16, 2025
540d00b
chore: Cleaning up some player variable names
Pazaz Dec 8, 2025
6868056
fix: Use JavaRandom.nextDouble() instead
Pazaz Dec 29, 2025
e791ddd
fix: Report-abuse session UUID
Pazaz Jan 4, 2026
4d8aa27
fix: Show SQLite query errors in console
Pazaz Jan 4, 2026
ef211ac
fix: Clear hero points on full npc_statheal
Pazaz Jan 11, 2026
f91248e
fix: Script error backtrace left out initial trigger
Pazaz Jan 13, 2026
8c295a7
fix: Incomplete base37 encoding
Pazaz Jan 1, 2026
34954f5
fix: prevent hiscores from updating banned players (#35)
xVye Dec 28, 2025
aa3ab65
fix: Reconnect woes
Pazaz Dec 28, 2025
8f4d4bf
fix: Some pointer fixes (#63)
AmVoidGuy Dec 27, 2025
a645bf7
fix: Complete stack backtrace if calling proc in a label
Pazaz Dec 22, 2025
2fdb071
fix: Cherry picking brought back some evils.
FlenarnDev Jan 26, 2026
1e3e26d
feat: Some loc type config work.
FlenarnDev Jan 26, 2026
c894eff
fix: Playerinfo packet correction.
FlenarnDev Jan 27, 2026
0f00851
feat: IdkType updated for 530.
Jan 27, 2026
c36b593
chore: Update compiler version for TS port.
Feb 16, 2026
d274f01
feat: Param helper update for 530.
Feb 16, 2026
2566e72
feat: More config handling.
Feb 17, 2026
849354c
chore: Mark vars unused.
FlenarnDev Feb 18, 2026
32e98b3
chore: Note for future name adjustment.
FlenarnDev Feb 18, 2026
24a8884
feat: Further progress on lent item generation, some stuff left still.
FlenarnDev Feb 18, 2026
4992e7f
fix: Update naming of config.
Feb 18, 2026
1200777
chore: Appease the gods.
Feb 18, 2026
6b88496
feat: toLentObj almost done, missing desc.
Feb 18, 2026
d9625d3
chore: Update skill count to 24.
Feb 18, 2026
316caf9
feat: Basic implementations for OpLocE & OpObjE.
Feb 18, 2026
2834335
chore: Removed stupid thinking.
FlenarnDev Feb 19, 2026
a66db6d
feat: Further loc decoding.
FlenarnDev Feb 19, 2026
564c356
feat: Window mode handling for player.
FlenarnDev Feb 19, 2026
c28e4d7
feat: Missing handler for window status.
FlenarnDev Feb 19, 2026
5480fb9
feat: Prot updates.
FlenarnDev Feb 19, 2026
095e90d
fix: Save all 24 of the skills.
Feb 19, 2026
1bc10fe
feat: More ScriptVarTypes.
Feb 19, 2026
e12cf0d
chore: Remove uneeded environment variables.
FlenarnDev Feb 20, 2026
819c519
chore: Add TypeScript compiler package.
FlenarnDev Feb 20, 2026
695f69b
feat: Additions to support JS5Pack.
Feb 21, 2026
be2925c
feat: JS5Pack loading, currently replaces the idx/dat script loading.
Feb 21, 2026
481c2f5
chore: Update ScriptVarType to support additional types.
Feb 22, 2026
c685b2e
feat: ColorFudge function, not strictly necessary, but handy for veri…
Feb 22, 2026
3c00e94
feat: Update GZIP to produce matching compression results.
Feb 22, 2026
15df1c8
fix: Missing dependency.
Feb 22, 2026
7d26c3b
feat: Update FloType for revision 530.
Feb 22, 2026
e06fdc1
feat: JS5 work to handle config dumping/packing.
Feb 22, 2026
f11bed8
feat: Enum pack/dump/loading.
Feb 22, 2026
201014f
feat: Flo pack/dump/unpack.
Feb 22, 2026
48ca834
feat: JS5 Archive pack handling.
Feb 22, 2026
810e131
feat: Midi pack/unpack.
Feb 22, 2026
bbfb2e6
feat: Flu pack/dump/unpack.
Feb 22, 2026
65c03e6
chore: Remove excess import.
Feb 22, 2026
e331834
feat: Inv pack/unpack.
Feb 22, 2026
d15482e
feat: Util commands.
Feb 22, 2026
dff7cb6
chore: Remove deprecated file.
Feb 22, 2026
17f0d15
chore: Cleanup packs.
Feb 23, 2026
882fc80
feat: Quickchat decode/unpack/packing.
Feb 23, 2026
b81aa41
chore: Temporary JS5 serving update.
Feb 23, 2026
826f16d
chore: Group filecount tool, extremely temporary.
Feb 23, 2026
d5a412c
feat: Some further progress on NPCType config.
Feb 23, 2026
ceaf254
chore: Cleanups.
Feb 23, 2026
d3bd9da
feat: Small debug tool to check file count in groups.
Feb 24, 2026
63e029c
chore: Abstract and cleanup pack/unpack/dump logic.
Feb 24, 2026
f0a73e0
feat: Midi support in enums.
Feb 24, 2026
b4031ec
chore: Tooling to match file hashes, based on Gregs keywords.
Feb 25, 2026
319b5a2
feat: Param dump/packing.
Feb 25, 2026
642e80a
feat: Struct dump/pack
Feb 25, 2026
18d10bc
chore: Correct JS5 output name.
Feb 25, 2026
52556ab
feat: Unpack/pack patches.
Feb 28, 2026
8937417
chore: Cleanup some basics in misc. packing.
Feb 28, 2026
5631009
chore: Remove deprecated opcode in ObjType.
Feb 28, 2026
94f957c
feat: NPC dumping/unpack/pack.
Feb 28, 2026
f3805b5
chore: Ship more of the files from .js5 packs.
Feb 28, 2026
e2a7d6f
feat: Param writes stuct references correctly.
Mar 1, 2026
0296ac9
feat: Obj dump/packing.
Mar 1, 2026
252b4b2
feat: Mesanim pack/unpack.
Mar 6, 2026
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
46 changes: 46 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,49 @@
"db:reset": "prisma migrate reset --force --schema prisma/multiworld/schema.prisma",
"db:schema": "prisma migrate dev --schema prisma/multiworld/schema.prisma",
"dev": "bun run --watch src/app.ts",
"dump:enums": "bun run tools/cache/dump/dumpEnums.ts",
"dump:flos": "bun run tools/cache/dump/dumpFlos.ts",
"dump:flus": "bun run tools/cache/dump/dumpFlus.ts",
"dump:mesanim": "bun run tools/cache/dump/dumpMesanims.ts",
"dump:invs": "bun run tools/cache/dump/dumpInvs.ts",
"dump:objs": "bun run tools/cache/dump/dumpObjs.ts",
"dump:npcs": "bun run tools/cache/dump/dumpNpcs.ts",
"dump:param": "bun run tools/cache/dump/dumpParams.ts",
"dump:struct": "bun run tools/cache/dump/dumpStructs.ts",
"dump:quickchat": "bun run tools/cache/dump/dumpQuickChat.ts --archive 24 && bun run tools/cache/dump/dumpQuickChat.ts --archive 25",
"decode:group": "bun run tools/cache/debug/decodeGroup.ts --archive 2 --group 21",
"dump:hashes": "bun run tools/cache/debug/dumpJs5NameHashes.ts",
"match:hashes": "bun run tools/cache/debug/matchJs5Hashes.ts",
"pack:quickchat": "bun run tools/cache/pack/packQuickChat.ts --archive 24 --mode server && bun run tools/cache/pack/packQuickChat.ts --archive 24 --mode client --exact && bun run tools/cache/pack/packQuickChat.ts --archive 25 --mode server && bun run tools/cache/pack/packQuickChat.ts --archive 25 --mode client --exact",
"pack:flos": "bun run tools/cache/pack/packFlos.ts",
"pack:flus": "bun run tools/cache/pack/packFlus.ts",
"pack:mesanim": "bun run tools/cache/pack/packMesanims.ts",
"pack:invs": "bun run tools/cache/pack/packInvs.ts",
"pack:objs": "bun run tools/cache/pack/packObjs.ts",
"pack:npcs": "bun run tools/cache/pack/packNpcs.ts",
"pack:param": "bun run tools/cache/pack/packParams.ts",
"pack:struct": "bun run tools/cache/pack/packStructs.ts",
"pack:enums": "bun run tools/cache/pack/packEnums.ts --mode server --no-exact && bun run tools/cache/pack/packJs5Archive.ts --archive 17 --groups-dir data/cache --out data/pack/client/client.enum.config.js5",
"pack:midis": "bun run tools/cache/pack/packMidis.ts",
"pack:patches": "bun run tools/cache/pack/packPatches.ts",
"unpack:midis": "bun run tools/unpack/UnpackMidi.ts",
"unpack:patches": "bun run tools/unpack/UnpackPatch.ts",
"gen:midi:pack": "bun run tools/pack/generators/generateMidiPack.ts",
"gen:patch:pack": "bun run tools/pack/generators/generatePatchPack.ts",
"gen:enum:pack": "bun run tools/pack/generators/generateEnumPack.ts",
"gen:flo:pack": "bun run tools/pack/generators/generateFloPack.ts",
"gen:flu:pack": "bun run tools/pack/generators/generateFluPack.ts",
"gen:mesanim:pack": "bun run tools/pack/generators/generateMesanimPack.ts",
"gen:inv:pack": "bun run tools/pack/generators/generateInvPack.ts",
"gen:obj:pack": "bun run tools/pack/generators/generateObjPack.ts",
"gen:npc:pack": "bun run tools/pack/generators/generateNpcPack.ts",
"gen:param:pack": "bun run tools/pack/generators/generateParamPack.ts",
"gen:struct:pack": "bun run tools/pack/generators/generateStructPack.ts",
"gen:chatcat:pack": "bun run tools/pack/generators/generateChatCatPack.ts",
"gen:chatphrase:pack": "bun run tools/pack/generators/generateChatPhrasePack.ts",
"compare:groups": "bun run tools/cache/compare/compareGroups.ts",
"verify:js5:compressed": "bun run tools/cache/verify/verifyJs5Compressed.ts",
"group:filecount": "bun run tools/cache/debug/groupFileCount.ts",
"friend": "bun run src/friend.ts",
"lint": "eslint --no-warn-ignored src tools",
"lint:staged": "eslint --no-warn-ignored",
Expand All @@ -37,6 +80,7 @@
"@inquirer/prompts": "^7.2.3",
"@jimp/js-png": "^1.6.0",
"@jimp/plugin-quantize": "^1.6.0",
"@lostcityrs/runescript": "0.9.2",
"axios": "^1.13.1",
"bcrypt": "^5.1.1",
"dotenv": "^16.4.7",
Expand All @@ -48,6 +92,7 @@
"mysql2": "^3.12.0",
"node-forge": "^1.3.1",
"prom-client": "^15.1.3",
"pako": "^2.1.0",
"ws": "^8.18.2"
},
"devDependencies": {
Expand All @@ -57,6 +102,7 @@
"@types/ejs": "^3.1.5",
"@types/node": "^22.10.2",
"@types/node-forge": "^1.3.11",
"@types/pako": "^2.0.3",
"@types/ws": "^8.18.1",
"eslint": "^9.27.0",
"globals": "^16.1.0",
Expand Down
79 changes: 77 additions & 2 deletions src/cache/config/EnumType.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,84 @@
import fs from 'fs';
import path from 'path';

import { ConfigType } from '#/cache/config/ConfigType.js';
import ScriptVarType from '#/cache/config/ScriptVarType.js';
import Packet from '#/io/Packet.js';
import { unpackJs5Group } from '#/io/Js5Group.js';
import Js5PackReader from '#/io/Js5PackReader.js';
import { parseJs5ArchiveIndexFromPack, splitGroupFiles } from '#/io/Js5ArchiveIndex.js';

export default class EnumType extends ConfigType {
static configNames = new Map<string, number>();
static configs: EnumType[] = [];

static load(_dir: string) {
static load(dir: string) {
const js5Path = path.join(dir, 'server.enum.config.js5');
if (fs.existsSync(js5Path)) {
this.loadFromJs5(js5Path);
return;
}

const candidates = [path.join(dir, 'server', 'enum.dat'), path.join(dir, 'enum.dat')];

let datPath: string | null = null;
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
datPath = candidate;
break;
}
}

if (!datPath) {
return;
}

const bytes = new Uint8Array(fs.readFileSync(datPath));
const unpacked = unpackJs5Group(bytes);
this.parse(new Packet(unpacked));
}

private static loadFromJs5(js5Path: string): void {
const js5Data = new Uint8Array(fs.readFileSync(js5Path));
const indexInfo = parseJs5ArchiveIndexFromPack(js5Data);
const pack = new Js5PackReader(js5Data);

EnumType.configNames = new Map();
EnumType.configs = [];

for (const groupId of indexInfo.groupIds) {
const packedGroup = pack.getGroup(groupId);
if (!packedGroup || packedGroup.length === 0) {
continue;
}

const fileIds = indexInfo.fileIdsByGroup.get(groupId);
if (!fileIds || fileIds.length === 0) {
continue;
}

const groupData = unpackJs5Group(packedGroup);
const files = splitGroupFiles(groupData, fileIds);

for (const fileId of fileIds) {
const fileData = files.get(fileId);
if (!fileData) {
continue;
}

const id = (groupId << 8) | fileId;
const config = new EnumType(id);
config.decodeType(new Packet(fileData));
EnumType.configs[id] = config;

if (config.debugname) {
EnumType.configNames.set(config.debugname, id);
}
}
}
}


static parse(dat: Packet) {
EnumType.configNames = new Map();
EnumType.configs = [];
Expand Down Expand Up @@ -49,12 +119,15 @@ export default class EnumType extends ConfigType {
}

// ----
// server-side
inputtype = ScriptVarType.INT;
outputtype = ScriptVarType.INT;
defaultInt: number = 0;
defaultString: string = 'null';
values = new Map<number, number | string>();

// Flags to track if defaults were explicitly encoded (even if value is 0/null)
hasExplicitDefaultInt = false;
hasExplicitDefaultString = false;

decode(code: number, dat: Packet): void {
if (code === 1) {
Expand All @@ -63,8 +136,10 @@ export default class EnumType extends ConfigType {
this.outputtype = dat.g1();
} else if (code === 3) {
this.defaultString = dat.gjstr();
this.hasExplicitDefaultString = true;
} else if (code === 4) {
this.defaultInt = dat.g4s();
this.hasExplicitDefaultInt = true;
} else if (code === 5) {
const count = dat.g2();

Expand Down
58 changes: 33 additions & 25 deletions src/cache/config/FloType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,6 @@ export default class FloType extends ConfigType {
}
}

static loadJag(config: Jagfile) {
FloType.configNames = new Map();
FloType.configs = [];

const client = config.read('flo.dat')!;
const count = client.g2();

for (let id = 0; id < count; id++) {
const config = new FloType(id);
config.decodeType(client);

FloType.configs[id] = config;

if (config.debugname) {
FloType.configNames.set(config.debugname, id);
}
}
}

static get(id: number): FloType {
return FloType.configs[id];
}
Expand All @@ -69,22 +50,49 @@ export default class FloType extends ConfigType {

// ----

rgb: number = 0;
texture: number = -1;
overlay: boolean = false;
colour: number = 0;
material: number = -1;
occlude: boolean = true;
averagecolour: number = -1;
materialscale: number = 512;
hardshadow: boolean = true;
priority: number = 8;
blend: boolean = false;
waterfogcolour: number = 1190717;
waterfogscale: number = 512;
code8: boolean = false; // Client-only opcode marker

decode(code: number, dat: Packet): void {
if (code === 1) {
this.rgb = dat.g3();
this.colour = dat.g3();
} else if (code === 2) {
this.texture = dat.g1();
this.material = dat.g1();
} else if (code === 3) {
this.overlay = true;
this.material = dat.g2();
if (this.material === 65535) {
this.material = -1;
}
} else if (code === 5) {
this.occlude = false;
} else if (code === 6) {
this.debugname = dat.gjstr();
} else if (code === 7) {
this.averagecolour = dat.g3();
} else if (code === 8) {
// Client-only code, we store it only to write the opcode later during packing.
this.code8 = true;
} else if (code === 9) {
this.materialscale = dat.g2();
} else if (code === 10) {
this.hardshadow = false;
} else if (code === 11) {
this.priority = dat.g1();
} else if (code === 12) {
this.blend = true;
} else if (code === 13) {
this.waterfogcolour = dat.g3();
} else if (code === 14) {
this.waterfogscale = dat.g1();
} else {
throw new Error(`Unrecognized flo config code: ${code}`);
}
Expand Down
75 changes: 75 additions & 0 deletions src/cache/config/FluType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { ConfigType } from '#/cache/config/ConfigType.js';
import Jagfile from '#/io/Jagfile.js';
import Packet from '#/io/Packet.js';

export default class FluType extends ConfigType {
static configNames: Map<string, number> = new Map();
static configs: FluType[] = [];

static load(_dir: string) {
}

static parse(server: Packet, jag: Jagfile) {
FluType.configNames = new Map();
FluType.configs = [];

const count = server.g2();

const client = jag.read('flo.dat')!;
client.pos = 2;

for (let id = 0; id < count; id++) {
const config = new FluType(id);
config.decodeType(server);
config.decodeType(client);

FluType.configs[id] = config;

if (config.debugname) {
FluType.configNames.set(config.debugname, id);
}
}
}

static get(id: number): FluType {
return FluType.configs[id];
}

static getId(name: string): number {
return FluType.configNames.get(name) ?? -1;
}

static getByName(name: string): FluType | null {
const id = this.getId(name);
if (id === -1) {
return null;
}

return this.get(id);
}

// ----

colour: number = 0;
material: number = -1;
materialscale: number = 512;
hardshadow: boolean = true;


decode(code: number, dat: Packet): void {
if (code === 1) {
this.colour = dat.g3();
} else if (code === 2) {
this.material = dat.g2();
if (this.material === 65535) {
this.material = -1;
}
} else if (code === 3) {
this.materialscale = dat.g2();
} else if (code === 4) {
this.hardshadow = false;
} else {
throw new Error(`Unrecognized flo config code: ${code}`);
}
}
}
27 changes: 21 additions & 6 deletions src/cache/config/IdkType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ export default class IdkType extends ConfigType {
type: number = -1;
models: Uint16Array | null = null;
heads: Uint16Array = new Uint16Array(5).fill(-1);
recol_s: Uint16Array = new Uint16Array(6).fill(0);
recol_d: Uint16Array = new Uint16Array(6).fill(0);
recol_s: Uint16Array | null = null;
recol_d: Uint16Array | null = null;
retex_s: Uint16Array | null = null;
retex_d: Uint16Array | null = null;
disable: boolean = false;

decode(code: number, dat: Packet): void {
Expand All @@ -72,10 +74,23 @@ export default class IdkType extends ConfigType {
}
} else if (code === 3) {
this.disable = true;
} else if (code >= 40 && code < 50) {
this.recol_s[code - 40] = dat.g2();
} else if (code >= 50 && code < 60) {
this.recol_d[code - 50] = dat.g2();
} else if (code === 40) {
const count = dat.g1();
this.recol_s = new Uint16Array(count);
this.recol_d = new Uint16Array(count);
for (let i = 0; i < count; i++) {
this.recol_s[i] = dat.g2();
this.recol_d[i] = dat.g2();
}

} else if (code === 41) {
const count = dat.g1();
this.retex_s = new Uint16Array(count);
this.retex_d = new Uint16Array(count);
for (let i = 0; i < count; i++) {
this.retex_s[i] = dat.g2();
this.retex_d[i] = dat.g2();
}
} else if (code >= 60 && code < 70) {
this.heads[code - 60] = dat.g2();
} else if (code === 250) {
Expand Down
Loading
Loading