Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor hooks #2642

Merged
merged 17 commits into from
Jan 15, 2025
Merged

refactor hooks #2642

merged 17 commits into from
Jan 15, 2025

Conversation

aymericdelab
Copy link
Collaborator

@aymericdelab aymericdelab commented Jan 15, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new hook usePlayers to retrieve and process player data.
    • Introduced utility functions for army management: armyHasTroops and armyHasTraveled.
    • Added new utility functions for entity management: getRealm, getStructure, getEntityInfo, getEntityName, getRealmName, getAddressName, getAddressNameFromEntity, and getPlayerAddressFromEntity.
    • Introduced getRealmAddressName and getRealmNameById for improved realm management.
  • Improvements

    • Streamlined data retrieval across multiple components using new hooks and utility functions.
    • Enhanced context management by integrating components into various function calls.
    • Simplified player and structure data access methods.
    • Improved performance by optimizing data fetching and memoization.
  • Changes

    • Removed several utility hooks and replaced them with more focused hooks and utility functions.
    • Updated import statements and type definitions across multiple files.
    • Modified how entity and resource information is retrieved and processed.
    • Removed the useGetAllPlayers hook in favor of usePlayers for direct player data access.
    • Adjusted how contributions are managed by utilizing LeaderboardManager.
  • Refactoring

    • Consolidated player and structure data management.
    • Simplified component logic by removing redundant hooks and utility functions.
    • Enhanced type safety and code clarity.
    • Streamlined the handling of leaderboard contributions and resource balances.

Copy link
Contributor

mentatbot bot commented Jan 15, 2025

Hi @aymericdelab! You need to be added as a user to interact with me. Please ask @ponderingdemocritus to add you on the settings page.

You are receiving this comment because I am set to review all PRs. That is configurable here.

Copy link

vercel bot commented Jan 15, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
eternum ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 15, 2025 4:41pm
eternum-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 15, 2025 4:41pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
eternum-landing ⬜️ Ignored (Inspect) Visit Preview Jan 15, 2025 4:41pm

Copy link
Contributor

coderabbitai bot commented Jan 15, 2025

Warning

Rate limit exceeded

@aymericdelab has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2f8524c and 16c5a7d.

📒 Files selected for processing (2)
  • client/apps/game/src/ui/modules/social/social.tsx (5 hunks)
  • client/apps/game/src/utils/players.ts (1 hunks)

Walkthrough

This pull request introduces a comprehensive refactoring of the game's client-side hooks and components, focusing on centralizing data retrieval, simplifying utility functions, and improving the overall modularity of the codebase. The changes span multiple files and modules, with a primary emphasis on restructuring how entities, resources, and player data are accessed and managed.

Changes

File/Module Change Summary
hooks/helpers/use-battle-events.tsx Added useBattleEventData helper function to consolidate battle event querying logic. Updated useBattleStart, useBattleJoin, and useBattleLeave to utilize this new function.
hooks/helpers/use-battles.tsx Updated player realms retrieval using usePlayerRealms, removing useEntities.
hooks/helpers/use-buildings.tsx Modified useBuildings to accept coordinates, added useBuildingQuantities hook for building quantity retrieval.
hooks/helpers/use-contributions.tsx Renamed useContributions to usePlayerContributions, removed additional hooks for simplification.
hooks/helpers/use-entities.tsx Replaced useEntities with usePlayerRealms and usePlayerStructures.
hooks/helpers/use-fragment-mines.tsx Updated return value of useFragmentMines to return the fragmentMines array directly.
hooks/helpers/use-get-all-players.tsx Removed the entire file containing the useGetAllPlayers hook.
hooks/helpers/use-guilds.tsx Renamed Player type to PlayerInfo and updated related functions.
hooks/helpers/use-hyperstructures.tsx Updated function signatures to include components as an argument for entity name retrieval.
hooks/helpers/use-players.tsx Introduced usePlayers hook for retrieving player data.
hooks/helpers/use-quests.tsx Removed several unused functions and streamlined quest handling logic.
hooks/helpers/use-realm.tsx Removed useRealm, added usePlayerRealms and simplified realm data retrieval.
hooks/helpers/use-resources.tsx Removed useResourcesUtils and simplified resource balance retrieval.
hooks/helpers/use-structures.tsx Updated entity name retrieval to include components as an argument.
hooks/helpers/use-trade.tsx Updated player realm retrieval to use usePlayerRealms.
Multiple Components Replaced useEntitiesUtils with direct utility function imports from @/utils/entities.
Resource Management Updated resource balance retrieval to use getBalance with components parameter.

Sequence Diagram

sequenceDiagram
    participant Client as Game Client
    participant Hooks as Custom Hooks
    participant Utilities as Utility Functions
    participant Components as Dojo Components

    Client->>Hooks: Request player data
    Hooks->>Components: Query components
    Components-->>Hooks: Return component values
    Hooks->>Utilities: Process data with utility functions
    Utilities-->>Hooks: Return processed data
    Hooks-->>Client: Provide refined data
Loading

Possibly Related PRs

Suggested Reviewers

  • bob0005

Poem

🐰 A Rabbit's Code Refactoring Tale 🌟

Hooks and utils, once scattered wide,
Now dance together, side by side.
Components sing a clearer song,
Where data flows both swift and strong.
Refactored code, a rabbit's delight! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

Failed to generate code suggestions for PR

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (7)
client/apps/game/src/ui/components/military/army-management-card.tsx (1)

Line range hint 137-141: Add missing dependencies to useEffect.

The useEffect hook uses owner_entity and dojo.setup.components but they're not included in the dependency array. This could lead to stale balance checks if these values change.

Add the missing dependencies:

  setCanCreate(canCreate);
- }, [troopCounts]);
+ }, [troopCounts, owner_entity, dojo.setup.components]);
client/apps/game/src/ui/modules/entity-details/realm/castle.tsx (1)

Line range hint 38-49: Correct the dependency array of useMemo.

The useMemo hook relies on dojo.setup.components through the getBalance function. Add dojo.setup.components to the dependency array to ensure the memoized value updates when components change.

Apply this diff:

- }, [getBalance, structureEntityId]);
+ }, [structureEntityId, dojo.setup.components]);
client/apps/game/src/ui/components/fragmentMines/fragment-mine-panel.tsx (1)

Line range hint 33-44: Improve error handling in name change logic

The current error handling only logs errors and doesn't inform the user. The loading state might also get stuck if an error occurs.

Consider this improved implementation:

  try {
    await provider.set_entity_name({ signer: account, entity_id: entity.entity_id, name: naming });
  } catch (e) {
    console.error(e);
+   // Show error to user
+   toast.error('Failed to change name. Please try again.');
+   return;
  } finally {
+   setIsLoading(false);
  }

- setIsLoading(false);
  setEditName(false);
client/apps/game/src/ui/components/structures/construction/structure-construction-menu.tsx (1)

Line range hint 121-127: Optimize balance checks and remove hardcoded resource ID.

The balance check is repeated for each resource, and the resource ID is hardcoded to AncientFragment instead of using the actual resource from the cost object.

-          const balance = getBalance(entityId || 0, ResourcesIds.AncientFragment, dojo.setup.components);
+          const resourceId = cost[Number(resourceId)].resource_tier === ResourceTier.Lords
+            ? ResourcesIds.AncientFragment
+            : Number(resourceId);
+          const balance = getBalance(entityId || 0, resourceId, dojo.setup.components);
           return (
             <ResourceCost
               key={index}
               type="horizontal"
-              resourceId={ResourcesIds.AncientFragment}
+              resourceId={resourceId}
               amount={cost[Number(resourceId)].min_amount * 1000}
               balance={balance.balance}
             />
client/apps/game/src/hooks/helpers/use-resource-arrivals.tsx (1)

Line range hint 89-90: Add missing dependencies to createArrivalInfo useCallback.

The callback depends on hasMinWeight and Position, but they're not included in the dependency array.

  const createArrivalInfo = useCallback(
    (id: Entity): ArrivalInfo | undefined => {
      // ... function body ...
    },
-    [account, playerStructurePositions],
+    [account, playerStructurePositions, hasMinWeight, Position],
  );
client/apps/game/src/ui/modules/rewards/rewards.tsx (1)

Line range hint 78-91: Remove duplicate setIsLoading(false) call.

The setIsLoading(false) is called twice in the claimRewards function - once in the finally block and once after it.

  const claimRewards = useCallback(async () => {
    setIsLoading(true);
    try {
      await claim_leaderboard_rewards({
        signer: account,
        token: env.VITE_LORDS_ADDRESS!,
      });
    } catch (error) {
      console.error("Error claiming rewards", error);
    } finally {
      setIsLoading(false);
    }
-   setIsLoading(false);
  }, [account]);
client/apps/game/src/ui/components/resources/travel-info.tsx (1)

Line range hint 36-56: Update useEffect dependencies to include dojo.setup.components.

The useEffect hook depends on dojo.setup.components through the getBalance call, but it's not included in the dependency array. This could lead to stale data if components change.

Apply this diff to fix the dependency array:

  useEffect(() => {
    const totalWeight = getTotalResourceWeight(resources);
    const multipliedWeight = multiplyByPrecision(totalWeight);
    setResourceWeight(multipliedWeight);

    const { balance } = getBalance(entityId, ResourcesIds.Donkey, dojo.setup.components);
    const currentDonkeyAmount = isAmm ? 0 : resources.find((r) => r.resourceId === ResourcesIds.Donkey)?.amount || 0;
    const calculatedDonkeyBalance = divideByPrecision(balance) - currentDonkeyAmount;
    setDonkeyBalance(calculatedDonkeyBalance);

    const onlyDonkeysAndLords = resources.every(
      (r) => r.resourceId === ResourcesIds.Donkey || r.resourceId === ResourcesIds.Lords,
    );

    if (setCanCarry) {
      onlyDonkeysAndLords ? setCanCarry(true) : setCanCarry(calculatedDonkeyBalance >= neededDonkeys);
    }
-  }, [resources, entityId, resourceWeight, donkeyBalance, setCanCarry]);
+  }, [resources, entityId, resourceWeight, donkeyBalance, setCanCarry, dojo.setup.components]);
🧹 Nitpick comments (29)
client/apps/game/src/ui/components/worldmap/armies/action-info.tsx (1)

97-105: Consider enhancing error handling for Army component.

The current implementation silently falls back to default values when the Army component is not found. Consider logging a warning or handling the error more explicitly for better debugging.

    const army = getComponentValue(components.Army, getEntityIdFromKeys([BigInt(selectedEntityId)]));
+   if (!army) {
+     console.warn(`Army component not found for entity ${selectedEntityId}`);
+   }
    return (
      army?.troops || {
        knight_count: 0n,
        paladin_count: 0n,
        crossbowman_count: 0n,
      }
    );
client/apps/game/src/ui/components/military/army-management-card.tsx (3)

250-250: Optimize balance calculations with useMemo.

The balance calculations within the map function will be recalculated on every render. Consider memoizing the balances for better performance.

Here's how you could optimize it:

