oh fuck i forgot
This commit is contained in:
173
extension/modules/state.js
Normal file
173
extension/modules/state.js
Normal file
@@ -0,0 +1,173 @@
|
||||
(function () {
|
||||
const MB = window.MagicBot;
|
||||
|
||||
// Store the raw full state to apply patches against
|
||||
let rawState = null;
|
||||
|
||||
MB.on('packet_received', (msg) => {
|
||||
// Log to discover flow
|
||||
if (msg.type !== 'Ping' && msg.type !== 'Pong' && msg.type !== 'PartialState') {
|
||||
console.log(`[MagicBot] RX: ${msg.type}`, Object.keys(msg));
|
||||
}
|
||||
|
||||
// --- DATA COLLECTION STREAM ---
|
||||
if (msg.type !== 'Ping' && msg.type !== 'Pong') { // Stream everything non-heartbeat
|
||||
fetch('http://localhost:5454', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type: msg.type, payload: msg })
|
||||
}).catch(e => console.warn('[MagicBot] Data Stream Error:', e));
|
||||
}
|
||||
// ------------------------------
|
||||
|
||||
if (msg.type === 'Welcome') {
|
||||
handleWelcome(msg);
|
||||
} else if (msg.type === 'State') {
|
||||
handleState(msg);
|
||||
} else if (msg.type === 'PartialState') {
|
||||
handlePartialState(msg);
|
||||
}
|
||||
});
|
||||
|
||||
function handleWelcome(msg) {
|
||||
console.log("[MagicBot] Parsing Welcome...");
|
||||
rawState = msg.fullState.data; // Store raw root
|
||||
|
||||
// Fix: Explicitly attach child (Quinoa/Game Scope) because 'data' doesn't contain it
|
||||
if (msg.fullState.child) {
|
||||
rawState.child = msg.fullState.child;
|
||||
}
|
||||
|
||||
// Initial parse
|
||||
parseRawState();
|
||||
}
|
||||
|
||||
function handleState(msg) {
|
||||
console.log("[MagicBot] Handling State message...", msg);
|
||||
// Replace or merge? Usually 'State' is a full dump of a specific scope.
|
||||
// For simplicity, we might just re-process.
|
||||
// CAUTION: If it's just 'Quinoa' data, we attach it to rawState.child.data?
|
||||
// Let's assume it updates the relevant part of rawState if we knew where it fits.
|
||||
// For now, let's just try to process it directly.
|
||||
|
||||
const data = msg.data || msg.state || msg;
|
||||
if (msg.scope === 'Quinoa' || (data.scope === 'Quinoa')) {
|
||||
// It's the game state!
|
||||
if (!rawState.child) rawState.child = {};
|
||||
rawState.child.data = data.data || data;
|
||||
parseRawState();
|
||||
}
|
||||
}
|
||||
|
||||
function handlePartialState(msg) {
|
||||
if (!rawState) return;
|
||||
|
||||
// Apply patches to rawState
|
||||
if (msg.patches && Array.isArray(msg.patches)) {
|
||||
// console.log(`[MagicBot] Applying ${msg.patches.length} patches...`);
|
||||
applyPatches(rawState, msg.patches);
|
||||
|
||||
// Re-eval the simplified state
|
||||
parseRawState();
|
||||
}
|
||||
}
|
||||
|
||||
function parseRawState() {
|
||||
if (!rawState) return;
|
||||
|
||||
const root = rawState;
|
||||
|
||||
// 1. Players
|
||||
if (root.players) MB.state.players = root.players;
|
||||
|
||||
// 2. PlayerID
|
||||
if (!MB.state.playerId && root.hostPlayerId) {
|
||||
MB.state.playerId = root.hostPlayerId;
|
||||
}
|
||||
|
||||
// 3. Game Data
|
||||
let gameData = null;
|
||||
if (root.child && root.child.data) {
|
||||
gameData = root.child.data;
|
||||
}
|
||||
|
||||
if (gameData) {
|
||||
processGameData(gameData);
|
||||
} else if (!MB.state.players) {
|
||||
// Only warn if we have NOTHING
|
||||
console.warn("[MagicBot] State looks empty.");
|
||||
}
|
||||
|
||||
MB.emit('state_updated', MB.state);
|
||||
}
|
||||
|
||||
function processGameData(gameData) {
|
||||
if (gameData.shops) MB.state.shops = gameData.shops;
|
||||
if (gameData.timer) MB.state.timer = gameData.timer;
|
||||
|
||||
if (gameData.userSlots && Array.isArray(gameData.userSlots)) {
|
||||
const userSlot = gameData.userSlots.find(s => s.playerId === MB.state.playerId);
|
||||
if (userSlot) {
|
||||
MB.state.coins = userSlot.data.coinsCount;
|
||||
MB.state.garden = userSlot.data.garden;
|
||||
MB.state.inventory = userSlot.data.inventory;
|
||||
MB.state.shopPurchases = userSlot.data.shopPurchases;
|
||||
MB.state.self = userSlot;
|
||||
} else {
|
||||
console.warn("[MagicBot] State: Could not find user slot for self:", MB.state.playerId);
|
||||
}
|
||||
} else {
|
||||
console.warn("[MagicBot] State: userSlots missing or invalid in gameData");
|
||||
}
|
||||
}
|
||||
|
||||
// --- JSON PATCH UTILS ---
|
||||
function applyPatches(doc, patches) {
|
||||
patches.forEach(patch => {
|
||||
try {
|
||||
applyOperation(doc, patch);
|
||||
} catch (e) {
|
||||
console.warn("[MagicBot] Patch failed:", e, patch);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function applyOperation(doc, patch) {
|
||||
const path = patch.path.split('/').filter(p => p.length > 0);
|
||||
let current = doc;
|
||||
|
||||
// Navigate to parent
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const key = path[i];
|
||||
if (current[key] === undefined) {
|
||||
if (patch.op === 'add') current[key] = {}; // Auto-create path for add?
|
||||
else return; // Path doesn't exist
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
const lastKey = path[path.length - 1];
|
||||
|
||||
switch (patch.op) {
|
||||
case 'add':
|
||||
case 'replace':
|
||||
if (Array.isArray(current) && !isNaN(lastKey)) {
|
||||
// Array set/insert
|
||||
const idx = parseInt(lastKey);
|
||||
if (patch.op === 'add') current.splice(idx, 0, patch.value);
|
||||
else current[idx] = patch.value;
|
||||
} else {
|
||||
current[lastKey] = patch.value;
|
||||
}
|
||||
break;
|
||||
case 'remove':
|
||||
if (Array.isArray(current)) {
|
||||
current.splice(parseInt(lastKey), 1);
|
||||
} else {
|
||||
delete current[lastKey];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log('[Magic Bot Extension] State module loaded.');
|
||||
})();
|
||||
Reference in New Issue
Block a user