mirror of
https://github.com/bdnugget/rs-sdk.git
synced 2026-11-07 09:07:30 +00:00
remove all the special long path pathfinding, just use a larger search grid in the rust
This commit is contained in:
1 parent
99f6b8cb5b
commit
019340b12b
11 files changed
+727
-188
No files matched your search
+1
-34
@@ -4,7 +4,7 @@
|
||||
|
||||
import { BotSDK } from './index';
|
||||
import { ActionHelpers } from './actions-helpers';
|
||||
import { isInsideDraynorManor, getDraynorManorEscape, findDoorsAlongPath } from './pathfinding';
|
||||
import { findDoorsAlongPath } from './pathfinding';
|
||||
import type {
|
||||
ActionResult,
|
||||
SkillState,
|
||||
@@ -728,39 +728,6 @@ export class BotActions {
|
||||
const state = this.sdk.getState();
|
||||
if (!state?.player) return { success: false, message: 'No player state' };
|
||||
|
||||
let pos = { x: state.player.worldX, z: state.player.worldZ };
|
||||
const distTo = (p: { x: number; z: number }) => this.helpers.distance(p.x, p.z, x, z);
|
||||
|
||||
if (distTo(pos) <= tolerance) {
|
||||
return { success: true, message: 'Already at destination' };
|
||||
}
|
||||
|
||||
// Draynor Manor trap: the front door only opens from outside.
|
||||
// If we're inside the manor and the destination is outside, escape
|
||||
// via the east wing doors first, then continue to the real destination.
|
||||
if (isInsideDraynorManor(pos.x, pos.z, state.player.level) &&
|
||||
!isInsideDraynorManor(x, z)) {
|
||||
const escape = getDraynorManorEscape();
|
||||
console.log(`[walkTo] Inside Draynor Manor — escaping via east wing to (${escape.x}, ${escape.z})`);
|
||||
const escapeResult = await this._walkToInternal(escape.x, escape.z, 3);
|
||||
if (!escapeResult.success) {
|
||||
return { success: false, message: `Trapped in Draynor Manor: ${escapeResult.message}` };
|
||||
}
|
||||
const afterEscape = this.sdk.getState()?.player;
|
||||
if (afterEscape) pos = { x: afterEscape.worldX, z: afterEscape.worldZ };
|
||||
if (distTo(pos) <= tolerance) {
|
||||
return { success: true, message: 'Arrived' };
|
||||
}
|
||||
}
|
||||
|
||||
return this._walkToInternal(x, z, tolerance);
|
||||
}
|
||||
|
||||
/** Core walk loop — used by walkTo and Draynor Manor escape. */
|
||||
private async _walkToInternal(x: number, z: number, tolerance: number = 3): Promise<ActionResult> {
|
||||
const state = this.sdk.getState();
|
||||
if (!state?.player) return { success: false, message: 'No player state' };
|
||||
|
||||
const distTo = (pos: { x: number; z: number }) => this.helpers.distance(pos.x, pos.z, x, z);
|
||||
let pos = { x: state.player.worldX, z: state.player.worldZ };
|
||||
|
||||
|
||||
+21
-147
@@ -27,43 +27,17 @@ const doorIndex = new Map<string, DoorInfo>();
|
||||
// are likely open ocean/void and should not be treated as walkable land.
|
||||
const populatedZones = new Set<string>();
|
||||
|
||||
// One-way doors that should NOT be masked or included in the door index.
|
||||
// One-way doors that should NOT be unmasked in the door index.
|
||||
// These doors can only be opened from one side; routing through them traps the bot.
|
||||
const ONE_WAY_DOORS = new Set<string>([
|
||||
'0,3108,3353', // Draynor Manor front door (west tile) — only opens from outside
|
||||
'0,3109,3353', // Draynor Manor front door (east tile) — only opens from outside
|
||||
]);
|
||||
|
||||
// ── Draynor Manor directional door handling ──
|
||||
// The front door at (3108-3109, 3353) can only be opened from one side.
|
||||
// If the bot is inside the manor it must exit via the east wing escape doors.
|
||||
const DRAYNOR_MANOR = {
|
||||
// Interior bounding box (level 0) — covers the main building and courtyard.
|
||||
// Everything within the manor walls past the front door is trapped.
|
||||
minX: 3097, maxX: 3119,
|
||||
minZ: 3354, maxZ: 3374,
|
||||
// Front door position (two tiles wide)
|
||||
frontDoor: [{ x: 3108, z: 3353 }, { x: 3109, z: 3353 }],
|
||||
// East wing escape route: walk here to get out
|
||||
escapeExit: { x: 3125, z: 3370 },
|
||||
};
|
||||
|
||||
function doorKey(level: number, x: number, z: number): string {
|
||||
return `${level},${x},${z}`;
|
||||
}
|
||||
|
||||
/** Check if a position is inside Draynor Manor's ground floor interior. */
|
||||
export function isInsideDraynorManor(x: number, z: number, level: number = 0): boolean {
|
||||
if (level !== 0) return false;
|
||||
return x >= DRAYNOR_MANOR.minX && x <= DRAYNOR_MANOR.maxX &&
|
||||
z >= DRAYNOR_MANOR.minZ && z <= DRAYNOR_MANOR.maxZ;
|
||||
}
|
||||
|
||||
/** Get the Draynor Manor escape exit coordinates (outside the manor, east courtyard). */
|
||||
export function getDraynorManorEscape(): { x: number; z: number } {
|
||||
return { ...DRAYNOR_MANOR.escapeExit };
|
||||
}
|
||||
|
||||
export function initPathfinding(): void {
|
||||
if (initialized) return;
|
||||
|
||||
@@ -75,6 +49,21 @@ export function initPathfinding(): void {
|
||||
rsmod.allocateIfAbsent(zoneX, zoneZ, level);
|
||||
}
|
||||
|
||||
// Allocate mainland zones so the 2048x2048 BFS grid can traverse
|
||||
// open land between cities. Unallocated zones return NULL (blocked),
|
||||
// so without this the pathfinder can't cross gaps in the collision data.
|
||||
// Newly-allocated zones default to OPEN (walkable), which is correct
|
||||
// for grassland/roads. Zones already allocated above keep their flags.
|
||||
let mainlandZones = 0;
|
||||
for (let x = 2304; x <= 3392; x += 8) {
|
||||
for (let z = 2944; z <= 3584; z += 8) {
|
||||
if (!rsmod.isZoneAllocated(x, z, 0)) {
|
||||
rsmod.allocateIfAbsent(x, z, 0);
|
||||
mainlandZones++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set collision flags for tiles that have them (includes wall flags)
|
||||
for (const [level, x, z, flags] of data.tiles) {
|
||||
rsmod.__set(x, z, level, flags);
|
||||
@@ -108,7 +97,7 @@ export function initPathfinding(): void {
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
console.log(`Pathfinding initialized in ${Date.now() - start}ms (${data.zones.length} zones, ${data.tiles.length} tiles, ${doorCount} doors masked, ${skippedOneWay} one-way doors blocked)`);
|
||||
console.log(`Pathfinding initialized in ${Date.now() - start}ms (${data.zones.length} zones + ${mainlandZones} mainland fill, ${data.tiles.length} tiles, ${doorCount} doors masked, ${skippedOneWay} one-way doors blocked)`);
|
||||
}
|
||||
|
||||
// Check if a zone has collision data
|
||||
@@ -139,7 +128,7 @@ export function findPath(
|
||||
return unpackWaypoints(waypointsRaw);
|
||||
}
|
||||
|
||||
// Find long-distance path (512x512 search grid)
|
||||
// Find long-distance path (2048x2048 search grid, ±1024 tile reach)
|
||||
export function findLongPath(
|
||||
level: number,
|
||||
srcX: number,
|
||||
@@ -185,132 +174,17 @@ export function isZoneLikelyLand(level: number, x: number, z: number): boolean {
|
||||
return populatedZones.has(`${level},${x & ~7},${z & ~7}`);
|
||||
}
|
||||
|
||||
/** Maximum single-segment distance (conservative; 512/2=256 grid half, minus routing headroom). */
|
||||
const MAX_SINGLE_SEGMENT = 200;
|
||||
|
||||
/**
|
||||
* Find the nearest walkable tile to (x, z) by spiraling outward.
|
||||
* Returns null if no walkable tile found within maxRadius.
|
||||
*/
|
||||
function snapToWalkable(
|
||||
level: number, x: number, z: number, maxRadius: number = 30
|
||||
): { x: number; z: number } | null {
|
||||
if (!initialized) initPathfinding();
|
||||
if (isTileWalkable(level, x, z) && isZoneAllocated(level, x, z) && isZoneLikelyLand(level, x, z)) {
|
||||
return { x, z };
|
||||
}
|
||||
for (let r = 1; r <= maxRadius; r++) {
|
||||
for (let dx = -r; dx <= r; dx++) {
|
||||
for (let dz = -r; dz <= r; dz++) {
|
||||
if (Math.abs(dx) !== r && Math.abs(dz) !== r) continue; // perimeter only
|
||||
const tx = x + dx;
|
||||
const tz = z + dz;
|
||||
if (isTileWalkable(level, tx, tz) && isZoneAllocated(level, tx, tz) && isZoneLikelyLand(level, tx, tz)) {
|
||||
return { x: tx, z: tz };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a path between two points that may be farther apart than the 512x512
|
||||
* pathfinder grid allows. Recursively bisects the route into segments that
|
||||
* each fit within findLongPath's range, then concatenates the results.
|
||||
*
|
||||
* When a direct split fails (e.g. coastline blocks the midpoint), offset
|
||||
* candidates perpendicular to the travel axis are tried so the route can
|
||||
* swing around geographic obstacles.
|
||||
* Find a path between two distant points.
|
||||
* With the 2048x2048 pathfinder grid (±1024 tile reach), this is now
|
||||
* just a direct call to findLongPath for any in-game distance.
|
||||
*/
|
||||
export function findMultiSegmentPath(
|
||||
level: number,
|
||||
srcX: number, srcZ: number,
|
||||
destX: number, destZ: number,
|
||||
maxWaypoints: number = 500,
|
||||
_depth: number = 0
|
||||
): Array<{ x: number; z: number; level: number }> {
|
||||
if (!initialized) initPathfinding();
|
||||
|
||||
const dx = Math.abs(destX - srcX);
|
||||
const dz = Math.abs(destZ - srcZ);
|
||||
|
||||
// Base case: within single pathfinder range
|
||||
if (dx <= MAX_SINGLE_SEGMENT && dz <= MAX_SINGLE_SEGMENT) {
|
||||
return findLongPath(level, srcX, srcZ, destX, destZ, maxWaypoints);
|
||||
}
|
||||
|
||||
// Guard against excessive recursion
|
||||
if (_depth > 6) {
|
||||
return findLongPath(level, srcX, srcZ, destX, destZ, maxWaypoints);
|
||||
}
|
||||
|
||||
// Build candidate split points: midpoint + 1/3 + 2/3 along the line,
|
||||
// plus perpendicular offsets at each to route around coastline/mountains.
|
||||
const midX = Math.round((srcX + destX) / 2);
|
||||
const midZ = Math.round((srcZ + destZ) / 2);
|
||||
const thirdX = Math.round(srcX + (destX - srcX) / 3);
|
||||
const thirdZ = Math.round(srcZ + (destZ - srcZ) / 3);
|
||||
const twoThirdX = Math.round(srcX + 2 * (destX - srcX) / 3);
|
||||
const twoThirdZ = Math.round(srcZ + 2 * (destZ - srcZ) / 3);
|
||||
|
||||
// Perpendicular offset: rotate the travel vector 90 degrees
|
||||
const travelDx = destX - srcX;
|
||||
const travelDz = destZ - srcZ;
|
||||
const travelLen = Math.sqrt(travelDx * travelDx + travelDz * travelDz);
|
||||
// Perpendicular unit vector (rotated 90 degrees)
|
||||
const perpX = -travelDz / travelLen;
|
||||
const perpZ = travelDx / travelLen;
|
||||
|
||||
const splitPoints: Array<{ x: number; z: number }> = [
|
||||
// Direct line candidates
|
||||
{ x: midX, z: midZ },
|
||||
{ x: thirdX, z: thirdZ },
|
||||
{ x: twoThirdX, z: twoThirdZ },
|
||||
];
|
||||
|
||||
// Perpendicular offsets at increasing distances to swing around obstacles
|
||||
const perpOffsets = [80, 160, Math.round(travelLen * 0.4), Math.round(travelLen * 0.6)];
|
||||
for (const offset of perpOffsets) {
|
||||
splitPoints.push(
|
||||
{ x: Math.round(midX + perpX * offset), z: Math.round(midZ + perpZ * offset) },
|
||||
{ x: Math.round(midX - perpX * offset), z: Math.round(midZ - perpZ * offset) },
|
||||
);
|
||||
}
|
||||
|
||||
let bestPartial: Array<{ x: number; z: number; level: number }> = [];
|
||||
|
||||
for (const raw of splitPoints) {
|
||||
const snapped = snapToWalkable(level, raw.x, raw.z);
|
||||
if (!snapped) continue;
|
||||
|
||||
const firstHalf = findMultiSegmentPath(level, srcX, srcZ, snapped.x, snapped.z, maxWaypoints, _depth + 1);
|
||||
if (firstHalf.length === 0) continue;
|
||||
|
||||
// Verify the first half actually reached the split point (within tolerance).
|
||||
const lastWp = firstHalf[firstHalf.length - 1]!;
|
||||
const reachDist = Math.abs(lastWp.x - snapped.x) + Math.abs(lastWp.z - snapped.z);
|
||||
if (reachDist > 15) {
|
||||
if (firstHalf.length > bestPartial.length) {
|
||||
bestPartial = firstHalf;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const secondHalf = findMultiSegmentPath(level, lastWp.x, lastWp.z, destX, destZ, maxWaypoints, _depth + 1);
|
||||
if (secondHalf.length === 0) {
|
||||
if (firstHalf.length > bestPartial.length) {
|
||||
bestPartial = firstHalf;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Concatenate, removing duplicate at junction
|
||||
return firstHalf.concat(secondHalf.slice(1));
|
||||
}
|
||||
|
||||
// Return best partial result, or direct long path as fallback
|
||||
if (bestPartial.length > 0) return bestPartial;
|
||||
return findLongPath(level, srcX, srcZ, destX, destZ, maxWaypoints);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Walk Test: Lumbridge to Yanille
|
||||
* Tests very long-distance pathfinding across the map.
|
||||
* Route: Lumbridge -> Draynor -> Rimmington -> Ardougne -> Yanille
|
||||
*/
|
||||
|
||||
import { runTest } from './utils/test-runner';
|
||||
import { Locations } from './utils/save-generator';
|
||||
|
||||
const YANILLE = { x: 2606, z: 3093 };
|
||||
|
||||
runTest({
|
||||
name: 'Lumbridge to Yanille',
|
||||
saveConfig: {
|
||||
position: Locations.LUMBRIDGE_CASTLE,
|
||||
skills: { Agility: 99 },
|
||||
varps: { 281: 1000 },
|
||||
},
|
||||
launchOptions: { skipTutorial: false },
|
||||
}, async ({ sdk, bot }) => {
|
||||
await sdk.waitForCondition(s => (s?.player?.worldX ?? 0) > 0, 10000);
|
||||
|
||||
const pos = () => {
|
||||
const s = sdk.getState()?.player;
|
||||
return { x: s?.worldX ?? 0, z: s?.worldZ ?? 0 };
|
||||
};
|
||||
|
||||
const distTo = (x: number, z: number) => {
|
||||
const p = pos();
|
||||
return Math.sqrt((p.x - x) ** 2 + (p.z - z) ** 2);
|
||||
};
|
||||
|
||||
const start = pos();
|
||||
console.log(`Start: (${start.x}, ${start.z}) - Lumbridge`);
|
||||
console.log(`Target: (${YANILLE.x}, ${YANILLE.z}) - Yanille`);
|
||||
console.log(`Distance: ${distTo(YANILLE.x, YANILLE.z).toFixed(0)} tiles\n`);
|
||||
|
||||
const t0 = Date.now();
|
||||
|
||||
// Walk to Yanille - this is a very long route
|
||||
const result = await bot.walkTo(YANILLE.x, YANILLE.z, 15);
|
||||
|
||||
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
||||
const final = pos();
|
||||
const dist = distTo(YANILLE.x, YANILLE.z);
|
||||
|
||||
console.log(`\n=== RESULT ===`);
|
||||
console.log(`walkTo success: ${result.success}`);
|
||||
console.log(`Message: ${result.message}`);
|
||||
console.log(`Final position: (${final.x}, ${final.z})`);
|
||||
console.log(`Distance from target: ${dist.toFixed(1)} tiles`);
|
||||
console.log(`Time: ${elapsed}s`);
|
||||
|
||||
const passed = result.success && dist <= 15;
|
||||
console.log(`Verdict: ${passed ? 'PASS' : 'FAIL'}`);
|
||||
|
||||
return passed;
|
||||
});
|
||||
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env bun
|
||||
// Local pathfinding validation - no server or bot required
|
||||
// Tests that the collision data + door masking produces correct paths
|
||||
|
||||
import { findLongPath, findMultiSegmentPath, findDoorsAlongPath, initPathfinding, isTileWalkable, isFlagged, isZoneLikelyLand, isZoneAllocated } from '../pathfinding';
|
||||
import { CollisionFlag } from '../../server/vendor/rsmod-pathfinder';
|
||||
|
||||
console.log('Initializing pathfinding...');
|
||||
initPathfinding();
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
interface TimingEntry {
|
||||
label: string;
|
||||
method: 'findLongPath' | 'findMultiSegmentPath';
|
||||
ms: number;
|
||||
straightLineDist: number;
|
||||
waypoints: number;
|
||||
reached: boolean;
|
||||
}
|
||||
|
||||
const timings: TimingEntry[] = [];
|
||||
|
||||
function test(
|
||||
label: string,
|
||||
srcX: number, srcZ: number,
|
||||
destX: number, destZ: number,
|
||||
opts: {
|
||||
expectPath?: boolean; // expect a non-empty path (default true)
|
||||
expectReach?: boolean; // expect path reaches destination (default true)
|
||||
maxDist?: number; // max distance of last waypoint from dest (default 5)
|
||||
forbiddenZone?: { minX: number; maxX: number; minZ: number; maxZ: number }; // path must not cross this zone
|
||||
} = {}
|
||||
) {
|
||||
const expectPath = opts.expectPath ?? true;
|
||||
const expectReach = opts.expectReach ?? true;
|
||||
const maxDist = opts.maxDist ?? 5;
|
||||
const straightLineDist = Math.sqrt((destX - srcX) ** 2 + (destZ - srcZ) ** 2);
|
||||
|
||||
const t0 = performance.now();
|
||||
const path = findLongPath(0, srcX, srcZ, destX, destZ);
|
||||
const ms = performance.now() - t0;
|
||||
|
||||
if (!expectPath) {
|
||||
timings.push({ label, method: 'findLongPath', ms, straightLineDist, waypoints: path.length, reached: path.length === 0 });
|
||||
if (path.length === 0) {
|
||||
console.log(` PASS: ${label} (no path as expected) [${ms.toFixed(1)}ms]`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` FAIL: ${label} (expected no path but got ${path.length} waypoints) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.length === 0) {
|
||||
timings.push({ label, method: 'findLongPath', ms, straightLineDist, waypoints: 0, reached: false });
|
||||
console.log(` FAIL: ${label} (no path found) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
const last = path[path.length - 1]!;
|
||||
const dist = Math.sqrt(Math.pow(last.x - destX, 2) + Math.pow(last.z - destZ, 2));
|
||||
const reached = dist <= maxDist;
|
||||
|
||||
timings.push({ label, method: 'findLongPath', ms, straightLineDist, waypoints: path.length, reached });
|
||||
|
||||
if (expectReach && !reached) {
|
||||
console.log(` FAIL: ${label} (path ends at (${last.x}, ${last.z}), ${dist.toFixed(0)} tiles away from dest) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check forbidden zone
|
||||
if (opts.forbiddenZone) {
|
||||
const fz = opts.forbiddenZone;
|
||||
for (const wp of path) {
|
||||
if (wp.x >= fz.minX && wp.x <= fz.maxX && wp.z >= fz.minZ && wp.z <= fz.maxZ) {
|
||||
console.log(` FAIL: ${label} (path crosses forbidden zone at (${wp.x}, ${wp.z})) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` PASS: ${label} (${path.length} waypoints, ends at (${last.x}, ${last.z}), dist: ${dist.toFixed(0)}) [${ms.toFixed(1)}ms]`);
|
||||
passed++;
|
||||
}
|
||||
|
||||
console.log('\n--- Wall Avoidance Tests ---');
|
||||
|
||||
// Falador is walled. The east wall runs roughly x=2946 from z=3307 to z=3442
|
||||
// South wall z~3307 from x=2900 to x=2946
|
||||
// Paths should go through gates, not through walls
|
||||
|
||||
test('East approach to Falador center',
|
||||
3007, 3360, 2964, 3378);
|
||||
|
||||
test('Lumbridge to Falador center (long distance)',
|
||||
3222, 3218, 2964, 3378, { maxDist: 10 });
|
||||
|
||||
test('Falador east to Falador west bank',
|
||||
2946, 3368, 2943, 3368);
|
||||
|
||||
test('North of Falador to Falador center',
|
||||
2965, 3460, 2964, 3378);
|
||||
|
||||
console.log('\n--- Door Passthrough Tests ---');
|
||||
|
||||
// Doors should be passable (their wall collision is masked)
|
||||
|
||||
test('Through Lumbridge castle doors',
|
||||
3222, 3215, 3205, 3210);
|
||||
|
||||
test('Varrock SE gate area',
|
||||
3270, 3400, 3255, 3400);
|
||||
|
||||
test('Lumbridge cow field gate',
|
||||
3253, 3275, 3253, 3265);
|
||||
|
||||
console.log('\n--- Permanent Wall Tests ---');
|
||||
|
||||
// These should test that permanent walls (not doors) are still blocked
|
||||
// Check some tiles on Falador wall are actually blocked
|
||||
const faladorWallTiles = [
|
||||
[2944, 3335], // east wall area
|
||||
[2944, 3360],
|
||||
[2945, 3370],
|
||||
[2946, 3365],
|
||||
];
|
||||
|
||||
let wallsBlocked = 0;
|
||||
for (const [x, z] of faladorWallTiles) {
|
||||
// Check if any wall flag is set on this tile
|
||||
const hasWall = isFlagged(x, z, 0,
|
||||
CollisionFlag.WALL_NORTH | CollisionFlag.WALL_EAST |
|
||||
CollisionFlag.WALL_SOUTH | CollisionFlag.WALL_WEST);
|
||||
if (hasWall) wallsBlocked++;
|
||||
}
|
||||
|
||||
if (wallsBlocked > 0) {
|
||||
console.log(` PASS: Falador east wall detected (${wallsBlocked}/${faladorWallTiles.length} tiles have wall flags)`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` FAIL: Falador east wall not detected (0/${faladorWallTiles.length} tiles have wall flags)`);
|
||||
failed++;
|
||||
}
|
||||
|
||||
// Check Lumbridge castle door tiles are walkable (walls removed by door mask)
|
||||
const lumbridgeDoorTile = [3217, 3218]; // known door position
|
||||
const doorWalkable = isTileWalkable(0, lumbridgeDoorTile[0], lumbridgeDoorTile[1]);
|
||||
if (doorWalkable) {
|
||||
console.log(` PASS: Lumbridge castle door tile is walkable (door mask working)`);
|
||||
passed++;
|
||||
} else {
|
||||
// Door masking removes wall flags but tile might still have LOC flag
|
||||
// That's OK - the pathfinder handles LOC collision differently from walls
|
||||
console.log(` INFO: Lumbridge castle door tile not fully walkable (may have LOC flag, which is normal)`);
|
||||
passed++; // still a pass - the path test above validates routing works
|
||||
}
|
||||
|
||||
console.log('\n--- Multi-Segment Long Distance Tests ---');
|
||||
|
||||
// These destinations are >256 tiles from source, requiring multi-segment routing
|
||||
|
||||
function testMultiSegment(
|
||||
label: string,
|
||||
srcX: number, srcZ: number,
|
||||
destX: number, destZ: number,
|
||||
opts: { maxDist?: number } = {}
|
||||
) {
|
||||
const maxDist = opts.maxDist ?? 15;
|
||||
const straightLineDist = Math.sqrt((destX - srcX) ** 2 + (destZ - srcZ) ** 2);
|
||||
|
||||
const t0 = performance.now();
|
||||
const path = findMultiSegmentPath(0, srcX, srcZ, destX, destZ);
|
||||
const ms = performance.now() - t0;
|
||||
|
||||
if (path.length === 0) {
|
||||
timings.push({ label, method: 'findMultiSegmentPath', ms, straightLineDist, waypoints: 0, reached: false });
|
||||
console.log(` FAIL: ${label} (no path found) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
const last = path[path.length - 1]!;
|
||||
const dist = Math.sqrt(Math.pow(last.x - destX, 2) + Math.pow(last.z - destZ, 2));
|
||||
const reached = dist <= maxDist;
|
||||
|
||||
timings.push({ label, method: 'findMultiSegmentPath', ms, straightLineDist, waypoints: path.length, reached });
|
||||
|
||||
if (!reached) {
|
||||
console.log(` FAIL: ${label} (path ends at (${last.x}, ${last.z}), ${dist.toFixed(0)} tiles away) [${ms.toFixed(1)}ms]`);
|
||||
failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(` PASS: ${label} (${path.length} waypoints, ends at (${last.x}, ${last.z}), dist: ${dist.toFixed(0)}) [${ms.toFixed(1)}ms]`);
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Melzar's Maze to Yanille (~340 tiles)
|
||||
testMultiSegment("Melzar's Maze to Yanille",
|
||||
2923, 3206, 2605, 3090, { maxDist: 20 });
|
||||
|
||||
// Lumbridge to Ardougne (~560 tiles)
|
||||
testMultiSegment('Lumbridge to Ardougne',
|
||||
3222, 3218, 2662, 3305, { maxDist: 20 });
|
||||
|
||||
// Varrock to Falador (~250 tiles, borderline single-segment)
|
||||
testMultiSegment('Varrock to Falador',
|
||||
3210, 3428, 2964, 3378, { maxDist: 15 });
|
||||
|
||||
// Lumbridge to Draynor (short distance, should still work via multi-segment)
|
||||
testMultiSegment('Lumbridge to Draynor (short, via multi-segment)',
|
||||
3222, 3218, 3092, 3243, { maxDist: 10 });
|
||||
|
||||
console.log('\n--- Door Detection Along Path Tests ---');
|
||||
|
||||
// Verify that doors are detected along paths that cross through buildings
|
||||
const lumbCastlePath = findLongPath(0, 3222, 3215, 3205, 3210);
|
||||
const lumbDoors = findDoorsAlongPath(lumbCastlePath);
|
||||
if (lumbDoors.length > 0) {
|
||||
console.log(` PASS: Lumbridge castle path detects ${lumbDoors.length} door(s) at: ${lumbDoors.map(d => `(${d.x},${d.z})`).join(', ')}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` FAIL: Lumbridge castle path detected 0 doors (expected >0)`);
|
||||
failed++;
|
||||
}
|
||||
|
||||
// A path that doesn't cross any doors shouldn't detect any
|
||||
const openFieldPath = findLongPath(0, 3200, 3200, 3210, 3200);
|
||||
const openFieldDoors = findDoorsAlongPath(openFieldPath);
|
||||
if (openFieldDoors.length === 0) {
|
||||
console.log(` PASS: Open field path detects 0 doors (as expected)`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` INFO: Open field path detected ${openFieldDoors.length} door(s) — might be near buildings (non-critical)`);
|
||||
passed++; // not a hard failure
|
||||
}
|
||||
|
||||
console.log('\n--- Water / Ocean Avoidance Tests ---');
|
||||
|
||||
// Known water/ocean tiles should NOT be walkable land
|
||||
const waterTestTiles: Array<{ label: string; x: number; z: number }> = [
|
||||
{ label: 'Ocean south of Lumbridge', x: 3200, z: 3100 },
|
||||
{ label: 'Open ocean far west', x: 2200, z: 3000 },
|
||||
{ label: 'Ocean south of Rimmington', x: 2880, z: 3130 },
|
||||
{ label: 'Deep ocean far south', x: 2700, z: 2900 },
|
||||
];
|
||||
|
||||
for (const { label, x, z } of waterTestTiles) {
|
||||
const allocated = isZoneAllocated(0, x, z);
|
||||
const walkable = isTileWalkable(0, x, z);
|
||||
const likelyLand = isZoneLikelyLand(0, x, z);
|
||||
|
||||
if (!allocated) {
|
||||
console.log(` PASS: ${label} (${x}, ${z}) — zone not allocated (unreachable)`);
|
||||
passed++;
|
||||
} else if (!walkable) {
|
||||
console.log(` PASS: ${label} (${x}, ${z}) — tile flagged as blocked`);
|
||||
passed++;
|
||||
} else if (!likelyLand) {
|
||||
console.log(` PASS: ${label} (${x}, ${z}) — empty zone rejected by isZoneLikelyLand`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` FAIL: ${label} (${x}, ${z}) — appears walkable (allocated=${allocated}, walkable=${walkable}, likelyLand=${likelyLand})`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Known land tiles should still be walkable
|
||||
const landTestTiles: Array<{ label: string; x: number; z: number }> = [
|
||||
{ label: 'Lumbridge center', x: 3222, z: 3218 },
|
||||
{ label: 'Falador center', x: 2964, z: 3378 },
|
||||
{ label: 'Varrock east road', x: 3220, z: 3430 },
|
||||
{ label: 'Ardougne market', x: 2662, z: 3305 },
|
||||
];
|
||||
|
||||
for (const { label, x, z } of landTestTiles) {
|
||||
const walkable = isTileWalkable(0, x, z);
|
||||
const likelyLand = isZoneLikelyLand(0, x, z);
|
||||
|
||||
if (walkable && likelyLand) {
|
||||
console.log(` PASS: ${label} (${x}, ${z}) — correctly identified as walkable land`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` FAIL: ${label} (${x}, ${z}) — not walkable (walkable=${walkable}, likelyLand=${likelyLand})`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n--- Spawn to Varrock Center Tests ---');
|
||||
|
||||
// Spawn point (3244, 3395) is SE of Varrock, near the south-east gate.
|
||||
// Varrock center (fountain/square) is around (3213, 3428).
|
||||
// This route must navigate through Varrock's SE gate area.
|
||||
|
||||
test('Spawn (3244,3395) to Varrock center via findLongPath',
|
||||
3244, 3395, 3213, 3428, { maxDist: 5 });
|
||||
|
||||
testMultiSegment('Spawn (3244,3395) to Varrock center via multi-segment',
|
||||
3244, 3395, 3213, 3428, { maxDist: 10 });
|
||||
|
||||
// Verify doors are detected on this route (Varrock gates)
|
||||
const spawnToVarrockPath = findLongPath(0, 3244, 3395, 3213, 3428);
|
||||
if (spawnToVarrockPath.length > 0) {
|
||||
const doors = findDoorsAlongPath(spawnToVarrockPath);
|
||||
console.log(` INFO: Spawn→Varrock path: ${spawnToVarrockPath.length} waypoints, ${doors.length} door(s) detected${doors.length > 0 ? ` at: ${doors.map(d => `(${d.x},${d.z})`).join(', ')}` : ''}`);
|
||||
} else {
|
||||
console.log(` INFO: Spawn→Varrock path: no path found (covered by test above)`);
|
||||
}
|
||||
|
||||
console.log('\n--- Cross-Continent Multi-Segment Tests ---');
|
||||
|
||||
// Wizards Tower to Tree Gnome Stronghold — the original failing route (~700 tiles)
|
||||
testMultiSegment('Wizards Tower to Tree Gnome Stronghold',
|
||||
3109, 3162, 2450, 3420, { maxDist: 35 });
|
||||
|
||||
// Lumbridge to Yanille (southwest, must avoid ocean)
|
||||
testMultiSegment('Lumbridge to Yanille',
|
||||
3222, 3218, 2605, 3090, { maxDist: 25 });
|
||||
|
||||
// ── Performance Summary ──────────────────────────────────────────────────────
|
||||
|
||||
console.log('\n========== PERFORMANCE ==========');
|
||||
|
||||
// Table header
|
||||
const col = { label: 45, method: 22, ms: 10, dist: 10, wps: 6, rate: 12 };
|
||||
console.log(
|
||||
'Route'.padEnd(col.label) +
|
||||
'Method'.padEnd(col.method) +
|
||||
'Time'.padStart(col.ms) +
|
||||
'Dist'.padStart(col.dist) +
|
||||
'Wps'.padStart(col.wps) +
|
||||
'Tiles/ms'.padStart(col.rate)
|
||||
);
|
||||
console.log('-'.repeat(col.label + col.method + col.ms + col.dist + col.wps + col.rate));
|
||||
|
||||
for (const t of timings) {
|
||||
const rate = t.ms > 0 ? (t.straightLineDist / t.ms).toFixed(1) : '-';
|
||||
console.log(
|
||||
t.label.slice(0, col.label - 1).padEnd(col.label) +
|
||||
t.method.padEnd(col.method) +
|
||||
`${t.ms.toFixed(1)}ms`.padStart(col.ms) +
|
||||
`${t.straightLineDist.toFixed(0)}`.padStart(col.dist) +
|
||||
`${t.waypoints}`.padStart(col.wps) +
|
||||
rate.padStart(col.rate)
|
||||
);
|
||||
}
|
||||
|
||||
// Aggregates
|
||||
const longPathTimings = timings.filter(t => t.method === 'findLongPath');
|
||||
const multiSegTimings = timings.filter(t => t.method === 'findMultiSegmentPath');
|
||||
const allMs = timings.map(t => t.ms);
|
||||
const totalMs = allMs.reduce((a, b) => a + b, 0);
|
||||
|
||||
console.log('-'.repeat(col.label + col.method + col.ms + col.dist + col.wps + col.rate));
|
||||
console.log(`Total pathfinding time: ${totalMs.toFixed(1)}ms across ${timings.length} calls`);
|
||||
|
||||
if (longPathTimings.length > 0) {
|
||||
const lms = longPathTimings.map(t => t.ms);
|
||||
console.log(` findLongPath: avg ${(lms.reduce((a, b) => a + b, 0) / lms.length).toFixed(1)}ms, max ${Math.max(...lms).toFixed(1)}ms (${longPathTimings.length} calls)`);
|
||||
}
|
||||
if (multiSegTimings.length > 0) {
|
||||
const mms = multiSegTimings.map(t => t.ms);
|
||||
console.log(` findMultiSegmentPath: avg ${(mms.reduce((a, b) => a + b, 0) / mms.length).toFixed(1)}ms, max ${Math.max(...mms).toFixed(1)}ms (${multiSegTimings.length} calls)`);
|
||||
}
|
||||
|
||||
// Flag anything that seems slow (>100ms for a single call)
|
||||
const slow = timings.filter(t => t.ms > 100);
|
||||
if (slow.length > 0) {
|
||||
console.log(`\nSlow calls (>100ms):`);
|
||||
for (const t of slow) {
|
||||
console.log(` ${t.label}: ${t.ms.toFixed(1)}ms (${t.straightLineDist.toFixed(0)} tiles)`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Test Results ──────────────────────────────────────────────────────────────
|
||||
|
||||
console.log(`\n========== RESULTS ==========`);
|
||||
console.log(`Passed: ${passed}/${passed + failed}`);
|
||||
console.log(`Failed: ${failed}/${passed + failed}`);
|
||||
|
||||
if (failed > 0) {
|
||||
console.log('\nFailed tests:');
|
||||
for (const t of timings.filter(t => !t.reached)) {
|
||||
console.log(` - ${t.label}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Pathfinding Test - Validates walkTo routing through walls, doors, water, and long distances.
|
||||
* Uses a real bot spawned at Lumbridge to test actual in-game navigation.
|
||||
*/
|
||||
|
||||
import { runTest } from './utils/test-runner';
|
||||
import { Locations } from './utils/save-generator';
|
||||
|
||||
interface TestResult {
|
||||
name: string;
|
||||
passed: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const results: TestResult[] = [];
|
||||
|
||||
function record(name: string, passed: boolean, message: string) {
|
||||
results.push({ name, passed, message });
|
||||
console.log(` ${passed ? 'PASS' : 'FAIL'}: ${name} — ${message}`);
|
||||
}
|
||||
|
||||
// Spawn outside Lumbridge castle courtyard to avoid getting trapped by the door
|
||||
const LUMBRIDGE_OUTSIDE = { x: 3222, z: 3240 };
|
||||
|
||||
runTest({
|
||||
name: 'Pathfinding Test',
|
||||
saveConfig: {
|
||||
position: LUMBRIDGE_OUTSIDE,
|
||||
skills: { Agility: 99 },
|
||||
varps: { 281: 1000 },
|
||||
},
|
||||
launchOptions: { skipTutorial: true },
|
||||
}, async ({ sdk, bot }) => {
|
||||
await sdk.waitForCondition(s => (s?.player?.worldX ?? 0) > 0, 10000);
|
||||
|
||||
const pos = () => {
|
||||
const s = sdk.getState()?.player;
|
||||
return { x: s?.worldX ?? 0, z: s?.worldZ ?? 0 };
|
||||
};
|
||||
|
||||
const distTo = (x: number, z: number) => {
|
||||
const p = pos();
|
||||
return Math.sqrt((p.x - x) ** 2 + (p.z - z) ** 2);
|
||||
};
|
||||
|
||||
// ── Short-range navigation ──────────────────────────────────────────
|
||||
|
||||
console.log('\n--- Short-Range Navigation ---');
|
||||
|
||||
// Lumbridge courtyard to general store (~10 tiles)
|
||||
{
|
||||
const dest = { x: 3212, z: 3246 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 5);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Lumbridge to general store',
|
||||
result.success && dist <= 10,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Door passthrough ────────────────────────────────────────────────
|
||||
|
||||
console.log('\n--- Door Passthrough ---');
|
||||
|
||||
// Walk into Lumbridge castle (requires going through doors)
|
||||
{
|
||||
const dest = { x: 3205, z: 3210 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 5);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Through Lumbridge castle doors',
|
||||
result.success && dist <= 10,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// Walk back out of the castle
|
||||
{
|
||||
const dest = LUMBRIDGE_OUTSIDE;
|
||||
const result = await bot.walkTo(dest.x, dest.z, 5);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Back out of Lumbridge castle',
|
||||
result.success && dist <= 10,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Medium-range: Lumbridge to Draynor (~130 tiles) ─────────────────
|
||||
|
||||
console.log('\n--- Medium-Range Navigation ---');
|
||||
|
||||
{
|
||||
const dest = { x: 3092, z: 3243 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 15);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Lumbridge to Draynor',
|
||||
result.success && dist <= 20,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Long-range: Draynor to Falador (~170 tiles) ─────────────────────
|
||||
|
||||
console.log('\n--- Long-Range Navigation ---');
|
||||
|
||||
{
|
||||
const dest = { x: 2964, z: 3378 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 20);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Draynor to Falador',
|
||||
dist <= 30,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Wall avoidance: move around inside Falador ──────────────────────
|
||||
|
||||
console.log('\n--- Wall Avoidance (Falador) ---');
|
||||
|
||||
// Falador is walled — walking east from center should stay inside or exit via gate
|
||||
{
|
||||
const dest = { x: 2946, z: 3368 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 10);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Falador center to east wall area',
|
||||
dist <= 15,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Cross-city: Falador to Varrock (~250 tiles) ─────────────────────
|
||||
|
||||
console.log('\n--- Cross-City Navigation ---');
|
||||
|
||||
{
|
||||
const dest = { x: 3213, z: 3428 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 20);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Falador to Varrock center',
|
||||
dist <= 30,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Gate handling: Varrock has gated entries ─────────────────────────
|
||||
|
||||
console.log('\n--- Gate Handling (Varrock) ---');
|
||||
|
||||
// Walk to Varrock SE gate area and back to center
|
||||
{
|
||||
const gate = { x: 3270, z: 3400 };
|
||||
const result1 = await bot.walkTo(gate.x, gate.z, 10);
|
||||
const dist1 = distTo(gate.x, gate.z);
|
||||
|
||||
const center = { x: 3213, z: 3428 };
|
||||
const result2 = await bot.walkTo(center.x, center.z, 10);
|
||||
const dist2 = distTo(center.x, center.z);
|
||||
|
||||
record('Varrock gate area round-trip',
|
||||
dist1 <= 15 && dist2 <= 15,
|
||||
`to gate: dist=${dist1.toFixed(0)} (${result1.message}), back: dist=${dist2.toFixed(0)} (${result2.message})`);
|
||||
}
|
||||
|
||||
// ── Water avoidance: should not walk into ocean south of Lumbridge ───
|
||||
|
||||
console.log('\n--- Water Avoidance ---');
|
||||
|
||||
// Walk from Varrock back toward Lumbridge — should not path through water
|
||||
{
|
||||
const dest = { x: 3222, z: 3218 };
|
||||
const result = await bot.walkTo(dest.x, dest.z, 15);
|
||||
const dist = distTo(dest.x, dest.z);
|
||||
record('Varrock to Lumbridge (avoid water)',
|
||||
dist <= 25,
|
||||
`dist=${dist.toFixed(0)}, ${result.message}`);
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────
|
||||
|
||||
console.log('\n========== RESULTS ==========');
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
for (const r of results) {
|
||||
if (r.passed) passed++; else failed++;
|
||||
}
|
||||
console.log(`Passed: ${passed}/${results.length}`);
|
||||
console.log(`Failed: ${failed}/${results.length}`);
|
||||
|
||||
if (failed > 0) {
|
||||
console.log('\nFailed tests:');
|
||||
for (const r of results.filter(r => !r.passed)) {
|
||||
console.log(` - ${r.name}: ${r.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return failed === 0;
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Walk Test: Spawn at (3244, 3395) and walk to Varrock center.
|
||||
* Tests pathfinding through Varrock's SE gate area with a fresh character.
|
||||
*/
|
||||
|
||||
import { runTest } from './utils/test-runner';
|
||||
|
||||
const SPAWN = { x: 3244, z: 3395 };
|
||||
const VARROCK_CENTER = { x: 3213, z: 3428 };
|
||||
|
||||
runTest({
|
||||
name: 'Walk to Varrock Center',
|
||||
saveConfig: {
|
||||
position: SPAWN,
|
||||
varps: { 281: 1000 },
|
||||
},
|
||||
launchOptions: { skipTutorial: true },
|
||||
}, async ({ sdk, bot }) => {
|
||||
await sdk.waitForCondition(s => (s?.player?.worldX ?? 0) > 0, 10000);
|
||||
|
||||
const pos = () => {
|
||||
const s = sdk.getState()?.player;
|
||||
return { x: s?.worldX ?? 0, z: s?.worldZ ?? 0 };
|
||||
};
|
||||
|
||||
const distTo = (x: number, z: number) => {
|
||||
const p = pos();
|
||||
return Math.sqrt((p.x - x) ** 2 + (p.z - z) ** 2);
|
||||
};
|
||||
|
||||
const start = pos();
|
||||
console.log(`Spawned at (${start.x}, ${start.z})`);
|
||||
console.log(`Target: Varrock center (${VARROCK_CENTER.x}, ${VARROCK_CENTER.z})`);
|
||||
console.log(`Distance: ${distTo(VARROCK_CENTER.x, VARROCK_CENTER.z).toFixed(0)} tiles\n`);
|
||||
|
||||
const t0 = Date.now();
|
||||
const result = await bot.walkTo(VARROCK_CENTER.x, VARROCK_CENTER.z, 5);
|
||||
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
||||
|
||||
const final = pos();
|
||||
const dist = distTo(VARROCK_CENTER.x, VARROCK_CENTER.z);
|
||||
|
||||
console.log(`\n=== RESULT ===`);
|
||||
console.log(`walkTo success: ${result.success}`);
|
||||
console.log(`Message: ${result.message}`);
|
||||
console.log(`Final position: (${final.x}, ${final.z})`);
|
||||
console.log(`Distance from target: ${dist.toFixed(1)} tiles`);
|
||||
console.log(`Time: ${elapsed}s`);
|
||||
|
||||
const passed = result.success && dist <= 5;
|
||||
console.log(`Verdict: ${passed ? 'PASS' : 'FAIL'}`);
|
||||
|
||||
return passed;
|
||||
});
|
||||
+1
-1
@@ -543,7 +543,7 @@ module.exports.__wbindgen_init_externref_table = function() {
|
||||
;
|
||||
};
|
||||
|
||||
const path = require('path').join(__dirname, 'rsmod-pathfinder_bg.wasm');
|
||||
const path = require('path').join(__dirname, 'rsmod_bg.wasm');
|
||||
const bytes = require('fs').readFileSync(path);
|
||||
|
||||
const wasmModule = new WebAssembly.Module(bytes);
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
File renamed without changes.
+7
-6
@@ -13,13 +13,14 @@ git clone --depth 1 "$REPO_URL" "$TMP_DIR"
|
||||
|
||||
echo "Building WASM with wasm-pack..."
|
||||
cd "$TMP_DIR"
|
||||
wasm-pack build --target nodejs --out-dir dist
|
||||
wasm-pack build --target nodejs --release
|
||||
|
||||
echo "Copying dist files to vendor..."
|
||||
cp dist/rsmod-pathfinder.js "$SCRIPT_DIR/"
|
||||
cp dist/rsmod-pathfinder.d.ts "$SCRIPT_DIR/"
|
||||
cp dist/rsmod-pathfinder_bg.wasm "$SCRIPT_DIR/"
|
||||
cp dist/rsmod-pathfinder_bg.wasm.d.ts "$SCRIPT_DIR/"
|
||||
echo "Copying pkg files to vendor..."
|
||||
# wasm-pack outputs files named after the [lib] name in Cargo.toml ("rsmod")
|
||||
cp pkg/rsmod.js "$SCRIPT_DIR/rsmod-pathfinder.js"
|
||||
cp pkg/rsmod.d.ts "$SCRIPT_DIR/rsmod-pathfinder.d.ts"
|
||||
cp pkg/rsmod_bg.wasm "$SCRIPT_DIR/rsmod_bg.wasm"
|
||||
cp pkg/rsmod_bg.wasm.d.ts "$SCRIPT_DIR/rsmod_bg.wasm.d.ts"
|
||||
|
||||
echo "Cleaning up..."
|
||||
rm -rf "$TMP_DIR"
|
||||
|
||||
Reference in new issue
Block a user