+ const troopBalances = useMemo(() => {
+   return troops.reduce((acc, troop) => ({
+     ...acc,
+     [troop.name]: getBalance(owner_entity, troop.name, dojo.setup.components).balance
+   }), {});
+ }, [owner_entity, troops, dojo.setup.components]);

  <div className="grid grid-cols-3 gap-3 my-4">
    {troops.map((troop) => {
-     const balance = getBalance(owner_entity, troop.name, dojo.setup.components).balance;
+     const balance = troopBalances[troop.name];

Line range hint 89-102: Improve error handling for army operations.

The error handling for handleDeleteArmy only logs to console. Consider adding user feedback for failed operations.

Example improvement:

  try {
    await armyManager.deleteArmy(account, army?.entity_id || 0);
    setSelectedEntity && setSelectedEntity(null);
  } catch (e) {
    console.error(e);
+   // Add user feedback
+   const errorMessage = e instanceof Error ? e.message : 'Failed to delete army';
+   // Assuming you have a notification system
+   showNotification({ type: 'error', message: errorMessage });
  }

Line range hint 1-289: Consider splitting the component for better maintainability.

The component handles multiple responsibilities (army management, troop management, naming). Consider splitting it into smaller, focused components:

  • ArmyHeader (name editing)
  • TroopManagement (troop counts and balance)
  • ArmyActions (delete/reinforce buttons)

This would improve maintainability and make the code easier to test.

client/apps/game/src/ui/components/worldmap/players/players-panel.tsx (2)

Line range hint 37-52: Consider extracting the player transformation logic.

The player data transformation logic is complex and handles multiple concerns (structures, guild info, whitelist status). Consider extracting this into a separate utility function for better maintainability and testability.

+ const transformPlayerData = (
+   player: PlayerInfo,
+   index: number,
+   components: any,
+   userGuild: any,
+   account: any
+ ): PlayerCustom => {
+   const structuresEntityIds = runQuery([
+     Has(Structure),
+     HasValue(Owner, { address: ContractAddress(player.address) }),
+   ]);
+   
+   const structures = Array.from(structuresEntityIds)
+     .map((entityId) => {
+       const structure = getComponentValue(Structure, entityId);
+       if (!structure) return undefined;
+       return getEntityName(structure.entity_id, components);
+     })
+     .filter((structure): structure is string => structure !== undefined);
+
+   const guild = getGuildFromPlayerAddress(player.address);
+   const isInvited = userGuild
+     ? getComponentValue(
+         GuildWhitelist,
+         getEntityIdFromKeys([player.address, BigInt(userGuild.entityId)])
+       )?.is_whitelisted ?? false
+     : false;
+
+   return {
+     ...player,
+     structures,
+     isUser: player.address === ContractAddress(account.address),
+     points: player.points || 0,
+     rank: index + 1,
+     isInvited,
+     guild,
+   };
+ };

  const playersWithStructures: PlayerCustom[] = useMemo(() => {
    const sortedPlayers = [...players].sort((a, b) => (b.points || 0) - (a.points || 0));
-   const playersWithStructures = sortedPlayers.map((player, index) => {
-     // ... existing transformation logic ...
-   });
+   const playersWithStructures = sortedPlayers.map((player, index) =>
+     transformPlayerData(player, index, components, userGuild, account)
+   );
    return playersWithStructures;
  }, [isLoading, players]);

Line range hint 74-90: Consider optimizing search performance.

The search functionality handles multiple criteria well, but consider these optimizations:

  1. Memoize individual search criteria results
  2. Consider debouncing the search input
  3. Add early return conditions for empty structures
+ const getSearchMatches = useMemo(() => {
+   const normalizedTerm = normalizeDiacriticalMarks(searchTerm.toLowerCase());
+   return {
+     nameMatches: new Set(
+       playersWithStructures
+         .filter(player => 
+           normalizeDiacriticalMarks(player.name.toLowerCase()).includes(normalizedTerm)
+         )
+         .map(p => p.address)
+     ),
+     addressMatches: new Set(
+       playersWithStructures
+         .filter(player =>
+           toHexString(player.address).toLowerCase().includes(normalizedTerm)
+         )
+         .map(p => p.address)
+     ),
+     structureMatches: new Set(
+       playersWithStructures
+         .filter(player =>
+           player.structures.some(
+             structure => structure && 
+             normalizeDiacriticalMarks(structure.toLowerCase()).includes(normalizedTerm)
+           )
+         )
+         .map(p => p.address)
+     )
+   };
+ }, [playersWithStructures, searchTerm]);

  const filteredPlayers = useMemo(() => {
    if (searchTerm === "") return playersWithStructures;
+   const { nameMatches, addressMatches, structureMatches } = getSearchMatches;
+   return playersWithStructures.filter(player =>
+     nameMatches.has(player.address) ||
+     addressMatches.has(player.address) ||
+     structureMatches.has(player.address)
+   );
-   return playersWithStructures.filter((player) => {
-     // ... existing filtering logic ...
-   });
  }, [playersWithStructures, searchTerm]);
client/apps/game/src/hooks/helpers/use-resources.tsx (1)

Line range hint 10-15: Refine dependencies in useMemo hook to prevent unnecessary recomputations

Including dojo.setup in the dependency array may cause resourceManager to recompute more often than necessary if dojo.setup is a complex object that changes on every render. Consider narrowing down the dependencies to specific properties or values that affect the resourceManager.

Apply this diff to refine the dependencies:

 const resourceManager = useMemo(() => {
   return new ResourceManager(dojo.setup.components, entityId, resourceId);
- }, [dojo.setup, entityId, resourceId, production]);
+ }, [dojo.setup.components, entityId, resourceId]);

If dojo.setup.components is stable or memoized, this change will help prevent unnecessary recomputations.

client/apps/game/src/hooks/helpers/use-realm.tsx (1)

10-10: Clarify variable naming for better readability

Destructuring account: { account } results in account.account, which can be confusing. Consider renaming the inner account to improve code clarity.

Apply this diff to rename the variable:

 const {
-   account: { account },
+   account: { account: userAccount },
    setup: { components },
 } = useDojo();

Update references to account.address with userAccount.address in the code.

client/apps/game/src/hooks/helpers/use-entities.tsx (3)

20-24: Include components in the dependency array of useMemo.

The useMemo hook relies on components through the getRealm function. To ensure the memoized value updates correctly when components change, add components to the dependency array.

Apply this diff:

  }, [entities]);
+ }, [entities, components]);

43-47: Include components in the dependency array of useMemo.

Similarly, the useMemo hook depends on components via the getStructure function. Add components to the dependency array to prevent potential stale data when components change.

Apply this diff:

  }, [entities]);
+ }, [entities, components]);

36-40: Remove redundant Has(components.Owner) from the query.

The HasValue(components.Owner, ...) condition already ensures that entities have the Owner component. Including Has(components.Owner) is redundant and can be removed to simplify the query.

Apply this diff:

const entities = useEntityQuery([
  Has(components.Structure),
  Has(components.Position),
- Has(components.Owner),
  HasValue(components.Owner, { address: playerAddress || ContractAddress(account.address) }),
]);
client/apps/game/src/ui/components/cityview/realm/settle-realm-component.tsx (1)

Line range hint 27-49: Improve accessibility by replacing the clickable div with a button element.

The SeasonPassRealm component uses a clickable div, which may not be accessible to keyboard users or screen readers. Replacing it with a button element enhances accessibility.

Apply this diff:

- <div
+ <button
    key={seasonPassRealm.realmId}
    className={`flex flex-col gap-2 p-2 rounded-md bg-gold/10 transition-colors duration-200 border border-2 ${
      selected ? "border-gold bg-gold/30" : "border-transparent"
    } ${className} hover:border-gold`}
    onClick={() => setSelected(!selected)}
+   type="button"
  >
    ...
- </div>
+ </button>
client/apps/game/src/ui/components/hyperstructures/contribution-summary.tsx (1)

Line range hint 93-100: Optimize sorting logic for contributors.

Consider handling BigInt comparisons carefully to ensure correct sorting of contributors based on their contributions.

Apply this diff to use BigInt comparison methods:

- return amountA > amountB ? -1 : amountA < amountB ? 1 : 0;
+ return amountB - amountA;
client/apps/game/src/ui/components/bank/resource-bar.tsx (1)

151-154: Optimize balance retrieval with useMemo.

Memoize the balance computation to prevent unnecessary recalculations during renders, enhancing performance.

Apply this diff to implement memoization:

+ const resourceBalance = useMemo(() => {
+   return divideByPrecision(getBalance(entityId, resource.id, dojo.setup.components).balance);
+ }, [entityId, resource.id, dojo.setup.components]);

<ResourceCost
  resourceId={resource.id}
- amount={divideByPrecision(getBalance(entityId, resource.id, dojo.setup.components).balance)}
+ amount={resourceBalance}
  className="border-0 bg-transparent"
/>
client/apps/game/src/hooks/helpers/use-players.tsx (1)

14-22: Consider adding error handling for edge cases.

The implementation is clean and efficient with proper memoization. However, consider adding error handling for potential edge cases:

  1. Invalid addressName format
  2. Empty or malformed name strings
 const players = useMemo(() => {
   return entities
     .map((id) => {
       const addressName = getComponentValue(components.AddressName, id);
-      if (!addressName) return;
+      if (!addressName?.address || !addressName?.name) return;
+      const name = addressName.name.toString().trim();
+      if (!name) return;
       return { entity: id, address: addressName.address, name: addressName.name.toString() };
     })
     .filter(Boolean) as Player[];
 }, [entities]);
client/apps/game/src/hooks/helpers/use-structure-entity-id.tsx (1)

Line range hint 33-52: Consider simplifying effect logic

The effect contains complex conditional logic and many dependencies which could lead to unnecessary re-renders.

Consider:

  1. Extracting the position calculation logic
  2. Memoizing the structure query results
  3. Splitting into smaller, focused effects
+ const position = useMemo(() => {
+   return new PositionInterface({
+     x: hexPosition.col,
+     y: hexPosition.row,
+   }).getContract();
+ }, [hexPosition]);

