mirror of
https://github.com/bdnugget/rs-sdk.git
synced 2026-11-07 09:07:30 +00:00
Updates all three vendored upstream repos (Engine-TS, Content, Client-TS) to the 274 game snapshot with all rs-sdk modifications re-applied via a 3-way merge (254 base + local mods, replayed onto upstream 274). ## Engine - Bump @2004scape/rsbuf 254.1.0 -> 274.1.0 (keep rsmod-pathfinder fork pin) - Adapt to 274's nested WorldConfig: Environment.ts now exposes flat back-compat aliases (WEB_PORT, NODE_ID, ...) plus the rs-sdk-only keys the bot framework uses, and createDefaultWorldConfig carries our defaults (xpRate 25, web.port 8888) - Collision export (web/pages/api.ts) reads 274's in-engine TS routefinder instead of the dropped @2004scape/rsmod-pathfinder WASM, which the engine no longer feeds - Remove dead GameMap.findLongPath (274 routefinder has no long-path variant; the SDK does long-distance pathfinding client-side) - Re-apply security/login mods (sdk_auth, IP login tracking, login-takeover, banned usernames, no dev staffmodlevel on non-prod) onto 274's auth refactor - Add MESSAGE_PUBLIC server packet at the free opcode 255 (254's 81 is now OBJ_ADD) - NODE_WS_ONDEMAND now defaults on: the 274 web client streams on-demand assets over the game WebSocket and otherwise stalls at ~60% on load ## Content - Full 254->274 content update with our 4 script/config patches re-applied - worldmap.jag regenerated for /mapview from the 274 maps (via engine build) ## Webclient - Rebuild standard + bot + viewer bundles; CLIENT_VERSION 274 - ServerProt: keep 274 opcodes, add MESSAGE_PUBLIC=255 with -1 size table entry - Replace removed Packet.pIsaac() calls with 274's p1Enc() - Disable terser property mangling (274 enabled it): the bot framework accesses client members by name from bot.ejs / the Puppeteer harness ## SDK - Regenerate sdk/collision-data.json from 274 maps (905k -> 958k tiles) ## start.ts - Add 274 to the version menu Verified end-to-end: engine boots and loads 274 maps, content packs, engine typechecks clean, webclient builds, a headless bot logs in, sdk.getState() returns a complete snapshot, and bot.walkTo() pathfinds/moves on the regenerated collision data. Map and hiscores sprites serve correctly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
295 lines
8.4 KiB
TypeScript
295 lines
8.4 KiB
TypeScript
import child_process from 'child_process';
|
|
import fs from 'fs';
|
|
|
|
import { ExitPromptError } from '@inquirer/core';
|
|
import { confirm, input, number, password, select } from '@inquirer/prompts';
|
|
|
|
// Ensure all relative paths resolve from this script's directory,
|
|
// regardless of where it's invoked from (e.g. `bun server/start.ts`).
|
|
process.chdir(import.meta.dir);
|
|
|
|
// if you're forking this feel free to change these :) it does make some assumptions elsewhere (branch names)
|
|
const repoOrg = 'https://github.com/LostCityRS';
|
|
const engineRepo = 'Engine-TS';
|
|
const contentRepo = 'Content';
|
|
const webRepo = 'Client-TS';
|
|
const javaRepo = 'Client-Java';
|
|
|
|
function cloneRepo(repo: string, dir: string, branch: string) {
|
|
child_process.execSync(`git clone ${repoOrg}/${repo} --single-branch -b ${branch} ${dir}`, {
|
|
stdio: 'inherit'
|
|
});
|
|
}
|
|
|
|
function updateRepo(cwd: string) {
|
|
child_process.execSync('git pull', {
|
|
stdio: 'inherit',
|
|
cwd
|
|
});
|
|
}
|
|
|
|
async function runOnOs(url: string) {
|
|
try {
|
|
const open = (await import('open')).default;
|
|
await open(url);
|
|
} catch (e) {
|
|
console.warn(`Could not open browser automatically.`);
|
|
console.warn(`Open this URL manually: ${url}`);
|
|
}
|
|
}
|
|
|
|
let config = {
|
|
rev: 'unset'
|
|
};
|
|
|
|
type RevInfo = {
|
|
description: string;
|
|
webclient?: boolean;
|
|
wip?: boolean;
|
|
}
|
|
|
|
const revInfo: Record<string, RevInfo> = {
|
|
'225': {
|
|
description: 'May 18, 2004',
|
|
webclient: true
|
|
},
|
|
'244': {
|
|
description: 'June 28, 2004',
|
|
webclient: true
|
|
},
|
|
'245.2': {
|
|
description: 'July 13, 2004 (there were 3 "245" builds!)',
|
|
webclient: true
|
|
},
|
|
'254': {
|
|
description: 'September 7, 2004',
|
|
webclient: true
|
|
},
|
|
'274': {
|
|
description: 'November 2, 2004',
|
|
wip: true
|
|
}
|
|
};
|
|
|
|
let running = true;
|
|
async function main() {
|
|
if (!fs.existsSync('server.json')) {
|
|
await promptConfig();
|
|
}
|
|
|
|
config = JSON.parse(fs.readFileSync('server.json', 'utf8'));
|
|
|
|
if (!fs.existsSync('engine')) {
|
|
cloneRepo(engineRepo, 'engine', config.rev);
|
|
}
|
|
|
|
if (!fs.existsSync('content')) {
|
|
cloneRepo(contentRepo, 'content', config.rev);
|
|
}
|
|
|
|
if (revInfo[config.rev]?.webclient && !fs.existsSync('webclient')) {
|
|
cloneRepo(webRepo, 'webclient', config.rev);
|
|
}
|
|
|
|
if (!fs.existsSync('javaclient')) {
|
|
cloneRepo(javaRepo, 'javaclient', config.rev);
|
|
}
|
|
|
|
if (!fs.existsSync('engine/.env')) {
|
|
child_process.spawnSync('bun install', {
|
|
shell: true,
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
|
|
child_process.spawnSync('bun run setup', {
|
|
shell: true,
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
}
|
|
|
|
const choice = await select({
|
|
message: 'What would you like to do?',
|
|
choices: [{
|
|
name: 'Start Server',
|
|
description: 'Starts the server normally',
|
|
value: 'start'
|
|
}, {
|
|
name: 'Update Source',
|
|
description: 'Pull the latest commits for all subprojects',
|
|
value: 'update'
|
|
}, {
|
|
name: 'Run Web Client',
|
|
description: 'Opens your browser to play using the modern web client (TypeScript)',
|
|
value: 'web'
|
|
}, {
|
|
name: 'Run Java Client',
|
|
description: 'Opens the legacy Java applet to play using the original client',
|
|
value: 'java'
|
|
}, {
|
|
name: 'Advanced Options',
|
|
description: 'View more options',
|
|
value: 'advanced'
|
|
}, {
|
|
name: 'Quit',
|
|
description: '',
|
|
value: 'quit'
|
|
}]
|
|
}, { clearPromptOnDone: true });
|
|
|
|
if (choice === 'start') {
|
|
child_process.execSync('bun start', {
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
} else if (choice === 'update') {
|
|
updateRepo('engine');
|
|
updateRepo('content');
|
|
updateRepo('webclient');
|
|
updateRepo('javaclient');
|
|
} else if (choice === 'web') {
|
|
if (!revInfo[config.rev]?.webclient) {
|
|
console.log('This version does not have a webclient available (yet?), sorry.');
|
|
} else {
|
|
const port = (process.platform === 'win32' || process.platform === 'darwin') ? '' : ':8888';
|
|
await runOnOs(`http://localhost${port}/rs2.cgi`);
|
|
}
|
|
} else if (choice === 'java') {
|
|
const command = process.platform === 'win32' ? 'gradlew' : './gradlew';
|
|
if (config.rev === '225') {
|
|
child_process.execSync(`${command} run --args="10 0 highmem members"`, {
|
|
stdio: 'inherit',
|
|
cwd: 'javaclient'
|
|
});
|
|
} else {
|
|
child_process.execSync(`${command} run --args="10 0 highmem members 32"`, {
|
|
stdio: 'inherit',
|
|
cwd: 'javaclient'
|
|
});
|
|
}
|
|
} else if (choice === 'advanced') {
|
|
await promptAdvanced();
|
|
} else if (choice === 'quit') {
|
|
running = false;
|
|
}
|
|
}
|
|
|
|
async function promptConfig() {
|
|
const orderedRevs = Object.entries(revInfo);
|
|
orderedRevs.sort((a, b) => parseInt(a[0]) - parseInt(b[0])); // descending revs
|
|
orderedRevs.sort((a, b) => a[1].wip ? 1 : -1); // wip last
|
|
|
|
let choices = [];
|
|
for (const [rev, info] of orderedRevs) {
|
|
choices.push({
|
|
name: info.wip ? `${rev} (WIP)` : rev,
|
|
value: rev,
|
|
description: info.description
|
|
});
|
|
}
|
|
|
|
const rev = await select({
|
|
message: 'What version are you interested in?',
|
|
choices
|
|
}, { clearPromptOnDone: true });
|
|
|
|
config.rev = rev;
|
|
|
|
fs.writeFileSync('server.json', JSON.stringify(config, null, 2));
|
|
}
|
|
|
|
async function promptAdvanced() {
|
|
const choice = await select({
|
|
message: 'What would you like to do?',
|
|
choices: [{
|
|
name: 'Start Server (engine dev)',
|
|
description: 'Starts the server and watches for .ts file changes to reload',
|
|
value: 'start-dev'
|
|
}, {
|
|
// todo:
|
|
// name: 'Reconfigure Server',
|
|
// description: 'Edit the environment config for the server',
|
|
// value: 'configure'
|
|
// }, {
|
|
name: 'Clean-build Server',
|
|
description: '',
|
|
value: 'clean-build'
|
|
}, {
|
|
name: 'Build Web Client',
|
|
description: '',
|
|
value: 'build-web'
|
|
}, {
|
|
name: 'Build Java Client',
|
|
description: '',
|
|
value: 'build-java'
|
|
}, {
|
|
name: 'Change Version',
|
|
description: '',
|
|
value: 'change-version'
|
|
}, {
|
|
name: 'Back',
|
|
description: 'Go back',
|
|
value: 'back'
|
|
}]
|
|
}, { clearPromptOnDone: true });
|
|
|
|
if (choice === 'start-dev') {
|
|
child_process.execSync('bun run dev', {
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
} else if (choice === 'configure') {
|
|
// todo: has issues with input appearing right now
|
|
child_process.spawnSync('bun run setup', {
|
|
shell: true,
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
} else if (choice === 'clean-build') {
|
|
child_process.execSync('bun run clean', {
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
|
|
child_process.execSync('bun run build', {
|
|
stdio: 'inherit',
|
|
cwd: 'engine'
|
|
});
|
|
} else if (choice === 'build-web') {
|
|
child_process.execSync('bun run build', {
|
|
stdio: 'inherit',
|
|
cwd: 'webclient'
|
|
});
|
|
} else if (choice === 'build-java') {
|
|
const command = process.platform === 'win32' ? 'gradlew' : './gradlew';
|
|
child_process.execSync(`${command} build`, {
|
|
stdio: 'inherit',
|
|
cwd: 'javaclient'
|
|
});
|
|
} else if (choice === 'change-version') {
|
|
await promptConfig();
|
|
|
|
fs.rmSync('engine', { recursive: true, force: true });
|
|
fs.rmSync('content', { recursive: true, force: true });
|
|
fs.rmSync('webclient', { recursive: true, force: true });
|
|
fs.rmSync('javaclient', { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
try {
|
|
while (running) {
|
|
await main();
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof ExitPromptError) {
|
|
process.exit(0);
|
|
} else if (e instanceof Error) {
|
|
if (e.message.startsWith('Command failed:')) {
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log(e.message);
|
|
}
|
|
}
|