118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
import { create } from 'zustand';
|
|
import { useInventoryStore } from './useInventoryStore';
|
|
import { mapStoreToSpooferConfig, mapSpooferConfigToStore } from './wsProtocol';
|
|
|
|
interface WebsocketState {
|
|
socket: WebSocket | null;
|
|
isConnected: boolean;
|
|
connect: () => void;
|
|
}
|
|
|
|
let lastTogglesJson = '';
|
|
let lastInventoryJson = '';
|
|
|
|
export const useWebsocketStore = create<WebsocketState>((set, get) => ({
|
|
socket: null,
|
|
isConnected: false,
|
|
|
|
connect: () => {
|
|
if (get().socket) return;
|
|
|
|
const ws = new WebSocket('ws://localhost:4444');
|
|
|
|
ws.onopen = () => {
|
|
console.log('Connected to WS server');
|
|
set({ socket: ws, isConnected: true });
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log('Disconnected. Retrying in 2s');
|
|
set({ socket: null, isConnected: false });
|
|
setTimeout(() => get().connect(), 2000);
|
|
};
|
|
|
|
ws.onerror = (err) => {
|
|
console.log('WS Error:', err);
|
|
ws.close();
|
|
};
|
|
|
|
ws.onmessage = async (msg) => {
|
|
try {
|
|
const payload = JSON.parse(msg.data);
|
|
|
|
if (payload.action === 0) {
|
|
const mapped = await mapSpooferConfigToStore(payload.profile);
|
|
|
|
lastInventoryJson = JSON.stringify({
|
|
unlockedCharacters: mapped.unlockedCharacters,
|
|
unlockedCustomizations: mapped.unlockedCustomizations,
|
|
unlockedDLCs: mapped.unlockedDLCs,
|
|
unlockedPerks: mapped.unlockedPerks,
|
|
items: mapped.items,
|
|
offerings: mapped.offerings,
|
|
addons: mapped.addons
|
|
});
|
|
lastTogglesJson = JSON.stringify({
|
|
spoofItems: payload.toggles?.spoofItems ?? false,
|
|
spoofPerks: payload.toggles?.spoofPerks ?? false,
|
|
spoofCatalog: payload.toggles?.spoofCatalog ?? false,
|
|
spoofDLCs: payload.toggles?.spoofDLCs ?? false
|
|
});
|
|
|
|
useInventoryStore.getState().importProfile(mapped);
|
|
useInventoryStore.getState().importToggles(payload.toggles);
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to parse WS message', e);
|
|
}
|
|
};
|
|
}
|
|
}));
|
|
|
|
let syncTimeout: any = null;
|
|
|
|
useInventoryStore.subscribe((state) => {
|
|
if (syncTimeout) clearTimeout(syncTimeout);
|
|
|
|
syncTimeout = setTimeout(() => {
|
|
const { socket, isConnected } = useWebsocketStore.getState();
|
|
if (!isConnected || !socket || socket.readyState !== WebSocket.OPEN) return;
|
|
|
|
const toggles = {
|
|
spoofItems: state.spoofItems,
|
|
spoofPerks: state.spoofPerks,
|
|
spoofCatalog: state.spoofCatalog,
|
|
spoofDLCs: state.spoofDLCs
|
|
};
|
|
const togglesJson = JSON.stringify(toggles);
|
|
if (togglesJson !== lastTogglesJson) {
|
|
lastTogglesJson = togglesJson;
|
|
socket.send(JSON.stringify({ action: 2, toggles }));
|
|
}
|
|
|
|
const inventory = {
|
|
unlockedCharacters: state.unlockedCharacters,
|
|
unlockedCustomizations: state.unlockedCustomizations,
|
|
unlockedDLCs: state.unlockedDLCs,
|
|
unlockedPerks: state.unlockedPerks,
|
|
items: state.items,
|
|
offerings: state.offerings,
|
|
addons: state.addons
|
|
};
|
|
const inventoryJson = JSON.stringify(inventory);
|
|
if (inventoryJson !== lastInventoryJson) {
|
|
lastInventoryJson = inventoryJson;
|
|
|
|
mapStoreToSpooferConfig(state).then((profile) => {
|
|
const ws = useWebsocketStore.getState();
|
|
if (
|
|
ws.isConnected &&
|
|
ws.socket &&
|
|
ws.socket.readyState === WebSocket.OPEN
|
|
)
|
|
ws.socket.send(JSON.stringify({ action: 1, profile }));
|
|
});
|
|
}
|
|
}, 250);
|
|
});
|