+ const structureQuery = useMemo(() => {
+   const { x, y } = position;
+   return runQuery([Has(Structure), HasValue(Position, { x, y })])
+     .values()
+     .next().value;
+ }, [position]);

  useEffect(() => {
-   const { x, y } = new PositionInterface({
-     x: hexPosition.col,
-     y: hexPosition.row,
-   }).getContract();

-   const structureEntity = runQuery([Has(Structure), HasValue(Position, { x, y })])
-     .values()
-     .next().value;
+   const structureEntity = structureQuery;
client/apps/game/src/hooks/helpers/use-battles.tsx (1)

43-43: Good architectural improvement!

The refactoring improves type safety and modularity by:

  1. Using ContractAddress for account addresses
  2. Using the more specific usePlayerRealms hook instead of the generic useEntities

Consider documenting this pattern of using specific hooks over generic ones in the team's coding guidelines, as it improves maintainability and type safety.

Also applies to: 49-49

client/apps/game/src/ui/modules/social/end-season-button.tsx (1)

51-55: Consider memoizing LeaderboardManager instance.

While the change to use LeaderboardManager is good for centralization, the instance creation in the render cycle could be optimized.

+const leaderboardManager = useMemo(
+  () => LeaderboardManager.instance(setup.components),
+  [setup.components]
+);
 const endGame = useCallback(async () => {
   if (!hasReachedFinalPoints) {
     return;
   }
   const contributions = Array.from(
-    LeaderboardManager.instance(setup.components).getHyperstructuresWithContributionsFromPlayer(
+    leaderboardManager.getHyperstructuresWithContributionsFromPlayer(
       ContractAddress(account.address),
     ),
   );
client/apps/game/src/ui/components/trading/realm-production.tsx (1)

81-85: Consider accessibility improvements for resource icons.

The resource icons should include proper aria labels for screen readers.

 {realm.resourcesProduced.map((resourceId) => (
   <ResourceIcon
     resource={ResourcesIds[resourceId]}
     size="sm"
     key={resourceId}
+    aria-label={`Resource: ${ResourcesIds[resourceId]}`}
   />
 ))}
client/apps/game/src/ui/components/trading/select-resources.tsx (1)

1-1: LGTM! Consistent refactoring approach.

The changes align with the broader refactoring pattern, maintaining consistent usage of utility functions with dojo context.

However, there's a potential optimization opportunity:

Consider memoizing the balance calculations to prevent unnecessary recalculations:

+const resourceBalance = useMemo(() => 
+  getBalance(entity_id, id, dojo.setup.components),
+  [entity_id, id, dojo.setup.components]
+);
+
 const options = [orderedResources.find((res) => res.id === id), ...unselectedResources].map((res: any) => ({
   id: res.id,
   label: (
     <ResourceCost
       resourceId={res.id}
-      amount={divideByPrecision(getBalance(entity_id, res.id, dojo.setup.components)?.balance || 0)}
+      amount={divideByPrecision(resourceBalance?.balance || 0)}
     />
   ),
 }));

Also applies to: 8-8, 25-25, 55-55, 60-63

client/apps/game/src/ui/components/entities/entity.tsx (1)

8-10: LGTM! Consistent refactoring with improved type safety.

The changes maintain the same functionality while providing better type safety through explicit contract addresses and components.

However, there's a potential performance optimization:

Consider memoizing the entity info to prevent unnecessary recalculations on each render:

+const entityInfo = useMemo(() => 
+  getEntityInfo(
+    arrival.entityId,
+    ContractAddress(dojo.account.account.address),
+    dojo.setup.components
+  ),
+  [arrival.entityId, dojo.account.account.address, dojo.setup.components]
+);
-const entity = getEntityInfo(arrival.entityId, ContractAddress(dojo.account.account.address), dojo.setup.components);

Also applies to: 34-35, 41-41, 44-44, 53-53, 58-58

client/apps/game/src/utils/army.ts (2)

108-116: Consider using BigInt comparison for troop counts.

The Number() conversion of BigInt values could potentially lead to precision loss for very large numbers. Consider using BigInt comparison instead.

-      (Number(army.troops.knight_count) !== 0 ||
-        Number(army.troops.crossbowman_count) !== 0 ||
-        Number(army.troops.paladin_count) !== 0),
+      (army.troops.knight_count !== 0n ||
+        army.troops.crossbowman_count !== 0n ||
+        army.troops.paladin_count !== 0n),

118-122: Remove redundant null check in condition.

The army && check is redundant since the entityArmies parameter type is ArmyInfo[] (not optional). Also, realmPosition is a required parameter, so its null check is unnecessary.

-  return entityArmies.some(
-    (army) => army && realmPosition && (army.position.x !== realmPosition.x || army.position.y !== realmPosition.y),
-  );
+  return entityArmies.some(
+    (army) => army.position.x !== realmPosition.x || army.position.y !== realmPosition.y
+  );
client/apps/game/src/ui/modules/social/social.tsx (1)

40-42: Consider memoizing initial playerInfo calculation.

The initial playerInfo calculation could be memoized using useMemo to avoid unnecessary recalculations during renders.

-  const [playerInfo, setPlayerInfo] = useState<PlayerInfo[]>(
-    getPlayerInfo(players, ContractAddress(account.address), components),
-  );
+  const initialPlayerInfo = useMemo(
+    () => getPlayerInfo(players, ContractAddress(account.address), components),
+    [players, account.address, components]
+  );
+  const [playerInfo, setPlayerInfo] = useState<PlayerInfo[]>(initialPlayerInfo);
client/apps/game/src/ui/components/structures/construction/structure-construction-menu.tsx (1)

42-45: Consider memoizing balance checks.

The checkBalance function is recreated on every render and performs balance checks for each resource. Consider memoizing the results to avoid unnecessary recalculations.

-  const checkBalance = (cost: any) =>
-    Object.keys(cost).every((resourceId) => {
-      const resourceCost = cost[Number(resourceId)];
-      const balance = getBalance(entityId, resourceCost.resource, dojo.setup.components);
-      return balance.balance >= multiplyByPrecision(resourceCost.amount);
-    });
+  const checkBalance = useMemo(
+    () => (cost: any) =>
+      Object.keys(cost).every((resourceId) => {
+        const resourceCost = cost[Number(resourceId)];
+        const balance = getBalance(entityId, resourceCost.resource, dojo.setup.components);
+        return balance.balance >= multiplyByPrecision(resourceCost.amount);
+      }),
+    [entityId, dojo.setup.components]
+  );
client/apps/game/src/ui/modules/entity-details/building-entity-details.tsx (1)

52-56: Consider extracting complex parameter passing.

The getEntityInfo call with multiple parameters could be simplified by creating a wrapper function or custom hook.

+const useEntityInfo = (entityId: ID) => {
+  const dojo = useDojo();
+  return getEntityInfo(
+    entityId,
+    ContractAddress(dojo.account.account.address),
+    dojo.setup.components
+  );
+};

-const selectedStructureInfo = getEntityInfo(
-  structureEntityId,
-  ContractAddress(dojo.account.account.address),
-  dojo.setup.components,
-);
+const selectedStructureInfo = useEntityInfo(structureEntityId);

Also applies to: 79-79

client/apps/game/src/ui/modules/navigation/quest-menu.tsx (1)

204-207: Consider using React Context for components prop.

The components prop is being passed down through multiple levels. Consider using React Context to avoid prop drilling.

+const ComponentsContext = React.createContext<Components | null>(null);

+export const ComponentsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+  const { setup: { components } } = useDojo();
+  return (
+    <ComponentsContext.Provider value={components}>
+      {children}
+    </ComponentsContext.Provider>
+  );
+};

+const useComponents = () => {
+  const context = React.useContext(ComponentsContext);
+  if (!context) {
+    throw new Error('useComponents must be used within a ComponentsProvider');
+  }
+  return context;
+};

-const QuestRewards = ({ realmEntityId, prizes }: { realmEntityId: ID; prizes: Prize[] | undefined }) => {
-  const {
-    setup: { components },
-  } = useDojo();
+const QuestRewards = ({ realmEntityId, prizes }: { realmEntityId: ID; prizes: Prize[] | undefined }) => {
+  const components = useComponents();

Also applies to: 214-214

client/apps/game/src/ui/modules/entity-details/realm/buildings.tsx (1)

29-29: Verify the impact of null coalescing.

The use of || for fallback values might not handle all edge cases. Consider using nullish coalescing (??) which only falls back on null or undefined:

-  const buildings = useBuildings(realm?.position.x || 0, realm?.position.y || 0);
+  const buildings = useBuildings(realm?.position.x ?? 0, realm?.position.y ?? 0);
client/apps/game/src/hooks/helpers/use-structures.tsx (1)

46-46: Consider adding null checks for getEntityName results.

While updating the API to include components is good, the function calls might return undefined. Consider adding null coalescing operators or default values:

-    const name = getEntityName(structure.entity_id, components);
+    const name = getEntityName(structure.entity_id, components) ?? "";

Also applies to: 90-90, 133-133, 180-180

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2282fe and f4842ce.

📒 Files selected for processing (82)
  • client/apps/game/src/hooks/helpers/use-battle-events.tsx (3 hunks)
  • client/apps/game/src/hooks/helpers/use-battles.tsx (2 hunks)
  • client/apps/game/src/hooks/helpers/use-buildings.tsx (2 hunks)
  • client/apps/game/src/hooks/helpers/use-contributions.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-entities.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-fragment-mines.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-get-all-players.tsx (0 hunks)
  • client/apps/game/src/hooks/helpers/use-guilds.tsx (10 hunks)
  • client/apps/game/src/hooks/helpers/use-hyperstructures.tsx (3 hunks)
  • client/apps/game/src/hooks/helpers/use-players.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-quests.tsx (4 hunks)
  • client/apps/game/src/hooks/helpers/use-realm.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-resource-arrivals.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-resources.tsx (1 hunks)
  • client/apps/game/src/hooks/helpers/use-structure-entity-id.tsx (2 hunks)
  • client/apps/game/src/hooks/helpers/use-structures.tsx (9 hunks)
  • client/apps/game/src/hooks/helpers/use-trade.tsx (3 hunks)
  • client/apps/game/src/hooks/use-starting-tutorial.tsx (1 hunks)
  • client/apps/game/src/ui/components/bank/add-liquidity.tsx (4 hunks)
  • client/apps/game/src/ui/components/bank/liquidity-table.tsx (3 hunks)
  • client/apps/game/src/ui/components/bank/resource-bar.tsx (4 hunks)
  • client/apps/game/src/ui/components/bank/swap.tsx (2 hunks)
  • client/apps/game/src/ui/components/battles/battle-list-item.tsx (2 hunks)
  • client/apps/game/src/ui/components/cityview/realm/settle-realm-component.tsx (1 hunks)
  • client/apps/game/src/ui/components/construction/select-preview-building.tsx (9 hunks)
  • client/apps/game/src/ui/components/entities/entity.tsx (3 hunks)
  • client/apps/game/src/ui/components/fragmentMines/fragment-mine-panel.tsx (1 hunks)
  • client/apps/game/src/ui/components/hyperstructures/co-owners.tsx (5 hunks)
  • client/apps/game/src/ui/components/hyperstructures/contribution-summary.tsx (5 hunks)
  • client/apps/game/src/ui/components/hyperstructures/hyperstructure-panel.tsx (4 hunks)
  • client/apps/game/src/ui/components/hyperstructures/hyperstructure-resource-chip.tsx (2 hunks)
  • client/apps/game/src/ui/components/hyperstructures/leaderboard.tsx (4 hunks)
  • client/apps/game/src/ui/components/hyperstructures/resource-exchange.tsx (3 hunks)
  • client/apps/game/src/ui/components/military/army-chip.tsx (1 hunks)
  • client/apps/game/src/ui/components/military/army-list.tsx (1 hunks)
  • client/apps/game/src/ui/components/military/army-management-card.tsx (3 hunks)
  • client/apps/game/src/ui/components/military/entities-army-table.tsx (3 hunks)
  • client/apps/game/src/ui/components/military/pillage-history.tsx (4 hunks)
  • client/apps/game/src/ui/components/quest/quest-details.ts (1 hunks)
  • client/apps/game/src/ui/components/resources/deposit-resources.tsx (1 hunks)
  • client/apps/game/src/ui/components/resources/inventory-resources.tsx (2 hunks)
  • client/apps/game/src/ui/components/resources/realm-resources-io.tsx (1 hunks)
  • client/apps/game/src/ui/components/resources/realm-transfer.tsx (3 hunks)
  • client/apps/game/src/ui/components/resources/travel-info.tsx (3 hunks)
  • client/apps/game/src/ui/components/structures/construction/structure-construction-menu.tsx (5 hunks)
  • client/apps/game/src/ui/components/trading/market-modal.tsx (3 hunks)
  • client/apps/game/src/ui/components/trading/market-order-panel.tsx (2 hunks)
  • client/apps/game/src/ui/components/trading/realm-production.tsx (4 hunks)
  • client/apps/game/src/ui/components/trading/resource-arrivals.tsx (1 hunks)
  • client/apps/game/src/ui/components/trading/select-entity-from-list.tsx (2 hunks)
  • client/apps/game/src/ui/components/trading/select-resources.tsx (3 hunks)
  • client/apps/game/src/ui/components/trading/trade-history-event.tsx (3 hunks)
  • client/apps/game/src/ui/components/trading/transfer-between-entities.tsx (3 hunks)
  • client/apps/game/src/ui/components/trading/transfer-view.tsx (3 hunks)
  • client/apps/game/src/ui/components/worldmap/armies/action-info.tsx (5 hunks)
  • client/apps/game/src/ui/components/worldmap/armies/army-info-label.tsx (3 hunks)
  • client/apps/game/src/ui/components/worldmap/guilds/guild-members.tsx (3 hunks)
  • client/apps/game/src/ui/components/worldmap/guilds/guilds.tsx (2 hunks)
  • client/apps/game/src/ui/components/worldmap/players/player-list.tsx (1 hunks)
  • client/apps/game/src/ui/components/worldmap/players/players-panel.tsx (2 hunks)
  • client/apps/game/src/ui/layouts/world.tsx (5 hunks)
  • client/apps/game/src/ui/modules/chat/chat.tsx (2 hunks)
  • client/apps/game/src/ui/modules/entity-details/building-entity-details.tsx (4 hunks)
  • client/apps/game/src/ui/modules/entity-details/enemy-armies.tsx (3 hunks)
  • client/apps/game/src/ui/modules/entity-details/realm/buildings.tsx (3 hunks)
  • client/apps/game/src/ui/modules/entity-details/realm/castle.tsx (3 hunks)
  • client/apps/game/src/ui/modules/military/battle-view/battle-side-view.tsx (3 hunks)
  • client/apps/game/src/ui/modules/military/battle-view/battle-twitter-share-button.tsx (3 hunks)
  • client/apps/game/src/ui/modules/military/military.tsx (1 hunks)
  • client/apps/game/src/ui/modules/navigation/left-navigation-module.tsx (4 hunks)
  • client/apps/game/src/ui/modules/navigation/quest-menu.tsx (5 hunks)
  • client/apps/game/src/ui/modules/navigation/top-left-navigation.tsx (3 hunks)
  • client/apps/game/src/ui/modules/navigation/top-navigation.tsx (1 hunks)
  • client/apps/game/src/ui/modules/rewards/rewards.tsx (5 hunks)
  • client/apps/game/src/ui/modules/settings/settings.tsx (1 hunks)
  • client/apps/game/src/ui/modules/social/end-season-button.tsx (2 hunks)
  • client/apps/game/src/ui/modules/social/player-id.tsx (4 hunks)
  • client/apps/game/src/ui/modules/social/social.tsx (5 hunks)
  • client/apps/game/src/ui/modules/stream/event-stream.tsx (2 hunks)
  • client/apps/game/src/ui/modules/world-structures/world-structures-menu.tsx (6 hunks)
  • client/apps/game/src/ui/utils/realms.tsx (1 hunks)
  • client/apps/game/src/utils/army.ts (1 hunks)
⛔ Files not processed due to max files limit (8)
  • client/apps/game/src/utils/entities.ts
  • client/apps/game/src/utils/players.ts
  • client/apps/game/src/utils/realm.ts
  • client/apps/game/src/utils/resources.ts
  • client/sdk/packages/eternum/src/constants/resources.ts
  • client/sdk/packages/eternum/src/modelManager/LeaderboardManager.ts
  • client/sdk/packages/eternum/src/types/common.ts
  • client/sdk/packages/eternum/src/utils/index.ts
💤 Files with no reviewable changes (1)
  • client/apps/game/src/hooks/helpers/use-get-all-players.tsx
✅ Files skipped from review due to trivial changes (2)
  • client/apps/game/src/ui/components/quest/quest-details.ts
  • client/apps/game/src/ui/components/resources/deposit-resources.tsx
🔇 Additional comments (116)
client/apps/game/src/ui/components/worldmap/armies/action-info.tsx (4)

9-9: LGTM! Good refactoring of resource balance handling.

The change from useResourceBalance hook to a utility function improves testability and follows a more functional approach.


27-29: Great type safety improvements!

The changes enhance type safety by:

  • Using more precise number type for entity IDs instead of string
  • Adding explicit type definition for the getBalance function

128-128: Good addition of null check!

Adding the null check for selectedEntityId improves the component's safety by preventing potential runtime errors.


136-138: LGTM! Clean implementation of getBalance.

The implementation correctly passes the components context to the utility function while maintaining proper typing.

client/apps/game/src/ui/components/military/army-management-card.tsx (1)

21-21: LGTM! Clean refactoring of resource balance handling.

The change from useResourceBalance hook to getBalance utility function aligns with the PR's objective of centralizing data retrieval.

client/apps/game/src/ui/components/worldmap/players/players-panel.tsx (1)

21-28: Clean component destructuring!

The separation of component destructuring improves code readability and maintainability.

client/apps/game/src/hooks/helpers/use-resources.tsx (2)

2-2: Imports are appropriate and necessary

The imported identifiers ID, ResourceManager, and ResourcesIds from "@bibliothecadao/eternum" are correctly utilized in the code.


4-4: useMemo hook correctly imported

The useMemo hook from React is properly imported and used for memoization.

client/apps/game/src/hooks/helpers/use-contributions.tsx (2)

2-4: Imported modules and types are correctly utilized

The imports from "@bibliothecadao/eternum" and "@dojoengine/recs" are necessary and properly used in the usePlayerContributions hook.


6-19: usePlayerContributions hook effectively retrieves player contributions

The hook efficiently queries and filters contributions related to the specified player and hyperstructure. The use of useEntityQuery, HasValue, and proper type guarding ensures accurate results.

client/apps/game/src/hooks/helpers/use-realm.tsx (4)

2-2: Importing getRealmInfo utility

The getRealmInfo function is correctly imported from "@/utils/realm" and used to construct realm information.


21-21: Ensure getRealmInfo handles undefined components gracefully

The getRealmInfo function should account for potential undefined components to prevent runtime errors. Confirm that it includes necessary checks.

If getRealmInfo already includes these checks, no action is needed.


29-47: useRealms hook correctly retrieves realm data

The useRealms hook efficiently queries all realms and maps their component values. The use of useMemo optimizes performance by memoizing the computed realms.


16-16: Ensure correct type casting with ContractAddress

If account.address is already of type ContractAddress, wrapping it again may be redundant. Verify if additional type casting is necessary.

Run the following script to check the type of account.address and usage:

✅ Verification successful

ContractAddress wrapping is necessary and correct

The ContractAddress wrapping is required here as it ensures type safety when interacting with the Owner component in the entity query system. The Owner component expects a ContractAddress type (which is a specific bigint representation) for contract interactions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the type of account.address in the codebase

# Find where account.address is defined and its type
rg 'account:\s*{\s*account\s*}' -A 5

# Check usages of account.address
rg 'account\.address' -A 2

Length of output: 35894


Script:

#!/bin/bash
# Find type definitions and implementations related to account and ContractAddress

# Look for type definitions of account
rg -t typescript "type.*Account" -B 2 -A 2

# Look for ContractAddress type/function definition
rg -t typescript "ContractAddress.*=|type.*ContractAddress|interface.*ContractAddress" -B 2 -A 2

# Look for direct assignments or comparisons with account.address
ast-grep --pattern 'account.address = $_' -l typescript
ast-grep --pattern '$_ === account.address' -l typescript

Length of output: 331


Script:

#!/bin/bash

# Find TypeScript files that might contain type definitions
fd "\.(ts|tsx)$" | grep -i "type\|interface"

# Look for ContractAddress definition and usage
rg "ContractAddress" -g "*.{ts,tsx}" --no-filename | grep "type\|interface\|function"

# Look for direct account.address usage without ContractAddress wrapping
rg "account\.address" -g "*.{ts,tsx}" --no-filename | grep -v "ContractAddress"

Length of output: 11716

client/apps/game/src/ui/components/hyperstructures/contribution-summary.tsx (3)

20-22: Confirm LeaderboardManager instantiation dependencies.

Ensure that all dependencies required for LeaderboardManager.instance(components) are included and that components encompasses all necessary data for proper initialization.


32-34: Efficient use of useMemo for contributions calculation.

Memoizing contributions with useMemo optimizes performance by avoiding unnecessary recalculations when leaderboardManager or hyperstructureEntityId change.


103-103: Verify synchronous usage of getAddressName.

Confirm that getAddressName is a synchronous function and that it won't cause any unexpected behavior if it performs asynchronous operations.

client/apps/game/src/ui/components/bank/resource-bar.tsx (1)

Line range hint 28-29: Addition of onFocus and onBlur props enhances functionality.

Introducing onFocus and onBlur callbacks improves the component's flexibility by allowing parent components to respond to focus events.

client/apps/game/src/ui/modules/navigation/top-navigation.tsx (2)

10-10: Confirm removal of getPlayers prop in Social component.

Ensure that the Social component functions correctly without the getPlayers prop and that it internally handles player data retrieval.


10-10: Simplified Social component usage is clean and effective.

Removing unnecessary props streamlines the component and reduces complexity.

client/apps/game/src/hooks/use-starting-tutorial.tsx (1)

11-11: Verify the return type of useQuests hook.

The change from destructuring to direct assignment suggests that the useQuests hook's return type has been modified. Please ensure this change is consistent with the hook's new implementation.

client/apps/game/src/ui/modules/military/military.tsx (1)

12-17: LGTM! Clean implementation of the refactored hook usage.

The changes effectively utilize the useDojo context and maintain type safety while simplifying the structure retrieval logic.

client/apps/game/src/hooks/helpers/use-battle-events.tsx (2)

7-27: Great refactoring of battle event logic!

The new useBattleEventData helper effectively consolidates duplicate logic from battle event hooks. The use of TypeScript generics and proper memoization demonstrates good practices.


29-38: Clean implementation of battle event hooks

The refactored hooks (useBattleStart, useBattleJoin, useBattleLeave) are now more maintainable and DRY by leveraging the shared helper function.

Also applies to: 41-54, 57-70

client/apps/game/src/ui/components/resources/realm-resources-io.tsx (2)

15-15: Good: Made realmEntityId prop required

Making the prop required improves type safety by ensuring the component always receives a valid ID.


20-21: Verify type safety of ID conversion

The conversion of realmEntityId to BigInt and passing it to getEntityIdFromKeys could potentially fail if an invalid ID is provided.

✅ Verification successful

ID conversion is properly type-safe

The conversion is safe as the realmEntityId comes from a validated Structure entity and is only used when the structure type is explicitly checked. The pattern is part of the core architecture and is properly type-safe through TypeScript's type system and runtime validation in parent components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for proper ID validation across the codebase
rg -A 2 "getEntityIdFromKeys\(\[BigInt\(" 

Length of output: 52223


Script:

#!/bin/bash
# Search for the component definition and type of realmEntityId
ast-grep --pattern 'interface $_ {
  $$$
  realmEntityId: $_
  $$$
}'

# Search for any validation or try-catch around BigInt conversion
rg -A 2 "try.*BigInt\(" 
rg "if.*realmEntityId"

Length of output: 795


Script:

#!/bin/bash
# Find the component definition and usage
rg -A 5 "realm-resources-io" 
ast-grep --pattern 'const RealmResourcesIO = ({ $_ }: { $_ }) => {'

# Find where realmEntityId is passed to this component
rg -B 3 "<RealmResourcesIO.*realmEntityId="

Length of output: 3332

client/apps/game/src/hooks/helpers/use-structure-entity-id.tsx (1)

26-26: Good: Using typed ContractAddress for player structures

The switch to usePlayerStructures with proper typing improves type safety.

client/apps/game/src/ui/components/fragmentMines/fragment-mine-panel.tsx (1)

19-19: Good: Properly passing components to utility function

The update to include components parameter in getAddressNameFromEntity improves modularity.

client/apps/game/src/ui/utils/realms.tsx (1)

2-2: Verify impact of removing wonder prefix functionality

The removal of getRealmName function eliminates the "WONDER" prefix for realms with wonders. This might affect UI components that relied on this distinction.

Let's check for any UI components that might be affected:

✅ Verification successful

No impact found from removing wonder prefix functionality

The affected components are already using getRealmNameById and don't rely on the wonder prefix functionality. The removal of getRealmName function appears to be a safe cleanup.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components that might have relied on the removed getRealmName function
rg -l "getRealmName|has_wonder.*realm" "client/apps/game/src/ui/components"

Length of output: 207


Script:

#!/bin/bash
# Check the content of affected files
echo "=== transfer-view.tsx ==="
rg -A 5 -B 5 "getRealmName|has_wonder.*realm" "client/apps/game/src/ui/components/trading/transfer-view.tsx"

echo -e "\n=== army-info-label.tsx ==="
rg -A 5 -B 5 "getRealmName|has_wonder.*realm" "client/apps/game/src/ui/components/worldmap/armies/army-info-label.tsx"

Length of output: 2376

client/apps/game/src/ui/components/bank/liquidity-table.tsx (1)

45-45: Good simplification of data flow

The changes improve the code by:

  1. Using the specific usePlayerStructures hook
  2. Simplifying playerStructureIds derivation
  3. Adding proper typing with ContractAddress

Also applies to: 47-47

client/apps/game/src/hooks/helpers/use-buildings.tsx (2)

9-19: Good improvement in hook specificity and performance

The useBuildings hook now properly requires location parameters and uses useEntityQuery with useMemo for better performance.


59-79: Review the dependency array in useBuildingQuantities

The useMemo dependency array might cause unnecessary rerenders or stale data:

  1. entityUpdate is an array-like object that might cause reference equality issues
  2. Consider using individual building quantities as dependencies instead

Let's check for potential issues:

Consider refactoring the dependency array:

   return useMemo(
     () => ({
       food: getBuildingQuantity(BuildingType.Farm) + getBuildingQuantity(BuildingType.FishingVillage),
       resource: getBuildingQuantity(BuildingType.Resource),
       workersHut: getBuildingQuantity(BuildingType.WorkersHut),
       markets: getBuildingQuantity(BuildingType.Market),
     }),
-    [structureEntityId, entityUpdate],
+    [structureEntityId, 
+     getBuildingQuantity(BuildingType.Farm),
+     getBuildingQuantity(BuildingType.FishingVillage),
+     getBuildingQuantity(BuildingType.Resource),
+     getBuildingQuantity(BuildingType.WorkersHut),
+     getBuildingQuantity(BuildingType.Market)],
   );
client/apps/game/src/ui/components/trading/trade-history-event.tsx (2)

1-1: LGTM! Refactoring aligns with centralized context pattern.

The change from useEntitiesUtils to useDojo follows a better pattern of accessing shared components through a centralized context.

Also applies to: 28-30


39-39: Verify the impact of the getAddressNameFromEntity signature change.

The function now requires the components parameter. While the change here is correct, we should ensure all other usages of this function have been updated.

✅ Verification successful

All usages of getAddressNameFromEntity are properly updated

All occurrences of the function across the codebase include both the entityId and components parameters. No additional updates are needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining calls to getAddressNameFromEntity that might not include the components parameter
rg "getAddressNameFromEntity\([^,)]*\)" --type ts

Length of output: 51


Script:

#!/bin/bash
# Search for all occurrences of getAddressNameFromEntity with surrounding context
rg -i "getAddressNameFromEntity" --type ts -B 2 -A 2

Length of output: 15487

client/apps/game/src/ui/components/trading/realm-production.tsx (1)

21-30: LGTM! Good use of memoization for derived data.

The memoization of realmsProduction prevents unnecessary recalculations of the transformed data.

client/apps/game/src/ui/components/trading/resource-arrivals.tsx (1)

10-10: LGTM! Good move to centralize type definitions.

Moving the ArrivalInfo type to @bibliothecadao/eternum improves type sharing across the project.

client/apps/game/src/ui/components/hyperstructures/leaderboard.tsx (1)

23-23: LGTM! Consistent usage of components.

The changes improve consistency by explicitly passing components to utility functions and using the correct component paths.

Also applies to: 51-51, 76-76

client/apps/game/src/ui/components/trading/transfer-view.tsx (1)

2-2: LGTM! Improved modularity with specific hooks.

The changes enhance modularity by replacing the generic useEntities hook with specific hooks for player realms and structures.

Also applies to: 4-4, 20-21

client/apps/game/src/ui/components/hyperstructures/hyperstructure-resource-chip.tsx (1)

1-1: LGTM! Refactoring improves code modularity.

The transition from custom hooks to utility functions with explicit dojo context makes the data flow more transparent and maintainable.

However, let's verify that all callers of these utility functions have been updated with the new signature.

Also applies to: 8-8, 29-30, 34-35

client/apps/game/src/ui/components/worldmap/armies/army-info-label.tsx (1)

39-47: LGTM! Good use of hook refactoring pattern.

The changes properly implement the hook refactoring pattern by:

  1. Using useDojo to access components centrally
  2. Passing components explicitly to getRealmAddressName
client/apps/game/src/ui/modules/military/battle-view/battle-side-view.tsx (1)

8-8: LGTM! Refactoring improves modularity.

The change from using useEntitiesUtils hook to direct getAddressNameFromEntity function import with explicit components parameter improves code clarity and maintainability.

Also applies to: 97-97

client/apps/game/src/ui/components/military/army-list.tsx (1)

10-10: LGTM! Type safety improvement.

Adding explicit PlayerStructure type improves type safety and makes the component's requirements clearer.

client/apps/game/src/ui/components/battles/battle-list-item.tsx (1)

10-10: LGTM! Consistent with the hook refactoring pattern.

The change from useEntitiesUtils hook to direct getAddressNameFromEntity function import maintains consistency with the broader refactoring effort.

Also applies to: 108-108

client/apps/game/src/hooks/helpers/use-resource-arrivals.tsx (1)

3-9: LGTM! Improved code organization.

Moving types and constants to @bibliothecadao/eternum reduces duplication and centralizes shared definitions.

client/apps/game/src/ui/components/worldmap/guilds/guilds.tsx (1)

10-10: LGTM! Type renaming is consistent.

The change from Player to PlayerInfo type is consistent with the refactoring mentioned in the PR summary.

Also applies to: 19-19

client/apps/game/src/ui/components/military/pillage-history.tsx (2)

18-18: LGTM! Components dependency is now explicit.

The changes improve code clarity by making the dependency on components explicit in utility function calls.

Also applies to: 27-27, 34-34


132-141: LGTM! Query definition is more concise.

The query definition and subscription logic are now more streamlined with direct usage of components.

client/apps/game/src/ui/components/bank/add-liquidity.tsx (2)

2-2: LGTM! Improved modularity with specific hooks.

The change from useEntities to usePlayerStructures improves code organization by using a more focused hook.

Also applies to: 29-29, 31-31


64-65: LGTM! Balance retrieval is more explicit.

The balance retrieval now correctly includes the components context, making the dependencies clear.

client/apps/game/src/ui/modules/social/player-id.tsx (2)

59-67: LGTM! Improved component organization.

The component destructuring is now more organized and maintainable.


82-82: LGTM! Utility function calls are more explicit.

The utility functions now correctly receive the components context, making dependencies clear.

Also applies to: 111-111

client/apps/game/src/ui/components/worldmap/guilds/guild-members.tsx (2)

10-10: LGTM! Type safety improvements.

The changes improve type safety by using the more specific PlayerInfo type instead of the generic Player type.

Also applies to: 15-15


26-26: LGTM! Enhanced context through components.

The addition of components to the getGuildFromEntityId function call provides better context for guild data retrieval.

Also applies to: 39-39

client/apps/game/src/ui/components/hyperstructures/resource-exchange.tsx (1)

6-6: LGTM! Improved resource management.

The changes enhance resource management by:

  1. Centralizing resource-related utilities through the imported getResourcesFromBalance function
  2. Providing better context through the components parameter

Also applies to: 48-49

client/apps/game/src/hooks/helpers/use-trade.tsx (1)

2-2: LGTM! Enhanced player-specific data handling.

The changes improve the code by:

  1. Using the more specific usePlayerRealms hook instead of the generic useEntities
  2. Simplifying the trade filtering logic using array mapping

Also applies to: 169-170, 181-181

client/apps/game/src/hooks/helpers/use-quests.tsx (4)

6-8: LGTM! Better organization of utility functions.

The changes improve code organization by centralizing utility functions and type imports.


18-36: LGTM! Enhanced quest management.

The changes improve the code by:

  1. Defining quest types as a constant array for better maintainability
  2. Using useMemo for efficient quest generation

55-59: LGTM! Improved position retrieval.

The changes enhance position retrieval by using the getEntityInfo utility with proper context through components.


89-89: LGTM! Simplified quest count management.

The changes simplify the unclaimed quests count management by directly returning the count.

Also applies to: 208-208

client/apps/game/src/ui/components/worldmap/players/player-list.tsx (1)

8-8: LGTM! Type updates look good.

The change from Player to PlayerInfo type is consistent with the broader refactoring effort.

Also applies to: 12-12

client/apps/game/src/ui/modules/stream/event-stream.tsx (2)

5-5: LGTM! Direct imports improve code clarity.

Importing utility functions directly instead of through a hook makes the dependencies more explicit.


51-52: Verify the impact of function signature changes.

The utility functions now require additional parameters (components). Let's verify that all callers have been updated correctly.

Also applies to: 58-60

✅ Verification successful

All function calls are properly updated

A thorough search of the codebase confirms that all calls to both getAddressNameFromEntity and getPlayerAddressFromEntity include the required components parameter. No outdated function calls were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining calls to these functions without the components parameter
rg "getAddressNameFromEntity\([^,]+" --type ts --type tsx
rg "getPlayerAddressFromEntity\([^,]+" --type ts --type tsx

Length of output: 177


Script:

#!/bin/bash
# Search for function calls using ast-grep
# First, find getAddressNameFromEntity calls
ast-grep --pattern 'getAddressNameFromEntity($_)'

# Then search for getPlayerAddressFromEntity calls
ast-grep --pattern 'getPlayerAddressFromEntity($_)'

Length of output: 105


Script:

#!/bin/bash
# Find function definitions and usages with broader patterns
rg "function.*getAddressNameFromEntity|const.*getAddressNameFromEntity" -t typescript
rg "function.*getPlayerAddressFromEntity|const.*getPlayerAddressFromEntity" -t typescript

# Also search for imports of these functions
rg "import.*getAddressNameFromEntity" -t typescript
rg "import.*getPlayerAddressFromEntity" -t typescript

Length of output: 429


Script:

#!/bin/bash
# Find all TypeScript files and search within them
fd -e ts -e tsx | xargs rg "getAddressNameFromEntity"
fd -e ts -e tsx | xargs rg "getPlayerAddressFromEntity"

Length of output: 5463

client/apps/game/src/ui/modules/entity-details/building-entity-details.tsx (1)

3-3: LGTM! Hook replacement improves specificity.

Using usePlayerStructures instead of the generic useEntities better reflects the intent of the code.

Also applies to: 50-50

client/apps/game/src/ui/modules/navigation/quest-menu.tsx (1)

2-2: LGTM! Import organization improves clarity.

The changes better organize imports and hooks, making dependencies clearer.

Also applies to: 8-9, 25-25, 36-36

client/apps/game/src/ui/modules/entity-details/realm/buildings.tsx (2)

24-27: Good use of useMemo for caching realm info!

The memoization of getRealmInfo prevents unnecessary recalculations when dependencies haven't changed, improving performance.


59-61: LGTM! Improved null safety.

Good defensive programming by moving the owner check after the realm null check.

client/apps/game/src/hooks/helpers/use-hyperstructures.tsx (2)

28-28: LGTM! Improved component organization.

Good separation of concerns by first destructuring the components object and then individual components. This makes the component dependencies more explicit and easier to maintain.

Also applies to: 31-31


49-49: Verify the impact of the API change.

The getAddressNameFromEntity function now requires the components parameter. Ensure all other call sites have been updated accordingly.

✅ Verification successful

All call sites properly updated

All occurrences of getAddressNameFromEntity in the codebase have been verified to include both the entityId and components parameters as required.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining calls to getAddressNameFromEntity that might be missing the components parameter
rg "getAddressNameFromEntity\([^,]+" --type ts --type tsx

Length of output: 87


Script:

#!/bin/bash
# Search for all occurrences of getAddressNameFromEntity using ripgrep
rg "getAddressNameFromEntity" --type typescript --type-add "typescript:*.{ts,tsx}"

# Use ast-grep to find function calls
ast-grep --pattern 'getAddressNameFromEntity($_)'

# Also search for the function definition to understand the required parameters
ast-grep --pattern 'function getAddressNameFromEntity($_) {
  $$$
}'

Length of output: 4080

client/apps/game/src/ui/modules/rewards/rewards.tsx (1)

46-48: LGTM! Good use of singleton pattern.

Using LeaderboardManager.instance with useMemo ensures consistent state management and prevents unnecessary reinstantiation.

client/apps/game/src/ui/components/resources/realm-transfer.tsx (3)

2-2: LGTM! Import changes align with hook refactoring.

The replacement of useEntities with usePlayerStructures improves modularity by using a more specific hook.


36-36: LGTM! Simplified player structures access.

The change improves code readability by directly accessing the player structures array instead of using a function call.


Line range hint 110-117: LGTM! Player structures mapping is correct.

The mapping logic properly handles player structures and passes all required props to the RealmTransferBalance component.

client/apps/game/src/ui/modules/navigation/left-navigation-module.tsx (2)

1-1: LGTM! Import changes improve modularity.

The updated imports better organize dependencies and align with the hook refactoring effort.

Also applies to: 12-13


54-58: LGTM! Enhanced entity info retrieval.

The changes properly integrate the Dojo context and improve entity info retrieval by providing necessary parameters.

Also applies to: 72-72

client/apps/game/src/ui/components/hyperstructures/co-owners.tsx (4)

3-3: LGTM! Import changes improve code organization.

The addition of specific hooks and utilities enhances modularity and code organization.

Also applies to: 13-13


57-60: LGTM! Improved components setup.

The explicit destructuring of components enhances code readability and maintainability.


119-119: LGTM! Enhanced player name retrieval.

The updated getAddressName call properly includes components for context.


179-179: LGTM! Simplified player data retrieval.

The use of the dedicated usePlayers hook improves code clarity and maintainability.

client/apps/game/src/ui/modules/settings/settings.tsx (3)

22-22: LGTM! Import change improves modularity.

The addition of the getAddressName utility import enhances code organization.


30-30: LGTM! Added components setup.

The proper integration of components from the Dojo context enhances functionality.


35-35: LGTM! Enhanced address name retrieval.

The updated getAddressName call properly includes components for context.

client/apps/game/src/hooks/helpers/use-guilds.tsx (2)

172-183: LGTM! Clean destructuring of components.

The components are now properly destructured from dojo.setup.components, improving code readability and maintainability.


206-227: LGTM! Well-implemented memoized function.

The getGuildFromEntityId function is properly memoized with appropriate dependencies, preventing unnecessary recalculations and improving performance.

client/apps/game/src/ui/components/military/army-chip.tsx (1)

17-17: LGTM! Import moved to utils.

The armyHasTroops import has been moved to @/utils/army, aligning with the codebase's organization of utility functions.

client/apps/game/src/ui/modules/world-structures/world-structures-menu.tsx (2)

42-48: LGTM! Efficient memoization of hyperstructures.

The myHyperstructures value is now properly memoized with appropriate dependencies, preventing unnecessary recalculations of hyperstructure contributions.


182-184: LGTM! Clean components destructuring.

Components are properly destructured from dojo.setup, improving code readability and maintainability.

client/apps/game/src/ui/components/hyperstructures/hyperstructure-panel.tsx (2)

69-69: LGTM! Using player-specific contributions hook.

The change to usePlayerContributions improves code clarity by explicitly indicating the hook's purpose.


77-77: LGTM! Structured component access.

Using components.Hyperstructure provides a more structured and maintainable way to access components.

client/apps/game/src/ui/layouts/world.tsx (3)

8-8: LGTM! Import changes improve modularity.

The replacement of useEntities with usePlayerStructures makes the code more specific and focused. Adding the PlayerStructure type improves type safety.

Also applies to: 16-16


117-117: LGTM! Hook usage is simplified and dependencies are properly updated.

The direct use of usePlayerStructures hook makes the code cleaner and more maintainable. The useMemo dependencies are correctly updated to reflect the new data source.

Also applies to: 120-121


338-338: LGTM! Props updated consistently with hook changes.

The TopLeftNavigation component now receives playerStructures directly from the new hook, maintaining consistency with the refactoring.

client/apps/game/src/ui/components/trading/transfer-between-entities.tsx (1)

14-14: LGTM! Explicit dependency injection improves maintainability.

The changes properly inject the components dependency into getRealmAddressName, making the code's dependencies more explicit and easier to test.

Also applies to: 253-253

client/apps/game/src/ui/components/bank/swap.tsx (1)

13-13: LGTM! Resource balance handling is more modular.

The changes improve modularity by:

  1. Using a dedicated utility function instead of a hook
  2. Explicitly passing the components dependency
  3. Properly memoizing the balance calculations

Also applies to: 78-85

client/apps/game/src/ui/components/trading/market-modal.tsx (1)

9-9: LGTM! Market modal refactoring improves code organization.

The changes:

  1. Use more specific hooks (useBank, usePlayerStructures)
  2. Simplify structure handling by using the hook directly
  3. Maintain consistent mapping in the Select component

Also applies to: 11-11, 62-62, 218-222

client/apps/game/src/ui/modules/chat/chat.tsx (3)

4-4: LGTM! Hook replacement aligns with refactoring goals.

The replacement of useGetAllPlayers with usePlayers aligns with the PR's objective of centralizing data retrieval.


47-47: LGTM! Clean implementation of the new hook.

The direct usage of usePlayers() simplifies the code by removing the need for additional filtering logic.


Line range hint 276-276: LGTM! Dependencies are properly specified.

The dependency array includes all necessary dependencies for proper memoization.

client/apps/game/src/ui/modules/navigation/top-left-navigation.tsx (4)

15-26: LGTM! Clean import organization.

The imports are well-organized, with related imports grouped together. The addition of ContractAddress and other types from @bibliothecadao/eternum improves type safety.


104-107: LGTM! Proper context destructuring.

The destructuring of dojo context is clean and provides clear access to both setup and account information.


123-126: LGTM! Improved entity info retrieval.

The updated getEntityInfo call now includes the necessary context (ContractAddress(account.address) and setup.components), making the data dependencies explicit.


130-130: LGTM! Optimized dependencies.

The dependency array has been simplified to include only the essential dependencies, which helps prevent unnecessary re-renders.

client/apps/game/src/ui/components/trading/market-order-panel.tsx (2)

19-19: LGTM! Clean import.

The import of getRealmAddressName from the realm utils module is appropriate.


360-361: LGTM! Updated function call with proper context.

The getRealmAddressName call now includes dojo.setup.components, aligning with the refactoring to provide explicit component context.

client/apps/game/src/ui/components/construction/select-preview-building.tsx (4)

23-24: LGTM! Clean utility imports.

The direct imports of utility functions align with the refactoring goal of removing custom hooks in favor of utility functions.


47-47: LGTM! Clean realm info retrieval.

Direct use of getRealmInfo with proper parameters simplifies the code.


77-77: LGTM! Updated balance check.

The getBalance call now includes the required dojo.setup.components parameter.


Line range hint 461-627: LGTM! Consistent balance retrieval pattern.

All instances of getBalance calls have been consistently updated to include dojo.setup.components. The pattern is applied uniformly across resource costs, building costs, and ongoing costs.

client/apps/game/src/ui/components/trading/select-entity-from-list.tsx (3)

1-1: LGTM! Import changes align with hook refactoring.

The replacement of useRealm with useDojo and the direct import of getRealmAddressName from utils aligns with the centralization of data retrieval.

Also applies to: 3-3


22-24: LGTM! Proper destructuring of components from useDojo.

The destructuring pattern follows the standard approach for accessing components through the Dojo context.


31-31: LGTM! Updated function call with required components parameter.

The getRealmAddressName function call has been properly updated to include the required components parameter.

client/apps/game/src/ui/modules/military/battle-view/battle-twitter-share-button.tsx (3)

1-1: LGTM! Import changes align with hook refactoring.

The replacement of entity utilities with useDojo and direct import of getAddressNameFromEntity follows the centralization pattern.

Also applies to: 5-5


26-28: LGTM! Proper destructuring of components from useDojo.

The destructuring pattern follows the standard approach for accessing components through the Dojo context.


40-43: LGTM! Updated function call with required components parameter.

The getAddressNameFromEntity function call has been properly updated to include the required components parameter.

client/apps/game/src/ui/components/resources/travel-info.tsx (3)

2-2: LGTM! Import changes align with hook refactoring.

The replacement of resource balance hooks with useDojo and direct import of getBalance follows the centralization pattern.

Also applies to: 12-12


29-30: LGTM! Proper initialization of dojo context.

The useDojo hook is correctly initialized at the component level.


41-41: LGTM! Updated function call with required components parameter.

The getBalance function call has been properly updated to include the required components parameter.

Comment on lines +7 to +8
import { getEntityName } from "@/utils/entities";
import { ContractAddress, PlayerInfo } from "@bibliothecadao/eternum";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Migration from Player[] to PlayerInfo[] is incomplete

Several components still use the old Player[] type while others have migrated to PlayerInfo[]. This inconsistency needs to be addressed in:

  • client/apps/landing/src/components/modules/player-leaderboard-panel.tsx
  • client/apps/game/src/utils/players.ts
  • client/apps/game/src/ui/elements/select-address.tsx
  • client/apps/game/src/ui/modules/chat/chat.tsx
  • client/apps/game/src/hooks/helpers/use-players.tsx
🔗 Analysis chain

Verify the impact of PlayerInfo type change across components.

The transition from Player[] to PlayerInfo[] and the centralization of entity utilities look good. However, let's verify the consistency of these changes across dependent components.

Also applies to: 16-16

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining Player[] type usage that might need updating
rg "Player\[\]" --type typescript

# Verify PlayerInfo usage in related components
rg "PlayerInfo" --type typescript

# Check for any remaining useEntitiesUtils imports that might need updating
rg "useEntitiesUtils" --type typescript

Length of output: 214


Script:

#!/bin/bash
# Search for Player[] type usage in .ts and .tsx files
rg "Player\[\]" -g "*.{ts,tsx}"

# Check PlayerInfo type usage
rg "PlayerInfo" -g "*.{ts,tsx}"

# Look for any remaining useEntitiesUtils
rg "useEntitiesUtils" -g "*.{ts,tsx}"

# Check getEntityName imports and usage
rg "getEntityName" -g "*.{ts,tsx}"

# Search for potential type definitions
ast-grep --pattern 'type $_ = Player[]'
ast-grep --pattern 'interface $_ { $$$: Player[] }'

Length of output: 10044

Comment on lines +54 to 55
if (!realmInfo) return;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure isLoading is set to false when realmInfo is not available.

If realmInfo is not available, the function returns early without resetting isLoading, leaving the component in a loading state indefinitely.

Apply this diff to fix the issue:

const levelUpRealm = async () => {
  setIsLoading(true);
  if (!realmInfo) {
+   setIsLoading(false);
    return;
  }
  await dojo.setup.systemCalls.upgrade_realm({
    signer: dojo.account.account,
    realm_entity_id: realmInfo.entityId,
  });
  setIsLoading(false);
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!realmInfo) return;
const levelUpRealm = async () => {
setIsLoading(true);
if (!realmInfo) {
setIsLoading(false);
return;
}
await dojo.setup.systemCalls.upgrade_realm({
signer: dojo.account.account,
realm_entity_id: realmInfo.entityId,
});
setIsLoading(false);
};

Comment on lines +70 to +73
leaderboardManager.getContributionsTotalPercentage(
hyperstructureEntityId,
resourceContributions[playerAddress],
) * 100,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Add null check to prevent NaN in percentage calculation

The current implementation could return NaN if totalHyperstructureContribution is zero. The suggested fix using || 0 is appropriate as it will handle this edge case and any other potential undefined/NaN scenarios, while also improving code readability.

🔗 Analysis chain

Handle potential undefined values in percentage calculation.

Ensure that getContributionsTotalPercentage returns a valid number to prevent potential issues when multiplying by 100.

Apply this diff to add a default value:

- percentage:
-   leaderboardManager.getContributionsTotalPercentage(
-     hyperstructureEntityId,
-     resourceContributions[playerAddress],
-   ) * 100,
+ const totalPercentage = leaderboardManager.getContributionsTotalPercentage(
+   hyperstructureEntityId,
+   resourceContributions[playerAddress],
+ ) || 0;
+ percentage: totalPercentage * 100,
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find implementation of getContributionsTotalPercentage
ast-grep --pattern 'getContributionsTotalPercentage($$$) {
  $$$
}'

# Search for similar percentage calculations
rg "getContributionsTotalPercentage.*\*\s*100" -A 2

# Look for the leaderboardManager class definition
ast-grep --pattern 'class $_ implements LeaderboardManager {
  $$$
}'

Length of output: 195


Script:

#!/bin/bash
# Find any mentions of getContributionsTotalPercentage
rg "getContributionsTotalPercentage"

# Find LeaderboardManager interface or class
rg "LeaderboardManager"

# Find files that might contain the implementation
fd -e ts -e tsx "leaderboard"

Length of output: 4613


Script:

#!/bin/bash
# Get the implementation of getContributionsTotalPercentage from LeaderboardManager
cat client/sdk/packages/eternum/src/modelManager/LeaderboardManager.ts | grep -A 10 "getContributionsTotalPercentage"

# Get the implementation from LeaderboardUtils
cat client/sdk/packages/eternum/src/modelManager/utils/LeaderboardUtils.ts | grep -A 15 "getContributionsTotalPercentage"

Length of output: 1522

Comment on lines +47 to 51
setSelectedResourceBalance(
divideByPrecision(getBalance(entityId, Number(resourceId), dojo.setup.components).balance),
);
}, [resourceId, getBalance, entityId]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Include dojo.setup.components in useEffect dependencies.

Add dojo.setup.components to the dependency array to ensure selectedResourceBalance updates correctly when components change.

Apply this diff to fix the dependencies:

}, [resourceId, getBalance, entityId]);
+}, [resourceId, getBalance, entityId, dojo.setup.components]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setSelectedResourceBalance(
divideByPrecision(getBalance(entityId, Number(resourceId), dojo.setup.components).balance),
);
}, [resourceId, getBalance, entityId]);
setSelectedResourceBalance(
divideByPrecision(getBalance(entityId, Number(resourceId), dojo.setup.components).balance),
);
}, [resourceId, getBalance, entityId, dojo.setup.components]);

