From 034509a9970c52fd0c528f833524f202c981fc2b Mon Sep 17 00:00:00 2001
From: ECWireless
Date: Mon, 5 Aug 2024 08:04:34 -0600
Subject: [PATCH 1/2] Get item stats through decoding bytes
---
.../client/src/contexts/CharacterContext.tsx | 61 +++++++--------
packages/client/src/pages/Character.tsx | 55 +++++--------
.../client/src/pages/CharacterCreation.tsx | 36 ++++-----
packages/client/src/utils/helpers.ts | 77 ++++++++++++++++++-
4 files changed, 139 insertions(+), 90 deletions(-)
diff --git a/packages/client/src/contexts/CharacterContext.tsx b/packages/client/src/contexts/CharacterContext.tsx
index 854220102..cc558c980 100644
--- a/packages/client/src/contexts/CharacterContext.tsx
+++ b/packages/client/src/contexts/CharacterContext.tsx
@@ -16,13 +16,17 @@ import {
import { formatEther, hexToString, zeroHash } from 'viem';
import { useToast } from '../hooks/useToast';
-import { fetchMetadataFromUri, uriToHttp } from '../utils/helpers';
+import {
+ decodeArmorStats,
+ decodeWeaponStats,
+ fetchMetadataFromUri,
+ uriToHttp,
+} from '../utils/helpers';
import type {
Armor,
Character,
CharacterData,
EntityStats,
- StatsClasses,
Weapon,
} from '../utils/types';
import { useMUD } from './MUDContext';
@@ -55,6 +59,7 @@ export const CharacterProvider = ({
CharacterEquipment,
Characters,
CharactersTokenURI,
+ Items,
ItemsBaseURI,
ItemsOwners,
ItemsTokenURI,
@@ -238,16 +243,14 @@ export const CharacterProvider = ({
const fullArmor = await Promise.all(
_armor.map(async item => {
- const itemTemplateStats =
- await worldContract.read.UD__getArmorStats([
- BigInt(item.tokenId),
- ]);
-
const tokenIdEntity = encodeEntity(
{ tokenId: 'uint256' },
{ tokenId: BigInt(item.tokenId) },
);
+ const itemTemplate = getComponentValueStrict(Items, tokenIdEntity);
+ const decodedArmorStats = decodeArmorStats(itemTemplate.stats);
+
const baseURI = getComponentValueStrict(
ItemsBaseURI,
singletonEntity,
@@ -264,18 +267,16 @@ export const CharacterProvider = ({
return {
...metadata,
- agiModifier: itemTemplateStats.agiModifier.toString(),
- armorModifier: itemTemplateStats.armorModifier.toString(),
+ agiModifier: decodedArmorStats.agiModifier,
+ armorModifier: decodedArmorStats.armorModifier,
balance: item.balance,
- classRestrictions: itemTemplateStats.classRestrictions.map(
- (classRestriction: number) => classRestriction as StatsClasses,
- ),
- hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
- intModifier: itemTemplateStats.intModifier.toString(),
+ classRestrictions: decodedArmorStats.classRestrictions,
+ hitPointModifier: decodedArmorStats.hitPointModifier,
+ intModifier: decodedArmorStats.intModifier,
itemId: item.itemId,
- minLevel: itemTemplateStats.minLevel.toString(),
+ minLevel: decodedArmorStats.minLevel,
owner: item.owner,
- strModifier: itemTemplateStats.strModifier.toString(),
+ strModifier: decodedArmorStats.strModifier,
tokenId: item.tokenId,
} as Armor;
}),
@@ -283,16 +284,14 @@ export const CharacterProvider = ({
const fullWeapons = await Promise.all(
_weapons.map(async item => {
- const itemTemplateStats =
- await worldContract.read.UD__getWeaponStats([
- BigInt(item.tokenId),
- ]);
-
const tokenIdEntity = encodeEntity(
{ tokenId: 'uint256' },
{ tokenId: BigInt(item.tokenId) },
);
+ const itemTemplate = getComponentValueStrict(Items, tokenIdEntity);
+ const decodedWeaponStats = decodeWeaponStats(itemTemplate.stats);
+
const baseURI = getComponentValueStrict(
ItemsBaseURI,
singletonEntity,
@@ -309,19 +308,17 @@ export const CharacterProvider = ({
return {
...metadata,
- agiModifier: itemTemplateStats.agiModifier.toString(),
+ agiModifier: decodedWeaponStats.agiModifier,
balance: item.balance,
- classRestrictions: itemTemplateStats.classRestrictions.map(
- (classRestriction: number) => classRestriction as StatsClasses,
- ),
- hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
- intModifier: itemTemplateStats.intModifier.toString(),
+ classRestrictions: decodedWeaponStats.classRestrictions,
+ hitPointModifier: decodedWeaponStats.hitPointModifier,
+ intModifier: decodedWeaponStats.intModifier,
itemId: item.itemId,
- maxDamage: itemTemplateStats.maxDamage.toString(),
- minDamage: itemTemplateStats.minDamage.toString(),
- minLevel: itemTemplateStats.minLevel.toString(),
+ maxDamage: decodedWeaponStats.maxDamage,
+ minDamage: decodedWeaponStats.minDamage,
+ minLevel: decodedWeaponStats.minLevel,
owner: item.owner,
- strModifier: itemTemplateStats.strModifier.toString(),
+ strModifier: decodedWeaponStats.strModifier,
tokenId: item.tokenId,
} as Weapon;
}),
@@ -336,7 +333,7 @@ export const CharacterProvider = ({
);
}
},
- [ItemsBaseURI, ItemsOwners, ItemsTokenURI, renderError, worldContract],
+ [Items, ItemsBaseURI, ItemsOwners, ItemsTokenURI, renderError],
);
useEffect(() => {
diff --git a/packages/client/src/pages/Character.tsx b/packages/client/src/pages/Character.tsx
index f3527d746..49efa5942 100644
--- a/packages/client/src/pages/Character.tsx
+++ b/packages/client/src/pages/Character.tsx
@@ -46,7 +46,9 @@ import { useToast } from '../hooks/useToast';
import { HOME_PATH, LEADERBOARD_PATH } from '../Routes';
import { MAX_EQUIPPED_ARMOR, MAX_EQUIPPED_WEAPONS } from '../utils/constants';
import {
+ decodeArmorStats,
decodeCharacterId,
+ decodeWeaponStats,
fetchMetadataFromUri,
uriToHttp,
} from '../utils/helpers';
@@ -445,7 +447,6 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
ItemsOwners,
ItemsTokenURI,
},
- network: { worldContract },
} = useMUD();
const {
@@ -499,6 +500,7 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
itemId: entity,
itemType: itemTemplate.itemType,
owner,
+ stats: itemTemplate.stats,
tokenId: tokenId.toString(),
tokenIdEntity,
};
@@ -515,10 +517,7 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
const fullArmor = await Promise.all(
_armor.map(async item => {
- const itemTemplateStats =
- await worldContract.read.UD__getArmorStats([
- BigInt(item.tokenId),
- ]);
+ const decodedArmorStats = decodeArmorStats(item.stats);
const baseURI = getComponentValueStrict(
ItemsBaseURI,
@@ -536,16 +535,14 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
return {
...metadata,
- agiModifier: itemTemplateStats.agiModifier.toString(),
- armorModifier: itemTemplateStats.armorModifier.toString(),
- classRestrictions: itemTemplateStats.classRestrictions.map(
- (classRestriction: number) => classRestriction as StatsClasses,
- ),
- hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
- intModifier: itemTemplateStats.intModifier.toString(),
+ agiModifier: decodedArmorStats.agiModifier,
+ armorModifier: decodedArmorStats.armorModifier,
+ classRestrictions: decodedArmorStats.classRestrictions,
+ hitPointModifier: decodedArmorStats.hitPointModifier,
+ intModifier: decodedArmorStats.intModifier,
itemId: item.itemId,
owner: item.owner,
- strModifier: itemTemplateStats.strModifier.toString(),
+ strModifier: decodedArmorStats.strModifier,
tokenId: item.tokenId,
} as Armor;
}),
@@ -553,10 +550,7 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
const fullWeapons = await Promise.all(
_weapons.map(async item => {
- const itemTemplateStats =
- await worldContract.read.UD__getWeaponStats([
- BigInt(item.tokenId),
- ]);
+ const decodedWeaponStats = decodeWeaponStats(item.stats);
const baseURI = getComponentValueStrict(
ItemsBaseURI,
@@ -574,19 +568,17 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
return {
...metadata,
- agiModifier: itemTemplateStats.agiModifier.toString(),
+ agiModifier: decodedWeaponStats.agiModifier,
balance: item.balance,
- classRestrictions: itemTemplateStats.classRestrictions.map(
- (classRestriction: number) => classRestriction as StatsClasses,
- ),
- hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
- intModifier: itemTemplateStats.intModifier.toString(),
+ classRestrictions: decodedWeaponStats.classRestrictions,
+ hitPointModifier: decodedWeaponStats.hitPointModifier,
+ intModifier: decodedWeaponStats.intModifier,
itemId: item.itemId,
- maxDamage: itemTemplateStats.maxDamage.toString(),
- minDamage: itemTemplateStats.minDamage.toString(),
- minLevel: itemTemplateStats.minLevel.toString(),
+ maxDamage: decodedWeaponStats.maxDamage,
+ minDamage: decodedWeaponStats.minDamage,
+ minLevel: decodedWeaponStats.minLevel,
owner: item.owner,
- strModifier: itemTemplateStats.strModifier.toString(),
+ strModifier: decodedWeaponStats.strModifier,
tokenId: item.tokenId,
} as Weapon;
}),
@@ -603,14 +595,7 @@ const ItemsPanel = ({ character }: { character: Character }): JSX.Element => {
setIsLoadingItems(false);
}
},
- [
- Items,
- ItemsBaseURI,
- ItemsOwners,
- ItemsTokenURI,
- renderError,
- worldContract,
- ],
+ [Items, ItemsBaseURI, ItemsOwners, ItemsTokenURI, renderError],
);
useEffect(() => {
diff --git a/packages/client/src/pages/CharacterCreation.tsx b/packages/client/src/pages/CharacterCreation.tsx
index 88e7f66e7..9c3cb2589 100644
--- a/packages/client/src/pages/CharacterCreation.tsx
+++ b/packages/client/src/pages/CharacterCreation.tsx
@@ -31,6 +31,7 @@ import { useUploadFile } from '../hooks/useUploadFile';
import { GAME_BOARD_PATH, HOME_PATH } from '../Routes';
import { API_URL } from '../utils/constants';
import {
+ decodeWeaponStats,
fetchMetadataFromUri,
shortenAddress,
uriToHttp,
@@ -44,6 +45,7 @@ export const CharacterCreation = (): JSX.Element => {
const { isConnected } = useAccount();
const {
components: {
+ Items,
ItemsBaseURI,
ItemsTokenURI,
StarterItems,
@@ -51,7 +53,6 @@ export const CharacterCreation = (): JSX.Element => {
},
delegatorAddress,
isSynced,
- network: { worldContract },
systemCalls: { enterGame, mintCharacter, rollStats },
} = useMUD();
const { character, isRefreshing, refreshCharacter } = useCharacter();
@@ -96,15 +97,14 @@ export const CharacterCreation = (): JSX.Element => {
try {
const _items: Weapon[] = await Promise.all(
starterWeaponTokenIds.map(async tokenId => {
- const itemTemplateStats = await worldContract.read.UD__getWeaponStats(
- [tokenId],
- );
-
const tokenIdEntity = encodeEntity(
{ tokenId: 'uint256' },
- { tokenId: tokenId },
+ { tokenId },
);
+ const itemTemplate = getComponentValueStrict(Items, tokenIdEntity);
+ const decodedWeaponStats = decodeWeaponStats(itemTemplate.stats);
+
const baseURI = getComponentValueStrict(
ItemsBaseURI,
singletonEntity,
@@ -120,14 +120,14 @@ export const CharacterCreation = (): JSX.Element => {
);
return {
- agiModifier: itemTemplateStats.agiModifier.toString(),
- classRestrictions: itemTemplateStats.classRestrictions,
- hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
- intModifier: itemTemplateStats.intModifier.toString(),
- maxDamage: itemTemplateStats.maxDamage.toString(),
- minDamage: itemTemplateStats.minDamage.toString(),
- minLevel: itemTemplateStats.minLevel.toString(),
- strModifier: itemTemplateStats.strModifier.toString(),
+ agiModifier: decodedWeaponStats.agiModifier,
+ classRestrictions: decodedWeaponStats.classRestrictions,
+ hitPointModifier: decodedWeaponStats.hitPointModifier,
+ intModifier: decodedWeaponStats.intModifier,
+ maxDamage: decodedWeaponStats.maxDamage,
+ minDamage: decodedWeaponStats.minDamage,
+ minLevel: decodedWeaponStats.minLevel,
+ strModifier: decodedWeaponStats.strModifier,
...fetachedMetadata,
} as Weapon;
}),
@@ -137,13 +137,7 @@ export const CharacterCreation = (): JSX.Element => {
} catch (e) {
renderError((e as Error)?.message ?? 'Error fetching starter item.', e);
}
- }, [
- ItemsBaseURI,
- ItemsTokenURI,
- renderError,
- starterWeaponTokenIds,
- worldContract,
- ]);
+ }, [Items, ItemsBaseURI, ItemsTokenURI, renderError, starterWeaponTokenIds]);
useEffect(() => {
fetchStarterWeapons();
diff --git a/packages/client/src/utils/helpers.ts b/packages/client/src/utils/helpers.ts
index a06b5b45e..d82ea9d55 100644
--- a/packages/client/src/utils/helpers.ts
+++ b/packages/client/src/utils/helpers.ts
@@ -1,6 +1,44 @@
-import { hexToBigInt } from 'viem';
+import { decodeAbiParameters, hexToBigInt } from 'viem';
-import type { Metadata } from '../utils/types';
+import {
+ type ArmorStats,
+ type Metadata,
+ StatsClasses,
+ WeaponStats,
+} from '../utils/types';
+
+export const decodeArmorStats = (statsBytes: string): ArmorStats => {
+ const itemTemplateStats = decodeAbiParameters(
+ [
+ {
+ name: 'armorStats',
+ type: 'tuple',
+ components: [
+ { name: 'agiModifier', type: 'int256' },
+ { name: 'armorModifier', type: 'uint256' },
+ { name: 'classRestrictions', type: 'uint8[]' },
+ { name: 'hitPointModifier', type: 'int256' },
+ { name: 'intModifier', type: 'int256' },
+ { name: 'minLevel', type: 'uint256' },
+ { name: 'strModifier', type: 'int256' },
+ ],
+ },
+ ],
+ statsBytes as `0x${string}`,
+ )[0];
+
+ return {
+ agiModifier: itemTemplateStats.agiModifier.toString(),
+ armorModifier: itemTemplateStats.armorModifier.toString(),
+ classRestrictions: itemTemplateStats.classRestrictions.map(
+ (classRestriction: number) => classRestriction as StatsClasses,
+ ),
+ hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
+ intModifier: itemTemplateStats.intModifier.toString(),
+ minLevel: itemTemplateStats.minLevel.toString(),
+ strModifier: itemTemplateStats.strModifier.toString(),
+ };
+};
export const decodeCharacterId = (
characterId: `0x${string}`,
@@ -18,6 +56,41 @@ export const decodeCharacterId = (
return { ownerAddress, characterTokenId: characterTokenId.toString() };
};
+export const decodeWeaponStats = (statsBytes: string): WeaponStats => {
+ const itemTemplateStats = decodeAbiParameters(
+ [
+ {
+ name: 'weaponStats',
+ type: 'tuple',
+ components: [
+ { name: 'agiModifier', type: 'int256' },
+ { name: 'classRestrictions', type: 'uint8[]' },
+ { name: 'hitPointModifier', type: 'int256' },
+ { name: 'intModifier', type: 'int256' },
+ { name: 'maxDamage', type: 'uint256' },
+ { name: 'minDamage', type: 'uint256' },
+ { name: 'minLevel', type: 'uint256' },
+ { name: 'strModifier', type: 'int256' },
+ ],
+ },
+ ],
+ statsBytes as `0x${string}`,
+ )[0];
+
+ return {
+ agiModifier: itemTemplateStats.agiModifier.toString(),
+ classRestrictions: itemTemplateStats.classRestrictions.map(
+ (classRestriction: number) => classRestriction as StatsClasses,
+ ),
+ hitPointModifier: itemTemplateStats.hitPointModifier.toString(),
+ intModifier: itemTemplateStats.intModifier.toString(),
+ maxDamage: itemTemplateStats.maxDamage.toString(),
+ minDamage: itemTemplateStats.minDamage.toString(),
+ minLevel: itemTemplateStats.minLevel.toString(),
+ strModifier: itemTemplateStats.strModifier.toString(),
+ };
+};
+
export const fetchMetadataFromUri = async (uri: string): Promise => {
const res = await fetch(uri);
if (!res.ok) throw new Error('Failed to fetch');
From 6b9acac6c6f97b04b87bc5af312cd8734e14a5e7 Mon Sep 17 00:00:00 2001
From: ECWireless
Date: Mon, 5 Aug 2024 08:12:01 -0600
Subject: [PATCH 2/2] Tweak starter item modifiers
---
packages/contracts/items.json | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/packages/contracts/items.json b/packages/contracts/items.json
index dafcf3636..c6e8bd8f3 100644
--- a/packages/contracts/items.json
+++ b/packages/contracts/items.json
@@ -24,14 +24,14 @@
"metadataUri": "2_rusty_sword.json",
"name": "Rusty Sword",
"stats": {
- "agiModifier": 4,
+ "agiModifier": 2,
"classRestrictions": [0],
"hitPointModifier": 6,
- "intModifier": 5,
+ "intModifier": 1,
"maxDamage": 2,
"minDamage": 1,
"minLevel": 1,
- "strModifier": 3
+ "strModifier": 5
}
},
{
@@ -43,11 +43,11 @@
"agiModifier": 4,
"classRestrictions": [1],
"hitPointModifier": 6,
- "intModifier": 5,
+ "intModifier": 2,
"maxDamage": 2,
"minDamage": 1,
"minLevel": 1,
- "strModifier": 3
+ "strModifier": 2
}
},
{
@@ -56,14 +56,14 @@
"metadataUri": "4_cobbled_wand.json",
"name": "Cobbled Wand",
"stats": {
- "agiModifier": 4,
+ "agiModifier": 2,
"classRestrictions": [2],
"hitPointModifier": 6,
"intModifier": 5,
"maxDamage": 2,
"minDamage": 1,
"minLevel": 1,
- "strModifier": 3
+ "strModifier": 1
}
}
]