All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m38s
RunicNPC now spawns its own NPC: Rust's scientist with its ScientistNPC and ScientistBrain swapped for ours, copying a fixed field list generated from Rust's unmodified assembly (D232, tools/fieldlist). Profiles live in data/RunicNPC/profiles.json in the approved shape (D238); placements persist with each/group respawn (D236) and wait for a missing profile or route (D237); roamers wander, follow Rust's monument paths, or walk a route (D233, D234), with our own chase where Rust's needs an AI zone; sentries hold their spot; NPCs walk home and sleep past 160 m of any player (D235). Spawns wait for the navmesh and are spread over frames; caps are off by default and the cost warning is shown instead (D227). The whole PLAN.md section 4 API and its hooks are in, documented in docs/runicnpc/API.md. tools/RunicNpcTest.cs is the stage 2 harness; every group passed on both rigs, and after a plugin reload and a server restart. checkPlugin no longer counts an override (RunicNpcPlayer.OnDied) as a hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
50 lines
2.0 KiB
JavaScript
50 lines
2.0 KiB
JavaScript
// Downloads a rig's managed assemblies (RustDedicated_Data/Managed/*.dll) into a local directory,
|
|
// for tools/fieldlist, which reads Rust's own field declarations from them (D232).
|
|
//
|
|
// node tools/managed.js <rig> <out dir>
|
|
//
|
|
// Use the CARBON rig. Oxide's patcher rewrites Assembly-CSharp.dll and makes private fields public,
|
|
// so the Oxide rig's copy no longer says which fields Rust itself serialises. Carbon leaves the
|
|
// assembly as Facepunch shipped it.
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
const { load, serverId, unmsys } = require('./panel')
|
|
|
|
const config = load()
|
|
const [rig, out] = process.argv.slice(2).map(unmsys)
|
|
if (!rig || !out) {
|
|
console.error('usage: node tools/managed.js <rig> <out dir>')
|
|
process.exit(2)
|
|
}
|
|
|
|
const base = `${config.panel}/api/client/servers/${serverId(config, rig)}`
|
|
const headers = { Authorization: 'Bearer ' + config.key, Accept: 'application/json' }
|
|
const dir = '/RustDedicated_Data/Managed'
|
|
|
|
async function main() {
|
|
const list = await fetch(`${base}/files/list?directory=${encodeURIComponent(dir)}`, { headers })
|
|
if (!list.ok) throw new Error(`list ${list.status}: ${await list.text()}`)
|
|
const files = (await list.json()).data
|
|
.map((f) => f.attributes)
|
|
.filter((f) => f.is_file && f.name.endsWith('.dll'))
|
|
|
|
fs.mkdirSync(out, { recursive: true })
|
|
let bytes = 0
|
|
for (const f of files) {
|
|
const target = path.join(out, f.name)
|
|
if (fs.existsSync(target) && fs.statSync(target).size === f.size) continue
|
|
const link = await fetch(`${base}/files/download?file=${encodeURIComponent(`${dir}/${f.name}`)}`, { headers })
|
|
if (!link.ok) throw new Error(`${f.name}: ${link.status} ${await link.text()}`)
|
|
const body = await fetch((await link.json()).attributes.url)
|
|
fs.writeFileSync(target, Buffer.from(await body.arrayBuffer()))
|
|
bytes += f.size
|
|
}
|
|
console.log(`${files.length} assemblies in ${out} (${(bytes / 1e6).toFixed(1)} MB downloaded)`)
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e.message || e)
|
|
process.exit(1)
|
|
})
|