Comment on lines +51 to +52

return fragmentMines;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add safety checks and simplify the transformation logic.

The implementation has several potential safety issues:

  1. Unsafe optional chaining with fragmentMine!
  2. Potential undefined values in nested property access
  3. Complex transformation logic that could be simplified

Consider applying these improvements:

 export const useFragmentMines = () => {
   // ... existing setup code ...
   
   const fragmentMines = useEntityQuery([Has(Structure), HasValue(Structure, { category: "FragmentMine" })]).map(
     (fragmentMineEntityId) => {
       const fragmentMine = getComponentValue(Structure, fragmentMineEntityId);
+      if (!fragmentMine?.entity_id) return null;
       
       const position = getComponentValue(Position, fragmentMineEntityId);
       const entityName = getComponentValue(EntityName, fragmentMineEntityId);
 
+      const entityIdKeys = [BigInt(fragmentMine.entity_id)];
       const owner = toHexString(
-        getComponentValue(Owner, getEntityIdFromKeys([BigInt(fragmentMine!.entity_id)]))?.address || 0n,
+        getComponentValue(Owner, getEntityIdFromKeys(entityIdKeys))?.address ?? 0n,
       );
 
+      const buildingQuery = runQuery([HasValue(Building, { outer_entity_id: fragmentMine.entity_id })]);
+      const buildingId = buildingQuery.values().next().value ?? ("0" as Entity);
       const building = getComponentValue(
         Building,
-        runQuery([HasValue(Building, { outer_entity_id: fragmentMine!.entity_id })])
-          .values()
-          .next().value ?? ("0" as Entity),
+        buildingId,
       );
 
+      const productionQuery = runQuery([HasValue(Production, { entity_id: fragmentMine.entity_id })]);
+      const productionId = productionQuery.values().next().value ?? ("0" as Entity);
       const production = getComponentValue(
         Production,
-        runQuery([HasValue(Production, { entity_id: fragmentMine!.entity_id })])
-          .values()
-          .next().value ?? ("0" as Entity),
+        productionId,
       );
 
       return {
         ...fragmentMine,
         ...position,
         ...building,
         ...production,
         owner,
         name: entityName
           ? shortString.decodeShortString(entityName.name.toString())
-          : `FragmentMine ${fragmentMine?.entity_id}`,
+          : `FragmentMine ${fragmentMine.entity_id}`,
       };
-    },
+    }).filter(Boolean),
   );
 
   return fragmentMines;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return fragmentMines;
