some mcp fixes

This commit is contained in:
max bittker committed 2026-02-09 09:49:33 -05:00
1 parent 740340e091
commit b15385fe9e
6 files changed
+86 -6

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
# Draynor Manor
## DANGER: One-Way Front Door
The front door of Draynor Manor **cannot be opened from inside**. The quest script (`quest_haunted.rs2`) checks `if(coordz(coord) > coordz(loc_coord))` — only players on the north (outside) side can open it.
**If the pathfinder routes you through the front door, you will be trapped inside.**
### How to Escape (If Trapped)
The manor has internal doors that connect rooms. Two key doors form an escape path through the east wing:
1. **Door at (3119, 3356) id:1530** — connects the SE room to the warehouse area
2. **Door at (3123, 3360) id:136** — connects warehouse to the courtyard
The SDK pathfinder can't open these doors automatically because `sendInteractLoc` relies on server-side pathfinding, which blocks on closed doors. Use raw `sendWalk` to get adjacent, then interact:
```typescript
// Step 1: Raw-walk to a tile adjacent to the door
sdk.sendWalk(doorX, doorZ - 1); // south side
await sdk.waitForTicks(2);
// Step 2: Interact to open
sdk.sendInteractLoc(doorX, doorZ, doorId, 1);
await sdk.waitForTicks(3);
// Step 3: Walk through
sdk.sendWalk(doorX, doorZ + 1); // north side
await sdk.waitForTicks(2);
```
### Avoidance
Avoid routing through Draynor Manor entirely. If you need to pass through the area, use coordinates that stay well clear of the manor grounds (x < 3090 or x > 3130).
## Draynor Manor Underground
- **Stairs down** at (3115, 3357) → crypt at (3077, 9771)
- Crypt is a separate area from the puzzle basement — no walking connection between them
- **Magic door** exit at (2874, 9750) leads to the Wilderness — avoid unless you know what you're doing
## Key Coordinates
| Location | Coordinates | Notes |
|----------|-------------|-------|
| Front door (one-way) | (3108, 3353) | DO NOT ENTER |
| East wing doors | (3119, 3356), (3123, 3360) | Escape route |
| Crypt stairs | (3115, 3357) | Goes to (3077, 9771) |
| Courtyard | (3125, 3370) | North of east wing |
+12 -3
View File
@@ -12,6 +12,7 @@ export interface BotConnection {
bot: BotActions;
username: string;
connected: boolean;
unsubscribeConnectionState?: () => void;
}
class BotManager {
@@ -99,7 +100,7 @@ class BotManager {
};
// Track connection state changes
sdk.onConnectionStateChange((state) => {
connection.unsubscribeConnectionState = sdk.onConnectionStateChange((state) => {
const wasConnected = connection.connected;
connection.connected = state === 'connected';
if (wasConnected && !connection.connected) {
@@ -114,11 +115,16 @@ class BotManager {
}
private async connectWithTimeout(sdk: BotSDK, timeoutMs: number): Promise<void> {
let timeoutId: ReturnType<typeof setTimeout>;
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`Connection timed out after ${timeoutMs / 1000}s`)), timeoutMs);
timeoutId = setTimeout(() => reject(new Error(`Connection timed out after ${timeoutMs / 1000}s`)), timeoutMs);
});
await Promise.race([sdk.connect(), timeoutPromise]);
try {
await Promise.race([sdk.connect(), timeoutPromise]);
} finally {
clearTimeout(timeoutId!);
}
}
/**
@@ -131,6 +137,9 @@ class BotManager {
}
console.error(`[MCP] Disconnecting bot "${name}"...`);
if (connection.unsubscribeConnectionState) {
connection.unsubscribeConnectionState();
}
connection.sdk.disconnect();
connection.connected = false;
this.connections.delete(name);
+8 -2
View File
@@ -196,10 +196,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Execute code with 10 minute timeout
const EXECUTION_TIMEOUT = 10 * 60 * 1000;
let timeoutId: ReturnType<typeof setTimeout>;
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`Code execution timed out after 10 minutes`)), EXECUTION_TIMEOUT);
timeoutId = setTimeout(() => reject(new Error(`Code execution timed out after 10 minutes`)), EXECUTION_TIMEOUT);
});
const result = await Promise.race([fn(connection.bot, connection.sdk), timeoutPromise]);
let result: any;
try {
result = await Promise.race([fn(connection.bot, connection.sdk), timeoutPromise]);
} finally {
clearTimeout(timeoutId!);
}
// Build formatted output
const parts: string[] = [];
+14 -1
View File
@@ -573,7 +573,16 @@ export class BotActions {
const invCountBefore = this.sdk.getInventory().length;
const startTick = this.sdk.getState()?.tick || 0;
// Send pickup directly - game client handles walk-to-interact via MOVE_OPCLICK
// Walk to the item's exact tile first
const walkResult = await this.walkTo(item.x, item.z, 0);
if (!walkResult.success) {
return { success: false, message: walkResult.message, reason: 'cant_reach' };
}
// Wait one tick before picking up
await this.sdk.waitForTicks(1);
// Now send the pickup command
const result = await this.sdk.sendPickup(item.x, item.z, item.id);
if (!result.success) {
return { success: false, message: result.message };
@@ -610,6 +619,10 @@ export class BotActions {
}
const pickedUp = this.sdk.getInventory().find(i => i.id === item.id);
// Wait one tick after picking up
await this.sdk.waitForTicks(1);
return { success: true, item: pickedUp, message: `Picked up ${item.name}` };
} catch {
return { success: false, message: 'Timed out waiting for pickup', reason: 'timeout' };
+3
View File
@@ -58,11 +58,13 @@ for (const dir of outDirs) {
for (const outDir of ['out/standard', 'out/bot']) {
fs.copyFileSync('src/3rdparty/bzip2-wasm/bzip2.wasm', `${outDir}/bzip2.wasm`);
fs.copyFileSync('src/3rdparty/tinymidipcm/tinymidipcm.wasm', `${outDir}/tinymidipcm.wasm`);
fs.copyFileSync('src/3rdparty/tinymidipcm/SCC1_Florestan.sf2', `${outDir}/SCC1_Florestan.sf2`);
}
// Also copy to root out for backwards compatibility
fs.copyFileSync('src/3rdparty/bzip2-wasm/bzip2.wasm', 'out/bzip2.wasm');
fs.copyFileSync('src/3rdparty/tinymidipcm/tinymidipcm.wasm', 'out/tinymidipcm.wasm');
fs.copyFileSync('src/3rdparty/tinymidipcm/SCC1_Florestan.sf2', 'out/SCC1_Florestan.sf2');
const args = process.argv.slice(2);
const prod = args[0] !== 'dev';
@@ -124,6 +126,7 @@ const viewerScript = await bunBuild(
fs.writeFileSync('out/viewer/viewer.js', replaceDepsUrl(viewerScript.source));
fs.copyFileSync('src/3rdparty/bzip2-wasm/bzip2.wasm', 'out/viewer/bzip2.wasm');
fs.copyFileSync('src/3rdparty/tinymidipcm/tinymidipcm.wasm', 'out/viewer/tinymidipcm.wasm');
fs.copyFileSync('src/3rdparty/tinymidipcm/SCC1_Florestan.sf2', 'out/viewer/SCC1_Florestan.sf2');
fs.writeFileSync('out/viewer/deps.js', deps.source);
// Copy bot client to root out for backwards compatibility
Binary file not shown.