diff --git a/client/.env.preview b/client/.env.preview index 9c1260517..fa0ec1cb2 100644 --- a/client/.env.preview +++ b/client/.env.preview @@ -3,19 +3,19 @@ VITE_PUBLIC_MASTER_PRIVATE_KEY=0x075362a844768f31c8058ce31aec3dd7751686440b4f220 VITE_PUBLIC_WORLD_ADDRESS="0x00fd85ef42eaed3b90d02d2cdc7417d6cae189ff4ba876aa5608551afbf1fb47" VITE_PUBLIC_ACCOUNT_CLASS_HASH="0x07dc7899aa655b0aae51eadff6d801a58e97dd99cf4666ee59e704249e51adf2" VITE_PUBLIC_FEE_TOKEN_ADDRESS=0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 -VITE_PUBLIC_TORII=https://api.cartridge.gg/x/sepolia-rc-18/torii -VITE_PUBLIC_NODE_URL=https://api.cartridge.gg/x/starknet/sepolia +VITE_PUBLIC_TORII=https://api.cartridge.gg/x/mainnet-cache-2/torii +VITE_PUBLIC_NODE_URL=https://api.cartridge.gg/x/starknet/mainnet VITE_PUBLIC_DEV=false VITE_PUBLIC_GAME_VERSION="v1.0.0-rc7" VITE_PUBLIC_SHOW_FPS=false VITE_PUBLIC_GRAPHICS_DEV=false -VITE_PUBLIC_TORII_RELAY=/dns4/api.cartridge.gg/tcp/443/x-parity-wss/%2Fx%2Fsepolia-rc-18%2Ftorii%2Fwss +VITE_PUBLIC_TORII_RELAY=/dns4/api.cartridge.gg/tcp/443/x-parity-wss/%2Fx%mainnet-cache-2%2Ftorii%2Fwss VITE_SEASON_PASS_ADDRESS=0x23cc88996a5f9c7bcb559fdcffc257c0f75abe60f2a7e5d5cd343f8a95967f7 VITE_REALMS_ADDRESS=0x3205f47bd6f0b5e9cd5c79fcae19e12523a024709776d0a9e8b375adf63468d VITE_LORDS_ADDRESS=0x0342ad5cc14002c005a5cedcfce2bd3af98d5e7fb79e9bf949b3a91cf145d72e -VITE_PUBLIC_CHAIN=sepolia -VITE_PUBLIC_SLOT=sepolia-rc-17 +VITE_PUBLIC_CHAIN=mainnet +VITE_PUBLIC_SLOT=mainnet-cache-2 VITE_SOCIAL_LINK=http://bit.ly/3Zz1mpp diff --git a/client/package.json b/client/package.json index 1249bad2c..a6f0ae922 100644 --- a/client/package.json +++ b/client/package.json @@ -19,14 +19,14 @@ "@bibliothecadao/eternum": "workspace:^", "@cartridge/connector": "0.5.6", "@cartridge/controller": "0.5.6", - "@dojoengine/core": "1.0.4-alpha.3.1.0", - "@dojoengine/create-burner": "1.0.4-alpha.3.1.0", - "@dojoengine/react": "1.0.4-alpha.3.1.0", + "@dojoengine/core": "1.0.4-alpha.3.1.1", + "@dojoengine/create-burner": "1.0.4-alpha.3.1.1", + "@dojoengine/react": "1.0.4-alpha.3.1.1", "@dojoengine/recs": "^2.0.13", - "@dojoengine/state": "1.0.4-alpha.3.1.0", - "@dojoengine/torii-client": "1.0.4-alpha.3.1.0", - "@dojoengine/torii-wasm": "1.0.4-alpha.3.1.0", - "@dojoengine/utils": "1.0.4-alpha.3.1.0", + "@dojoengine/state": "1.0.4-alpha.3.1.1", + "@dojoengine/torii-client": "1.0.4-alpha.3.1.1", + "@dojoengine/torii-wasm": "1.0.4-alpha.3.1.1", + "@dojoengine/utils": "1.0.4-alpha.3.1.1", "@headlessui/react": "^1.7.18", "@latticexyz/utils": "^2.0.0-next.12", "@radix-ui/react-collapsible": "^1.1.1", diff --git a/client/src/dojo/entityWorker.js b/client/src/dojo/entityWorker.js new file mode 100644 index 000000000..741624522 --- /dev/null +++ b/client/src/dojo/entityWorker.js @@ -0,0 +1,30 @@ +import { debounce } from "lodash"; + +let entityBatch = {}; +let logging = false; +const DEBOUNCE_DELAY = 1000; + +let debouncedSendBatch = debounce(() => { + if (Object.keys(entityBatch).length > 0) { + console.log("Worker: Sending batch", entityBatch); + self.postMessage({ updatedEntities: entityBatch }); + entityBatch = {}; + } +}, DEBOUNCE_DELAY); + +self.onmessage = async (e) => { + const { type, entities, logging: logFlag } = e.data; + if (type === "update") { + logging = logFlag; + if (logging) console.log("Worker: Received entities update"); + handleUpdate(entities.fetchedEntities, entities.data); + } +}; + +function handleUpdate(fetchedEntities, data) { + entityBatch[fetchedEntities] = { + ...entityBatch[fetchedEntities], + ...data, + }; + debouncedSendBatch(); +} diff --git a/client/src/dojo/indexedDB.ts b/client/src/dojo/indexedDB.ts new file mode 100644 index 000000000..243d8d096 --- /dev/null +++ b/client/src/dojo/indexedDB.ts @@ -0,0 +1,115 @@ +import { Component, Metadata, Schema } from "@dojoengine/recs"; +import { setEntities } from "@dojoengine/state"; +import { Entities } from "@dojoengine/torii-client"; + +const DB_NAME = "eternum-db"; +const DB_VERSION = 1; + +function openDatabase(): Promise { + let db: IDBDatabase; + + return new Promise((resolve, reject) => { + const request: IDBOpenDBRequest = indexedDB.open(DB_NAME, DB_VERSION); + + request.onupgradeneeded = (event: IDBVersionChangeEvent) => { + const db = (event.target as IDBOpenDBRequest).result; + if (!db.objectStoreNames.contains("entities")) { + db.createObjectStore("entities", { keyPath: "id" }); + } + }; + + request.onsuccess = (event: Event) => { + db = (event.target as IDBOpenDBRequest).result; + resolve(db); + }; + + request.onerror = (event: Event) => { + console.error("Database error:", (event.target as IDBOpenDBRequest).error); + reject((event.target as IDBOpenDBRequest).error); + }; + }); +} + +async function syncEntitiesFromStorage( + dbConnection: IDBDatabase, + components: Component[], +): Promise { + return new Promise((resolve, reject) => { + const transaction = dbConnection.transaction(["entities"], "readonly"); + const store = transaction.objectStore("entities"); + const request = store.getAll(); + + request.onsuccess = () => { + const entities = request.result; + + const CHUNK_SIZE = 50000; + + // Process entities in chunks + for (let i = 0; i < entities.length; i += CHUNK_SIZE) { + const chunk = entities.slice(i, i + CHUNK_SIZE); + const chunkMap: Entities = {}; + + for (const entity of chunk) { + const { id, ...data } = entity; + chunkMap[id] = data; + } + + setEntities(chunkMap, components, false); + } + + resolve(); + }; + + request.onerror = () => { + console.log("Error fetching entities from storage:", request.error); + reject(request.error); + }; + }); +} + +async function insertEntitiesInDB(db: IDBDatabase, entities: Entities): Promise { + return new Promise((resolve, reject) => { + const transaction = db.transaction(["entities"], "readwrite"); + const store = transaction.objectStore("entities"); + + let error: Error | null = null; + + // Handle transaction completion + transaction.oncomplete = () => { + if (error) { + reject(error); + } else { + resolve(); + } + }; + + transaction.onerror = () => { + reject(transaction.error); + }; + + // Store each entity + for (const [entityId, data] of Object.entries(entities)) { + const entityData = { + id: entityId, + ...data, + }; + + const request = store.put(entityData); + + request.onerror = () => { + error = request.error; + }; + } + }); +} + +async function clearCache() { + Object.keys(localStorage) + .filter((x) => x.endsWith("_query")) + .forEach((x) => localStorage.removeItem(x)); + + indexedDB.deleteDatabase(DB_NAME); + location.reload(); +} + +export { clearCache, insertEntitiesInDB, openDatabase, syncEntitiesFromStorage }; diff --git a/client/src/dojo/queries.ts b/client/src/dojo/queries.ts index b0334ecad..5b3b0141a 100644 --- a/client/src/dojo/queries.ts +++ b/client/src/dojo/queries.ts @@ -4,35 +4,11 @@ import { Component, Metadata, Schema } from "@dojoengine/recs"; import { getEntities } from "@dojoengine/state"; import { PatternMatching, ToriiClient } from "@dojoengine/torii-client"; -// on hexception -> fetch below queries based on entityID - -// background sync after load -> - -export const syncPosition = async ( - client: ToriiClient, - components: Component[], - entityID: string, -) => { - await getEntities( - client, - { - Keys: { - keys: [entityID], - pattern_matching: "FixedLen" as PatternMatching, - models: ["s0_eternum-Position"], - }, - }, - components, - [], - [], - 30_000, - ); -}; - export const addToSubscriptionTwoKeyModelbyRealmEntityId = async ( client: ToriiClient, components: Component[], entityID: string[], + db: IDBDatabase, ) => { await getEntities( client, @@ -54,6 +30,8 @@ export const addToSubscriptionTwoKeyModelbyRealmEntityId = async [], entityID: string[], + db: IDBDatabase, ) => { await getEntities( client, @@ -82,6 +61,8 @@ export const addToSubscriptionOneKeyModelbyRealmEntityId = async ( client: ToriiClient, components: Component[], entityID: string[], + db: IDBDatabase, position?: { x: number; y: number }[], ) => { const start = performance.now(); @@ -121,6 +103,8 @@ export const addToSubscription = async ( [], [], 30_000, + false, + { dbConnection: db, timestampCacheKey: `entity_${entityID}_query` }, ); const end = performance.now(); console.log("AddToSubscriptionEnd", end - start); @@ -129,6 +113,7 @@ export const addToSubscription = async ( export const addMarketSubscription = async ( client: ToriiClient, components: Component[], + db: IDBDatabase, ) => { const start = performance.now(); await getEntities( @@ -145,6 +130,7 @@ export const addMarketSubscription = async ( [], 30_000, false, + { dbConnection: db, timestampCacheKey: "market_query" }, ); const end = performance.now(); console.log("MarketEnd", end - start); diff --git a/client/src/dojo/setup.ts b/client/src/dojo/setup.ts index 0c1677cdf..7f9c88d95 100644 --- a/client/src/dojo/setup.ts +++ b/client/src/dojo/setup.ts @@ -4,72 +4,72 @@ import { WORLD_CONFIG_ID, } from "@bibliothecadao/eternum"; import { DojoConfig } from "@dojoengine/core"; -import { Component, Metadata, Schema } from "@dojoengine/recs"; -import { getEntities, getEvents, setEntities } from "@dojoengine/state"; -import { Clause, EntityKeysClause, ToriiClient } from "@dojoengine/torii-client"; -import { debounce } from "lodash"; +import { getEntities, getEvents } from "@dojoengine/state"; +import { Clause } from "@dojoengine/torii-client"; import { createClientComponents } from "./createClientComponents"; import { createSystemCalls } from "./createSystemCalls"; +import { openDatabase, syncEntitiesFromStorage } from "./indexedDB"; import { ClientConfigManager } from "./modelManager/ConfigManager"; import { setupNetwork } from "./setupNetwork"; +import { setupWorker } from "./worker"; export type SetupResult = Awaited>; export const configManager = ClientConfigManager.instance(); -export const syncEntitiesDebounced = async ( - client: ToriiClient, - components: Component[], - entityKeyClause: EntityKeysClause[], - logging: boolean = true, - historical: boolean = false, -) => { - if (logging) console.log("Starting syncEntities"); - - let entityBatch: Record = {}; - - const debouncedSetEntities = debounce(() => { - if (Object.keys(entityBatch).length > 0) { - // console.log("Applying batch update", entityBatch); - setEntities(entityBatch, components, logging); - entityBatch = {}; // Clear the batch after applying - } - }, 1000); // Increased debounce time to 1 second for larger batches - - // Handle entity updates - const entitySub = await client.onEntityUpdated(entityKeyClause, (fetchedEntities: any, data: any) => { - if (logging) console.log("Entity updated", fetchedEntities); - // Merge new data with existing data for this entity - entityBatch[fetchedEntities] = { - ...entityBatch[fetchedEntities], - ...data, - }; - debouncedSetEntities(); - }); - - // Handle event message updates - const eventSub = await client.onEventMessageUpdated( - entityKeyClause, - historical, - (fetchedEntities: any, data: any) => { - if (logging) console.log("Event message updated", fetchedEntities); - // Merge new data with existing data for this entity - entityBatch[fetchedEntities] = { - ...entityBatch[fetchedEntities], - ...data, - }; - debouncedSetEntities(); - }, - ); - - // Return combined subscription that can cancel both - return { - cancel: () => { - entitySub.cancel(); - eventSub.cancel(); - }, - }; -}; +// export const syncEntitiesDebounced = async ( +// client: ToriiClient, +// components: Component[], +// entityKeyClause: EntityKeysClause[], +// logging: boolean = true, +// historical: boolean = false, +// ) => { +// if (logging) console.log("Starting syncEntities"); + +// let entityBatch: Record = {}; + +// const debouncedSetEntities = debounce(() => { +// if (Object.keys(entityBatch).length > 0) { +// // console.log("Applying batch update", entityBatch); +// setEntities(entityBatch, components, logging); +// entityBatch = {}; // Clear the batch after applying +// } +// }, 1000); // Increased debounce time to 1 second for larger batches + +// // Handle entity updates +// const entitySub = await client.onEntityUpdated(entityKeyClause, (fetchedEntities: any, data: any) => { +// if (logging) console.log("Entity updated", fetchedEntities); +// // Merge new data with existing data for this entity +// entityBatch[fetchedEntities] = { +// ...entityBatch[fetchedEntities], +// ...data, +// }; +// debouncedSetEntities(); +// }); + +// // Handle event message updates +// const eventSub = await client.onEventMessageUpdated( +// entityKeyClause, +// historical, +// (fetchedEntities: any, data: any) => { +// if (logging) console.log("Event message updated", fetchedEntities); +// // Merge new data with existing data for this entity +// entityBatch[fetchedEntities] = { +// ...entityBatch[fetchedEntities], +// ...data, +// }; +// debouncedSetEntities(); +// }, +// ); + +// // Return combined subscription that can cancel both +// return { +// cancel: () => { +// entitySub.cancel(); +// eventSub.cancel(); +// }, +// }; +// }; export async function setup({ ...config }: DojoConfig) { const network = await setupNetwork(config); @@ -114,61 +114,83 @@ export async function setup({ ...config }: DojoConfig) { }, ]; - await getEntities( - network.toriiClient, - { Composite: { operator: "Or", clauses: configClauses } }, - network.contractComponents as any, - ); - - // fetch all existing entities from torii - await getEntities( - network.toriiClient, - { - Keys: { - keys: [undefined], - pattern_matching: "FixedLen", - models: [ - "s0_eternum-AddressName", - "s0_eternum-Realm", - "s0_eternum-PopulationConfig", - "s0_eternum-CapacityConfig", - "s0_eternum-ProductionConfig", - "s0_eternum-RealmLevelConfig", - "s0_eternum-BankConfig", - "s0_eternum-Bank", - "s0_eternum-Trade", - "s0_eternum-Army", - "s0_eternum-Structure", - "s0_eternum-Battle", - "s0_eternum-EntityOwner", - ], + const indexedDB = await openDatabase(); + await syncEntitiesFromStorage(indexedDB, network.contractComponents as any); + + await Promise.all([ + getEntities( + network.toriiClient, + { Composite: { operator: "Or", clauses: configClauses } }, + network.contractComponents as any, + [], + [], + 40_000, + false, + { dbConnection: indexedDB, timestampCacheKey: "config_query" }, + ), + + // fetch all existing entities from torii + getEntities( + network.toriiClient, + { + Keys: { + keys: [undefined], + pattern_matching: "FixedLen", + models: [ + "s0_eternum-AddressName", + "s0_eternum-Realm", + "s0_eternum-PopulationConfig", + "s0_eternum-CapacityConfig", + "s0_eternum-ProductionConfig", + "s0_eternum-RealmLevelConfig", + "s0_eternum-BankConfig", + "s0_eternum-Bank", + "s0_eternum-Trade", + "s0_eternum-Army", + "s0_eternum-Structure", + "s0_eternum-Battle", + "s0_eternum-EntityOwner", + ], + }, }, - }, - network.contractComponents as any, - [], - [], - 40_000, - false, - ); - - await getEntities( - network.toriiClient, - { - Keys: { - keys: [undefined, undefined], - pattern_matching: "FixedLen", - models: ["s0_eternum-CapacityConfigCategory", "s0_eternum-ResourceCost"], + network.contractComponents as any, + [], + [], + 40_000, + false, + { dbConnection: indexedDB, timestampCacheKey: "single_keyed_query" }, + ), + + getEntities( + network.toriiClient, + { + Keys: { + keys: [undefined, undefined], + pattern_matching: "FixedLen", + models: ["s0_eternum-CapacityConfigCategory", "s0_eternum-ResourceCost"], + }, }, + network.contractComponents as any, + [], + [], + 40_000, + false, + { dbConnection: indexedDB, timestampCacheKey: "double_keyed_query" }, + ), + ]); + + const sync = await setupWorker( + { + rpcUrl: config.rpcUrl, + toriiUrl: config.toriiUrl, + relayUrl: config.relayUrl, + worldAddress: config.manifest.world.address || "", }, network.contractComponents as any, [], - [], - 40_000, false, ); - const sync = await syncEntitiesDebounced(network.toriiClient, network.contractComponents as any, [], false); - configManager.setDojo(components); const eventSync = getEvents( @@ -206,5 +228,6 @@ export async function setup({ ...config }: DojoConfig) { systemCalls, sync, eventSync, + db: indexedDB, }; } diff --git a/client/src/dojo/worker.ts b/client/src/dojo/worker.ts new file mode 100644 index 000000000..39e2a54f1 --- /dev/null +++ b/client/src/dojo/worker.ts @@ -0,0 +1,56 @@ +import { Component, Metadata, Schema } from "@dojoengine/recs"; +import { setEntities } from "@dojoengine/state"; +import { EntityKeysClause, createClient } from "@dojoengine/torii-wasm"; + +export async function setupWorker( + config: { rpcUrl: string; toriiUrl: string; relayUrl: string; worldAddress: string }, + components: Component[], + entityKeyClause: EntityKeysClause[], + historical: boolean, + logging = false, +) { + if (logging) console.log("Starting syncEntities"); + + const worker = new Worker(new URL("./entityWorker.js", import.meta.url), { type: "module" }); + + // Create the client in the main thread + const toriiClient = await createClient({ + rpcUrl: config.rpcUrl, + toriiUrl: config.toriiUrl, + relayUrl: config.relayUrl, + worldAddress: config.worldAddress, + }); + + // Listen for batches from the Worker + worker.onmessage = (event) => { + const { updatedEntities } = event.data; + setEntities(updatedEntities, components, logging); + }; + + // Set up subscriptions in the main thread + await toriiClient.onEntityUpdated(entityKeyClause, (fetchedEntities: any, data: any) => { + if (logging) console.log("Main: Entity updated", fetchedEntities); + // Send updates to worker for processing + worker.postMessage({ + type: "update", + entities: { fetchedEntities, data }, + logging, + }); + }); + + await toriiClient.onEventMessageUpdated(entityKeyClause, historical, (fetchedEntities: any, data: any) => { + if (logging) console.log("Main: Event message updated", fetchedEntities); + // Send updates to worker for processing + worker.postMessage({ + type: "update", + entities: { fetchedEntities, data }, + logging, + }); + }); + + return { + cancel: () => { + worker.terminate(); + }, + }; +} diff --git a/client/src/ui/components/resources/InventoryResources.tsx b/client/src/ui/components/resources/InventoryResources.tsx index ce042c370..08f78f79c 100644 --- a/client/src/ui/components/resources/InventoryResources.tsx +++ b/client/src/ui/components/resources/InventoryResources.tsx @@ -51,9 +51,12 @@ export const InventoryResources = ({ setIsSyncing(true); try { - await addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ - entityId.toString(), - ]); + await addToSubscription( + dojo.network.toriiClient, + dojo.network.contractComponents as any, + [entityId.toString()], + dojo.setup.db, + ); localStorage.setItem(cacheKey, now.toString()); } catch (error) { console.error("Fetch failed", error); diff --git a/client/src/ui/components/trading/ResourceArrivals.tsx b/client/src/ui/components/trading/ResourceArrivals.tsx index 389d85cc2..1205d597e 100644 --- a/client/src/ui/components/trading/ResourceArrivals.tsx +++ b/client/src/ui/components/trading/ResourceArrivals.tsx @@ -29,9 +29,12 @@ export const AllResourceArrivals = memo( }); // Move API call outside of state updates - addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, unsubscribedIds).catch( - (error) => console.error("Fetch failed", error), - ); + addToSubscription( + dojo.network.toriiClient, + dojo.network.contractComponents as any, + unsubscribedIds, + dojo.setup.db, + ).catch((error) => console.error("Fetch failed", error)); }, [arrivals, subscribedIds]); return ( diff --git a/client/src/ui/layouts/World.tsx b/client/src/ui/layouts/World.tsx index 21c769b63..1a42ea1e6 100644 --- a/client/src/ui/layouts/World.tsx +++ b/client/src/ui/layouts/World.tsx @@ -3,12 +3,7 @@ import { lazy, Suspense, useEffect, useMemo, useState } from "react"; import { Redirect } from "wouter"; import useUIStore from "../../hooks/store/useUIStore"; -import { - addMarketSubscription, - addToSubscription, - addToSubscriptionOneKeyModelbyRealmEntityId, - addToSubscriptionTwoKeyModelbyRealmEntityId, -} from "@/dojo/queries"; +import { addMarketSubscription, addToSubscription, addToSubscriptionOneKeyModelbyRealmEntityId } from "@/dojo/queries"; import { useDojo } from "@/hooks/context/DojoContext"; import { PlayerStructure, useEntities } from "@/hooks/helpers/useEntities"; import { useStructureEntityId } from "@/hooks/helpers/useStructureEntityId"; @@ -121,6 +116,7 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => { [structures, subscriptions], ); + // Handle initial structure and admin bank subscription useEffect(() => { if ( !structureEntityId || @@ -141,49 +137,87 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => { ...prev, [structureEntityId.toString()]: true, [ADMIN_BANK_ENTITY_ID.toString()]: true, - ...Object.fromEntries(filteredStructures.map((structure) => [structure.entity_id.toString(), true])), })); const fetch = async () => { try { await Promise.all([ - addToSubscriptionOneKeyModelbyRealmEntityId( + addToSubscription( dojo.network.toriiClient, dojo.network.contractComponents as any, - [...filteredStructures.map((structure) => structure.entity_id.toString())], + [structureEntityId.toString()], + dojo.setup.db, + [{ x: position?.x || 0, y: position?.y || 0 }], ), - addToSubscriptionTwoKeyModelbyRealmEntityId( + addToSubscription( dojo.network.toriiClient, dojo.network.contractComponents as any, - [...filteredStructures.map((structure) => structure.entity_id.toString())], + [ADMIN_BANK_ENTITY_ID.toString()], + dojo.setup.db, ), - addToSubscription( + ]); + } catch (error) { + console.error("Fetch failed", error); + } finally { + setWorldLoading(false); + } + }; + + fetch(); + }, [structureEntityId, subscriptions]); + + // Handle filtered structures subscription + useEffect(() => { + if (filteredStructures.length === 0) return; + + setWorldLoading(true); + setSubscriptions((prev) => ({ + ...prev, + ...Object.fromEntries(filteredStructures.map((structure) => [structure.entity_id.toString(), true])), + })); + + const fetch = async () => { + try { + await Promise.all([ + addToSubscriptionOneKeyModelbyRealmEntityId( dojo.network.toriiClient, dojo.network.contractComponents as any, - [structureEntityId.toString()], - [{ x: position?.x || 0, y: position?.y || 0 }], + filteredStructures.map((structure) => structure.entity_id.toString()), + dojo.setup.db, ), - addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ - ADMIN_BANK_ENTITY_ID.toString(), - ]), addToSubscription( dojo.network.toriiClient, dojo.network.contractComponents as any, - [...filteredStructures.map((structure) => structure.entity_id.toString())], - [...filteredStructures.map((structure) => ({ x: structure.position.x, y: structure.position.y }))], + filteredStructures.map((structure) => structure.entity_id.toString()), + dojo.setup.db, + filteredStructures.map((structure) => ({ x: structure.position.x, y: structure.position.y })), ), - addMarketSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any), ]); } catch (error) { console.error("Fetch failed", error); } finally { setWorldLoading(false); + } + }; + + fetch(); + }, [filteredStructures]); + + // Handle market subscription + useEffect(() => { + const fetch = async () => { + setMarketLoading(true); + try { + await addMarketSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, dojo.setup.db); + } catch (error) { + console.error("Market fetch failed", error); + } finally { setMarketLoading(false); } }; fetch(); - }, [structureEntityId, subscriptions, setWorldLoading, setSubscriptions, filteredStructures]); + }, []); return (
{ return { timeLeftBeforeNextTick: timeLeft, progress: progressValue }; }, [nextBlockTimestamp]); - console.log(entityInfo.structureCategory); return (
{ />
-
+
@@ -333,7 +329,18 @@ export const TopLeftNavigation = memo(() => { TopLeftNavigation.displayName = "TopLeftNavigation"; -const TickProgress = () => { +const ProgressBar = memo(({ progress }: { progress: number }) => { + return ( +
+ ); +}); + +ProgressBar.displayName = "ProgressBar"; + +const TickProgress = memo(() => { const setTooltip = useUIStore((state) => state.setTooltip); const { nextBlockTimestamp } = useNextBlockTimestamp(); @@ -397,4 +404,4 @@ const TickProgress = () => { {progress.toFixed()}%
); -}; +}); diff --git a/client/src/ui/modules/settings/Settings.tsx b/client/src/ui/modules/settings/Settings.tsx index 700b8e54d..ba066498e 100644 --- a/client/src/ui/modules/settings/Settings.tsx +++ b/client/src/ui/modules/settings/Settings.tsx @@ -5,6 +5,7 @@ import { ReactComponent as Unmuted } from "@/assets/icons/common/unmuted.svg"; import { ReactComponent as Controller } from "@/assets/icons/Controller.svg"; import { ReactComponent as DojoMark } from "@/assets/icons/dojo-mark-full-dark.svg"; import { ReactComponent as RealmsWorld } from "@/assets/icons/rw-logo.svg"; +import { clearCache } from "@/dojo/indexedDB"; import { useDojo } from "@/hooks/context/DojoContext"; import { useRealm } from "@/hooks/helpers/useRealm"; import useUIStore from "@/hooks/store/useUIStore"; @@ -61,7 +62,7 @@ export const SettingsWindow = () => { const isOpen = useUIStore((state) => state.isPopupOpen(settings)); - const GRAPHICS_SETTING = localStorage.getItem("GRAPHICS_SETTING") as GraphicsSettings || GraphicsSettings.HIGH; + const GRAPHICS_SETTING = (localStorage.getItem("GRAPHICS_SETTING") as GraphicsSettings) || GraphicsSettings.HIGH; return ( togglePopup(settings)} show={isOpen} title={settings}> @@ -171,6 +172,15 @@ export const SettingsWindow = () => { Github +