export const useFragmentMines = () => {
// ... existing setup code ...
const fragmentMines = useEntityQuery([Has(Structure), HasValue(Structure, { category: "FragmentMine" })]).map(
(fragmentMineEntityId) => {
const fragmentMine = getComponentValue(Structure, fragmentMineEntityId);
if (!fragmentMine?.entity_id) return null;
const position = getComponentValue(Position, fragmentMineEntityId);
const entityName = getComponentValue(EntityName, fragmentMineEntityId);
const entityIdKeys = [BigInt(fragmentMine.entity_id)];
const owner = toHexString(
getComponentValue(Owner, getEntityIdFromKeys(entityIdKeys))?.address ?? 0n,
);
const buildingQuery = runQuery([HasValue(Building, { outer_entity_id: fragmentMine.entity_id })]);
const buildingId = buildingQuery.values().next().value ?? ("0" as Entity);
const building = getComponentValue(
Building,
buildingId,
);
const productionQuery = runQuery([HasValue(Production, { entity_id: fragmentMine.entity_id })]);
const productionId = productionQuery.values().next().value ?? ("0" as Entity);
const production = getComponentValue(
Production,
productionId,
);
return {
...fragmentMine,
...position,
...building,
...production,
owner,
name: entityName
? shortString.decodeShortString(entityName.name.toString())
: `FragmentMine ${fragmentMine.entity_id}`,
};
}).filter(Boolean);
return fragmentMines;
};

Comment on lines +14 to +15
// todo: pay attention to expensive query
const realms = useRealms();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Consider implementing pagination or virtual scrolling for realm queries

The TODO comment flags a legitimate performance concern. The current implementation:

  • Fetches all realms at once without pagination
  • Performs multiple component lookups per realm
  • Processes each realm with expensive operations

Suggested improvements:

  • Implement pagination or virtual scrolling to limit the number of realms loaded at once
  • Consider implementing a more efficient batch lookup for realm components
  • Cache processed realm data to avoid repeated transformations
🔗 Analysis chain

Address the TODO comment about query performance.

The comment indicates a potential performance issue with the query. This should be investigated and documented properly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other instances of useRealms to understand the impact
rg "useRealms\(" --type ts

Length of output: 122


Script:

#!/bin/bash
# Search for useRealms hook definition
ast-grep --pattern 'export function useRealms() {
  $$$
}'

# Also search for any other exports of useRealms
rg "export.*useRealms" --type ts

# Look for the underlying query implementation
rg "getRealms|fetchRealms|queryRealms" --type ts

Length of output: 335


Script:

#!/bin/bash
# Get the hook implementation
cat client/apps/game/src/hooks/helpers/use-realm.tsx

# Get the getRealms implementation
cat client/apps/game/src/utils/realm.ts

Length of output: 11630

Comment on lines +39 to 46
() =>
dynamic.map(
(resourceId): Resource => ({
resourceId,
amount: getBalance(entityId, resourceId, dojo.setup.components).balance,
}),
),
[dynamic, entityId, getBalance],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove getBalance from the dependency array.

The getBalance function is imported and its reference won't change during the component's lifecycle. Including it in the dependency array could cause unnecessary re-renders.

-    [dynamic, entityId, getBalance],
+    [dynamic, entityId],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
() =>
dynamic.map(
(resourceId): Resource => ({
resourceId,
amount: getBalance(entityId, resourceId, dojo.setup.components).balance,
}),
),
[dynamic, entityId, getBalance],
() =>
dynamic.map(
(resourceId): Resource => ({
resourceId,
amount: getBalance(entityId, resourceId, dojo.setup.components).balance,
}),
),
[dynamic, entityId],

@@ -29,7 +29,7 @@ export const EntitiesArmyTable = () => {
</div>
<BattleSimulation />
<PillageSimulation />
{playerStructures().map((entity: any, index: number) => {
{playerStructures.map((entity: any, index: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove any type and use proper typing.

Using any type defeats TypeScript's type checking. Consider using the correct type from @bibliothecadao/eternum.

-      {playerStructures.map((entity: any, index: number) => {
+      {playerStructures.map((entity: PlayerStructure, index: number) => {

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +8 to +9
import { getEntityInfo } from "@/utils/entities";
import { ArmyInfo, BattleManager, ContractAddress } from "@bibliothecadao/eternum";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

LGTM! Consistent refactoring with improved error handling.

The changes maintain functionality while adding proper null handling for battle conditions.

However, there's a potential issue with the dependencies array:

The getEntityInfo function is listed in the dependencies array of useCallback but it's imported from a module. Remove it from the dependencies as it won't change between renders:

  useCallback(
    (army: ArmyInfo, index: number) => {
      // ... function body ...
    },
    [
      nextBlockTimestamp,
      ownArmySelected,
      ownArmySelected?.entity_id,
      position,
-     getEntityInfo,
      useIsStructureImmune,
      setBattleView,
      setTooltip,
      structureAtPosition,
      ownArmyIsImmune,
      dojo,
    ],
  );

Also applies to: 30-34, 48-52

Comment on lines 52 to 55
useEffect(() => {
setPlayers(getPlayers());
setPlayerInfo(getPlayerInfo(players, ContractAddress(account.address), components));
setIsLoading(false);
}, [playersByRank]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incomplete useEffect dependencies.

The useEffect hook is missing dependencies that it uses within its callback. Add players, account, and components to the dependency array to prevent stale closures.

  useEffect(() => {
    setPlayerInfo(getPlayerInfo(players, ContractAddress(account.address), components));
    setIsLoading(false);
-  }, [playersByRank]);
+  }, [playersByRank, players, account.address, components]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
setPlayers(getPlayers());
setPlayerInfo(getPlayerInfo(players, ContractAddress(account.address), components));
setIsLoading(false);
}, [playersByRank]);
useEffect(() => {
setPlayerInfo(getPlayerInfo(players, ContractAddress(account.address), components));
setIsLoading(false);
}, [playersByRank, players, account.address, components]);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
client/apps/game/src/utils/entities.ts (5)

16-31: Consider adding input validation and error handling.

The function could benefit from additional error handling:

  • Validate entityId before conversion to BigInt
  • Add null checks for position and owner values
 export const getRealm = (entityId: ID, components: ClientComponents) => {
+  if (!entityId || entityId <= 0) return undefined;
   const { Realm, Owner, Position } = components;
   const entity = getEntityIdFromKeys([BigInt(entityId)]);
   const realm = getComponentValue(Realm, entity);
   if (!realm) return undefined;

   const position = getComponentValue(Position, entity);
   const owner = getComponentValue(Owner, entity);
+  if (!position || !owner) return undefined;

   return {
     ...realm,
     position,
     name: getRealmNameById(realm.realm_id),
     owner,
   } as RealmWithPosition;
 };

52-55: Consider breaking down the complex component destructuring.

The large number of destructured components makes the function harder to maintain. Consider grouping related components into separate objects.

-  const { ArrivalTime, Movable, CapacityCategory, CapacityConfig, EntityOwner, Owner, Structure, Army, Position } =
-    components;
+  const movementComponents = { ArrivalTime, Movable, Position } = components;
+  const capacityComponents = { CapacityCategory, CapacityConfig } = components;
+  const ownershipComponents = { EntityOwner, Owner } = components;
+  const entityComponents = { Structure, Army } = components;

118-123: Move abbreviations to constants.

The abbreviations object should be defined as a constant outside the function to improve maintainability and reusability.

+const STRUCTURE_ABBREVIATIONS: Record<string, string> = {
+  FragmentMine: "FM",
+  Hyperstructure: "HS",
+  Bank: "BK",
+};

 export const getEntityName = (entityId: ID, components: ClientComponents, abbreviate: boolean = false) => {
   // ...
   if (abbreviate && structure) {
-    const abbreviations: Record<string, string> = {
-      FragmentMine: "FM",
-      Hyperstructure: "HS",
-      Bank: "BK",
-    };
-    const abbr = abbreviations[structure.category];
+    const abbr = STRUCTURE_ABBREVIATIONS[structure.category];

137-157: Add JSDoc documentation and consistent error handling.

These utility functions would benefit from clear documentation of their purpose and return types, along with consistent error handling.

+/**
+ * Retrieves the name associated with a contract address
+ * @param address - The contract address to look up
+ * @param components - Client components containing the AddressName component
+ * @returns The name string if found, undefined otherwise
+ */
 export const getAddressName = (address: ContractAddress, components: ClientComponents) => {
+  if (!address) return undefined;
   const addressName = getComponentValue(components.AddressName, getEntityIdFromKeys([BigInt(address)]));
   return addressName ? addressName.name.toString() : undefined;
 };

+/**
+ * Retrieves the name associated with an entity's owner address
+ * @param entityId - The entity ID to look up
+ * @param components - Client components containing required components
+ * @returns The owner's name string if found, undefined otherwise
+ */
 export const getAddressNameFromEntity = (entityId: ID, components: ClientComponents) => {
+  if (!entityId) return undefined;
   const address = getPlayerAddressFromEntity(entityId, components);
   if (!address) return;
   const addressName = getComponentValue(components.AddressName, getEntityIdFromKeys([BigInt(address)]));
   return addressName ? addressName.name.toString() : undefined;
 };

1-157: Consider organizing utilities into logical groups.

The file contains well-structured utilities, but could benefit from organizing related functions into separate modules:

  • Entity retrieval (getRealm, getStructure)
  • Entity information (getEntityInfo, getEntityName)
  • Address management (getAddressName, getAddressNameFromEntity, getPlayerAddressFromEntity)

This would improve maintainability and make the code more modular.

client/apps/game/src/utils/realm.ts (2)

8-19: Add error handling and improve type safety.

The function could benefit from the following improvements:

  1. Add explicit return type annotation
  2. Handle potential shortString decoding errors
  3. Consider returning null/undefined instead of empty string for better error handling

Consider this implementation:

-export const getRealmAddressName = (realmEntityId: ID, components: ClientComponents) => {
+export const getRealmAddressName = (realmEntityId: ID, components: ClientComponents): string | null => {
   const owner = getComponentValue(components.Owner, getEntityIdFromKeys([BigInt(realmEntityId)]));
   const addressName = owner
     ? getComponentValue(components.AddressName, getEntityIdFromKeys([owner.address]))
     : undefined;

   if (addressName) {
+    try {
       return shortString.decodeShortString(String(addressName.name));
+    } catch (error) {
+      console.error(`Failed to decode address name: ${error}`);
+      return null;
+    }
   } else {
-    return "";
+    return null;
   }
 };

43-72: Improve type safety and readability of getRealmInfo.

The function could benefit from better type safety and readability improvements:

  1. Type-safe population spreading
  2. Document magic numbers
  3. Break down complex calculations
 export function getRealmInfo(entity: Entity, components: ClientComponents): RealmInfo | undefined {
   const realm = getComponentValue(components.Realm, entity);
   const owner = getComponentValue(components.Owner, entity);
   const position = getComponentValue(components.Position, entity);
   const population = getComponentValue(components.Population, entity);

   if (realm && owner && position) {
     const { realm_id, entity_id, produced_resources, order, level } = realm;
     const name = getRealmNameById(realm_id);
     const { address } = owner;

+    // Extract population-related calculations
+    const BASE_POPULATION_CAPACITY = configManager.getBasePopulationCapacity();
+    const currentPopulation = population?.population ?? 0;
+    const populationCapacity = population?.capacity ?? 0;
+    const totalCapacity = populationCapacity + BASE_POPULATION_CAPACITY;
+    const hasAvailableCapacity = !population || totalCapacity > currentPopulation;

     return {
       realmId: realm_id,
       entityId: entity_id,
       name,
       level,
       resourceTypesPacked: produced_resources,
       order,
       position,
-      ...population,
+      population: currentPopulation,
+      capacity: populationCapacity,
-      hasCapacity:
-        !population || population.capacity + configManager.getBasePopulationCapacity() > population.population,
+      hasCapacity: hasAvailableCapacity,
       owner: address,
       ownerName: getRealmAddressName(realm.entity_id, components),
       hasWonder: realm.has_wonder,
     };
   }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4842ce and 38ba118.

📒 Files selected for processing (2)
  • client/apps/game/src/utils/entities.ts (1 hunks)
  • client/apps/game/src/utils/realm.ts (1 hunks)
🔇 Additional comments (2)
client/apps/game/src/utils/entities.ts (1)

132-135: LGTM!

The function is well-structured and handles the wonder case appropriately.

client/apps/game/src/utils/realm.ts (1)

1-7: LGTM! Well-structured imports.

The imports are well-organized and properly typed, following TypeScript best practices.

const position = getComponentValue(Position, entity);
const owner = getComponentValue(Owner, entity);
const structureName = getEntityName(structure.entity_id, components);
const name = structureName ? `${structure?.category} ${structureName}` : structure.category || "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle potential undefined values in name construction.

The current implementation might result in "undefined undefined" if both structureName and structure.category are undefined.

-  const name = structureName ? `${structure?.category} ${structureName}` : structure.category || "";
+  const category = structure?.category || "";
+  const name = structureName ? `${category} ${structureName}` : category;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const name = structureName ? `${structure?.category} ${structureName}` : structure.category || "";
const category = structure?.category || "";
const name = structureName ? `${category} ${structureName}` : category;

Comment on lines +55 to +63
const entityIdBigInt = BigInt(entityId);
const arrivalTime = getComponentValue(ArrivalTime, getEntityIdFromKeys([entityIdBigInt]));
const movable = getComponentValue(Movable, getEntityIdFromKeys([entityIdBigInt]));

const entityCapacityCategory = getComponentValue(CapacityCategory, getEntityIdFromKeys([entityIdBigInt]))
?.category as unknown as string;
const capacityCategoryId = CAPACITY_CONFIG_CATEGORY_STRING_MAP[entityCapacityCategory] || 0n;
const capacity = getComponentValue(CapacityConfig, getEntityIdFromKeys([BigInt(capacityCategoryId)]));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize repeated BigInt conversions and improve type safety.

The entityId is converted to BigInt multiple times. Consider converting once and reusing the value.

+  if (!entityId) return undefined;
   const entityIdBigInt = BigInt(entityId);
+  const entityKeys = getEntityIdFromKeys([entityIdBigInt]);
-  const arrivalTime = getComponentValue(ArrivalTime, getEntityIdFromKeys([entityIdBigInt]));
-  const movable = getComponentValue(Movable, getEntityIdFromKeys([entityIdBigInt]));
+  const arrivalTime = getComponentValue(ArrivalTime, entityKeys);
+  const movable = getComponentValue(Movable, entityKeys);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const entityIdBigInt = BigInt(entityId);
const arrivalTime = getComponentValue(ArrivalTime, getEntityIdFromKeys([entityIdBigInt]));
const movable = getComponentValue(Movable, getEntityIdFromKeys([entityIdBigInt]));
const entityCapacityCategory = getComponentValue(CapacityCategory, getEntityIdFromKeys([entityIdBigInt]))
?.category as unknown as string;
const capacityCategoryId = CAPACITY_CONFIG_CATEGORY_STRING_MAP[entityCapacityCategory] || 0n;
const capacity = getComponentValue(CapacityConfig, getEntityIdFromKeys([BigInt(capacityCategoryId)]));
if (!entityId) return undefined;
const entityIdBigInt = BigInt(entityId);
const entityKeys = getEntityIdFromKeys([entityIdBigInt]);
const arrivalTime = getComponentValue(ArrivalTime, entityKeys);
const movable = getComponentValue(Movable, entityKeys);
const entityCapacityCategory = getComponentValue(CapacityCategory, getEntityIdFromKeys([entityIdBigInt]))
?.category as unknown as string;
const capacityCategoryId = CAPACITY_CONFIG_CATEGORY_STRING_MAP[entityCapacityCategory] || 0n;
const capacity = getComponentValue(CapacityConfig, getEntityIdFromKeys([BigInt(capacityCategoryId)]));

Comment on lines +37 to +41
const getRealmNameById = (realmId: ID): string => {
const features = realmsJson["features"][realmId - 1];
if (!features) return "";
return features["name"];
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking and improve type safety.

The current implementation could lead to runtime errors due to unsafe array access and lack of bounds checking.

-const getRealmNameById = (realmId: ID): string => {
+const getRealmNameById = (realmId: ID): string => {
+  if (realmId <= 0) {
+    console.warn(`Invalid realmId: ${realmId}`);
+    return "";
+  }
+
+  const index = Number(realmId) - 1;
+  if (index >= realmsJson.features.length) {
+    console.warn(`RealmId ${realmId} exceeds realm count`);
+    return "";
+  }
+
-  const features = realmsJson["features"][realmId - 1];
+  const features = realmsJson.features[index];
   if (!features) return "";
-  return features["name"];
+  return features.name || "";
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getRealmNameById = (realmId: ID): string => {
const features = realmsJson["features"][realmId - 1];
if (!features) return "";
return features["name"];
};
const getRealmNameById = (realmId: ID): string => {
if (realmId <= 0) {
console.warn(`Invalid realmId: ${realmId}`);
return "";
}
const index = Number(realmId) - 1;
if (index >= realmsJson.features.length) {
console.warn(`RealmId ${realmId} exceeds realm count`);
return "";
}
const features = realmsJson.features[index];
if (!features) return "";
return features.name || "";
};

Comment on lines +21 to +28
interface Attribute {
trait_type: string;
value: any;
}

let realms: {
[key: string]: any;
} = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace 'any' types with more specific types.

Using any reduces TypeScript's type safety benefits. Consider defining more specific types:

 interface Attribute {
   trait_type: string;
-  value: any;
+  value: string | number | boolean; // or more specific union type based on possible values
 }

 let realms: {
-  [key: string]: any;
+  [key: string]: {
+    features?: {
+      name: string;
+      // Add other known properties
+    }[];
+    // Add other known properties
+  };
 } = {};

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +30 to +35
const loadRealms = async () => {
const response = await fetch("/jsons/realms.json");
realms = await response.json();
};

loadRealms();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and configuration in loadRealms.

The current implementation has several potential issues:

  1. No error handling for failed fetch requests
  2. Hardcoded path should be configurable
  3. Immediate invocation could cause issues in testing environments

Consider this improved implementation:

+const REALMS_JSON_PATH = process.env.REALMS_JSON_PATH || '/jsons/realms.json';
+const MAX_RETRIES = 3;

-const loadRealms = async () => {
+export const loadRealms = async (retries = MAX_RETRIES): Promise<void> => {
   try {
-    const response = await fetch("/jsons/realms.json");
+    const response = await fetch(REALMS_JSON_PATH);
+    if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`);
+    }
     realms = await response.json();
+  } catch (error) {
+    console.error(`Failed to load realms: ${error}`);
+    if (retries > 0) {
+      console.log(`Retrying... (${retries} attempts remaining)`);
+      await new Promise(resolve => setTimeout(resolve, 1000));
+      return loadRealms(retries - 1);
+    }
+    throw error;
   }
 };

-loadRealms();
+// Consider moving this to your app's initialization logic
+// if (process.env.NODE_ENV !== 'test') {
+//   loadRealms().catch(console.error);
+// }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const loadRealms = async () => {
const response = await fetch("/jsons/realms.json");
realms = await response.json();
};
loadRealms();
const REALMS_JSON_PATH = process.env.REALMS_JSON_PATH || '/jsons/realms.json';
const MAX_RETRIES = 3;
export const loadRealms = async (retries = MAX_RETRIES): Promise<void> => {
try {
const response = await fetch(REALMS_JSON_PATH);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
realms = await response.json();
} catch (error) {
console.error(`Failed to load realms: ${error}`);
if (retries > 0) {
console.log(`Retrying... (${retries} attempts remaining)`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadRealms(retries - 1);
}
throw error;
}
};
// Consider moving this to your app's initialization logic
// if (process.env.NODE_ENV !== 'test') {
// loadRealms().catch(console.error);
// }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
client/apps/game/src/utils/players.ts (1)

18-34: Consider adding error handling and optimizing queries.

A few suggestions for improvement:

  1. The runQuery for isAlive check could be cached if used frequently
  2. Add error handling for getComponentValue and guild name resolution
  3. Consider logging or handling edge cases when guild data is missing
 const playerInfo = players
   .map((player) => {
+    try {
       const isAlive = !!runQuery([HasValue(Owner, { address: player.address })]).size;
 
       const guildMember = getComponentValue(GuildMember, player.entity);
-      const guildName = guildMember ? getEntityName(guildMember.guild_entity_id, components) : "";
+      const guildName = guildMember ? getEntityName(guildMember.guild_entity_id, components) : null;
 
       return {
         entity: player.entity,
         address: player.address,
         name: player.name,
         isAlive,
-        guildName,
+        guildName: guildName ?? "",
       };
+    } catch (error) {
+      console.error(`Failed to process player ${player.address}:`, error);
+      return undefined;
+    }
   })
   .filter((player) => player !== undefined);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38ba118 and 2f8524c.

📒 Files selected for processing (1)
  • client/apps/game/src/utils/players.ts (1 hunks)
🔇 Additional comments (2)
client/apps/game/src/utils/players.ts (2)

1-6: LGTM! Well-organized imports.

The imports are logically organized and include all necessary dependencies for the function's operation.


7-12: LGTM! Well-structured function signature.

The function signature is well-typed and the component destructuring improves readability.

Comment on lines 14 to 16
const playersByRank = useLeaderBoardStore((state) => state.playersByRank);

const totalPoints = playersByRank.reduce((sum, [, points]) => sum + points, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Hook usage in utility function.

The useLeaderBoardStore hook is being called inside a utility function. This violates React's rules of hooks, which state that hooks must be called from either React function components or custom hooks. This could lead to runtime errors.

Consider refactoring to pass the leaderboard data as a parameter:

 export const getPlayerInfo = (
   players: Player[],
   playerAddress: ContractAddress,
   components: ClientComponents,
+  playersByRank: [string, number][],
 ): PlayerInfo[] => {
   const { Realm, Owner, GuildMember, Hyperstructure, Structure } = components;
-  const playersByRank = useLeaderBoardStore((state) => state.playersByRank);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +35 to +62
let unrankedCount = 0;

return playerInfo.map((player) => {
const rankIndex = playersByRank.findIndex(([address]) => address === player.address);
if (rankIndex === -1) unrankedCount++;

const points = rankIndex === -1 ? 0 : playersByRank[rankIndex][1];

return {
entity: player.entity,
name: player.name,
address: player.address,
points,
rank: rankIndex === -1 ? Number.MAX_SAFE_INTEGER : rankIndex + 1,
percentage: calculatePlayerSharePercentage(points, totalPoints),
lords: 0,
realms: runQuery([Has(Realm), HasValue(Owner, { address: player.address })]).size,
mines: runQuery([
HasValue(Structure, { category: StructureType[StructureType.FragmentMine] }),
HasValue(Owner, { address: player.address }),
]).size,
hyperstructures: runQuery([Has(Hyperstructure), HasValue(Owner, { address: player.address })]).size,
isAlive: player.isAlive,
guildName: player.guildName || "",
isUser: player.address === playerAddress,
};
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize queries and add validation.

The current implementation has several areas for improvement:

  1. Multiple separate runQuery calls could be combined or cached
  2. Consider validating points and percentages
  3. The rank calculation could be more explicit
+ const playerQueries = runQuery([
+   Has(Realm, Hyperstructure, Structure),
+   HasValue(Owner, { address: player.address }),
+ ]).reduce((acc, entity) => {
+   if (Has(Realm)(entity)) acc.realms++;
+   if (Has(Hyperstructure)(entity)) acc.hyperstructures++;
+   if (HasValue(Structure, { category: StructureType[StructureType.FragmentMine] })(entity)) acc.mines++;
+   return acc;
+ }, { realms: 0, mines: 0, hyperstructures: 0 });

  return {
    // ... other fields ...
-   points,
+   points: Math.max(0, points),
-   percentage: calculatePlayerSharePercentage(points, totalPoints),
+   percentage: Math.min(100, Math.max(0, calculatePlayerSharePercentage(points, totalPoints))),
-   realms: runQuery([Has(Realm), HasValue(Owner, { address: player.address })]).size,
-   mines: runQuery([
-     HasValue(Structure, { category: StructureType[StructureType.FragmentMine] }),
-     HasValue(Owner, { address: player.address }),
-   ]).size,
-   hyperstructures: runQuery([Has(Hyperstructure), HasValue(Owner, { address: player.address })]).size,
+   ...playerQueries,

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Collaborator

@bob0005 bob0005 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm, some things seem to have been broken, but let's fix those after.

  • Realm/adress names
  • Player structures (None shown, can't select between them)
  • Shares % in "world structures" / hyperstructures panel
  • Client crash on market/transfer tab

@bob0005 bob0005 merged commit 1f36d01 into next Jan 15, 2025
7 of 9 checks passed
@bob0005 bob0005 deleted the raschel/clean branch January 15, 2025 17:06
This was referenced Jan 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants