From de7029b5d8d18a3fccaee804c2dbff171008491f Mon Sep 17 00:00:00 2001 From: dk1a Date: Thu, 28 Mar 2024 18:34:40 +0300 Subject: [PATCH 1/8] EscapeSystem --- packages/contracts/mud.config.ts | 3 + packages/contracts/script/PostDeploy.s.sol | 14 +- packages/contracts/src/codegen/index.sol | 1 + .../src/codegen/tables/EscapeIndex.sol | 199 ++++++++++++++++++ .../src/codegen/tables/GameConfig.sol | 174 ++++++++++++--- .../src/codegen/world/IEscapeSystem.sol | 13 ++ .../contracts/src/codegen/world/IWorld.sol | 2 + packages/contracts/src/constants.sol | 2 + .../src/libraries/LibEscapedStumpTokenURI.sol | 43 ++++ .../contracts/src/libraries/init/LibInit.sol | 4 +- .../src/systems/progression/EscapeSystem.sol | 30 +++ 11 files changed, 453 insertions(+), 32 deletions(-) create mode 100644 packages/contracts/src/codegen/tables/EscapeIndex.sol create mode 100644 packages/contracts/src/codegen/world/IEscapeSystem.sol create mode 100644 packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol create mode 100644 packages/contracts/src/systems/progression/EscapeSystem.sol diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index 099f2431..02139028 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -16,7 +16,9 @@ export default defineWorld({ schema: { adminAddress: "address", tokenAddress: "address", + escapedStumpTokenAddress: "address", globalSpawnIndex: "uint32", // Global index for all players + globalEscapeIndex: "uint32", // Global index for all players scaleDown: "uint32", // Used to scale down the amounts in the UI flowRate: "uint32", // Amount flowing from the inlet depotCapacity: "uint32", // Amount of material that can be stored in a depot @@ -36,6 +38,7 @@ export default defineWorld({ BuildIndex: "uint32", // Build index of a particular machine type in a particular pod BuildTracker: "uint32[]", // How many machines of each type have been built in pod since its creation? SpawnIndex: "uint32", // How many players have spawned? + EscapeIndex: "uint32", // How many players have escaped? // ... Tutorial: "bool", TutorialLevel: "uint32", diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index db536f1f..9ce9715c 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -6,6 +6,9 @@ import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { IERC20Mintable } from "@latticexyz/world-modules/src/modules/erc20-puppet/IERC20Mintable.sol"; import { registerERC20 } from "@latticexyz/world-modules/src/modules/erc20-puppet/registerERC20.sol"; import { ERC20MetadataData } from "@latticexyz/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol"; +import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; +import { registerERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/registerERC721.sol"; +import { ERC721MetadataData } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol"; import { IWorld } from "../src/codegen/world/IWorld.sol"; @@ -14,7 +17,7 @@ import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOw import { MATERIAL_TYPE } from "../src/codegen/common.sol"; import { LibOrder, LibInitRecipes, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; -import { ONE_MINUTE, ONE_DAY, ONE_HOUR } from "../src/constants.sol"; +import { ONE_MINUTE, ONE_DAY, ONE_HOUR, ESCAPED_STUMP_TOKEN_NAMESPACE } from "../src/constants.sol"; uint256 constant POOL_SUPPLY = 1_000_000 wei; @@ -38,9 +41,16 @@ contract PostDeploy is Script { token.mint(worldAddress, POOL_SUPPLY); + // Register ERC721 escaped stump token + IERC721Mintable escapedStumpToken = registerERC721( + world, + ESCAPED_STUMP_TOKEN_NAMESPACE, + ERC721MetadataData({ name: "TCM", symbol: "TCM", baseURI: "" }) + ); + // Initialize gameConfig and tutorial levels // Root namespace owner is admin - LibInit.init(NamespaceOwner.get(ROOT_NAMESPACE_ID), address(token)); + LibInit.init(NamespaceOwner.get(ROOT_NAMESPACE_ID), address(token), address(escapedStumpToken)); // Initialize recipes LibInitRecipes.init(); diff --git a/packages/contracts/src/codegen/index.sol b/packages/contracts/src/codegen/index.sol index 0c87e321..b80f9866 100644 --- a/packages/contracts/src/codegen/index.sol +++ b/packages/contracts/src/codegen/index.sol @@ -13,6 +13,7 @@ import { CarriedBy } from "./tables/CarriedBy.sol"; import { BuildIndex } from "./tables/BuildIndex.sol"; import { BuildTracker } from "./tables/BuildTracker.sol"; import { SpawnIndex } from "./tables/SpawnIndex.sol"; +import { EscapeIndex } from "./tables/EscapeIndex.sol"; import { Tutorial } from "./tables/Tutorial.sol"; import { TutorialLevel } from "./tables/TutorialLevel.sol"; import { Order, OrderData } from "./tables/Order.sol"; diff --git a/packages/contracts/src/codegen/tables/EscapeIndex.sol b/packages/contracts/src/codegen/tables/EscapeIndex.sol new file mode 100644 index 00000000..5c8e19d1 --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeIndex.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeIndex { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeIndex", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x74620000000000000000000000000000457363617065496e6465780000000000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (bytes32) + Schema constant _keySchema = Schema.wrap(0x002001005f000000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint32) + Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](1); + keyNames[0] = "id"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function get(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _get(bytes32 id) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(bytes32 id, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(uint32 value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + EncodedLengths _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(bytes32 id) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + return _keyTuple; + } +} diff --git a/packages/contracts/src/codegen/tables/GameConfig.sol b/packages/contracts/src/codegen/tables/GameConfig.sol index 1878406f..66bcc489 100644 --- a/packages/contracts/src/codegen/tables/GameConfig.sol +++ b/packages/contracts/src/codegen/tables/GameConfig.sol @@ -19,7 +19,9 @@ import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; struct GameConfigData { address adminAddress; address tokenAddress; + address escapedStumpTokenAddress; uint32 globalSpawnIndex; + uint32 globalEscapeIndex; uint32 scaleDown; uint32 flowRate; uint32 depotCapacity; @@ -30,12 +32,12 @@ library GameConfig { ResourceId constant _tableId = ResourceId.wrap(0x7462000000000000000000000000000047616d65436f6e666967000000000000); FieldLayout constant _fieldLayout = - FieldLayout.wrap(0x0038060014140404040400000000000000000000000000000000000000000000); + FieldLayout.wrap(0x0050080014141404040404040000000000000000000000000000000000000000); // Hex-encoded key schema of () Schema constant _keySchema = Schema.wrap(0x0000000000000000000000000000000000000000000000000000000000000000); - // Hex-encoded value schema of (address, address, uint32, uint32, uint32, uint32) - Schema constant _valueSchema = Schema.wrap(0x0038060061610303030300000000000000000000000000000000000000000000); + // Hex-encoded value schema of (address, address, address, uint32, uint32, uint32, uint32, uint32) + Schema constant _valueSchema = Schema.wrap(0x0050080061616103030303030000000000000000000000000000000000000000); /** * @notice Get the table's key field names. @@ -50,13 +52,15 @@ library GameConfig { * @return fieldNames An array of strings with the names of value fields. */ function getFieldNames() internal pure returns (string[] memory fieldNames) { - fieldNames = new string[](6); + fieldNames = new string[](8); fieldNames[0] = "adminAddress"; fieldNames[1] = "tokenAddress"; - fieldNames[2] = "globalSpawnIndex"; - fieldNames[3] = "scaleDown"; - fieldNames[4] = "flowRate"; - fieldNames[5] = "depotCapacity"; + fieldNames[2] = "escapedStumpTokenAddress"; + fieldNames[3] = "globalSpawnIndex"; + fieldNames[4] = "globalEscapeIndex"; + fieldNames[5] = "scaleDown"; + fieldNames[6] = "flowRate"; + fieldNames[7] = "depotCapacity"; } /** @@ -149,13 +153,51 @@ library GameConfig { StoreCore.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((tokenAddress)), _fieldLayout); } + /** + * @notice Get escapedStumpTokenAddress. + */ + function getEscapedStumpTokenAddress() internal view returns (address escapedStumpTokenAddress) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get escapedStumpTokenAddress. + */ + function _getEscapedStumpTokenAddress() internal view returns (address escapedStumpTokenAddress) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Set escapedStumpTokenAddress. + */ + function setEscapedStumpTokenAddress(address escapedStumpTokenAddress) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((escapedStumpTokenAddress)), _fieldLayout); + } + + /** + * @notice Set escapedStumpTokenAddress. + */ + function _setEscapedStumpTokenAddress(address escapedStumpTokenAddress) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((escapedStumpTokenAddress)), _fieldLayout); + } + /** * @notice Get globalSpawnIndex. */ function getGlobalSpawnIndex() internal view returns (uint32 globalSpawnIndex) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -165,7 +207,7 @@ library GameConfig { function _getGlobalSpawnIndex() internal view returns (uint32 globalSpawnIndex) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -175,7 +217,7 @@ library GameConfig { function setGlobalSpawnIndex(uint32 globalSpawnIndex) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((globalSpawnIndex)), _fieldLayout); } /** @@ -184,7 +226,45 @@ library GameConfig { function _setGlobalSpawnIndex(uint32 globalSpawnIndex) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 2, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((globalSpawnIndex)), _fieldLayout); + } + + /** + * @notice Get globalEscapeIndex. + */ + function getGlobalEscapeIndex() internal view returns (uint32 globalEscapeIndex) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get globalEscapeIndex. + */ + function _getGlobalEscapeIndex() internal view returns (uint32 globalEscapeIndex) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set globalEscapeIndex. + */ + function setGlobalEscapeIndex(uint32 globalEscapeIndex) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((globalEscapeIndex)), _fieldLayout); + } + + /** + * @notice Set globalEscapeIndex. + */ + function _setGlobalEscapeIndex(uint32 globalEscapeIndex) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((globalEscapeIndex)), _fieldLayout); } /** @@ -193,7 +273,7 @@ library GameConfig { function getScaleDown() internal view returns (uint32 scaleDown) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -203,7 +283,7 @@ library GameConfig { function _getScaleDown() internal view returns (uint32 scaleDown) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -213,7 +293,7 @@ library GameConfig { function setScaleDown(uint32 scaleDown) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((scaleDown)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((scaleDown)), _fieldLayout); } /** @@ -222,7 +302,7 @@ library GameConfig { function _setScaleDown(uint32 scaleDown) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 3, abi.encodePacked((scaleDown)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((scaleDown)), _fieldLayout); } /** @@ -231,7 +311,7 @@ library GameConfig { function getFlowRate() internal view returns (uint32 flowRate) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 6, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -241,7 +321,7 @@ library GameConfig { function _getFlowRate() internal view returns (uint32 flowRate) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 6, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -251,7 +331,7 @@ library GameConfig { function setFlowRate(uint32 flowRate) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((flowRate)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 6, abi.encodePacked((flowRate)), _fieldLayout); } /** @@ -260,7 +340,7 @@ library GameConfig { function _setFlowRate(uint32 flowRate) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 4, abi.encodePacked((flowRate)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 6, abi.encodePacked((flowRate)), _fieldLayout); } /** @@ -269,7 +349,7 @@ library GameConfig { function getDepotCapacity() internal view returns (uint32 depotCapacity) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 7, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -279,7 +359,7 @@ library GameConfig { function _getDepotCapacity() internal view returns (uint32 depotCapacity) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 7, _fieldLayout); return (uint32(bytes4(_blob))); } @@ -289,7 +369,7 @@ library GameConfig { function setDepotCapacity(uint32 depotCapacity) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((depotCapacity)), _fieldLayout); + StoreSwitch.setStaticField(_tableId, _keyTuple, 7, abi.encodePacked((depotCapacity)), _fieldLayout); } /** @@ -298,7 +378,7 @@ library GameConfig { function _setDepotCapacity(uint32 depotCapacity) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreCore.setStaticField(_tableId, _keyTuple, 5, abi.encodePacked((depotCapacity)), _fieldLayout); + StoreCore.setStaticField(_tableId, _keyTuple, 7, abi.encodePacked((depotCapacity)), _fieldLayout); } /** @@ -335,7 +415,9 @@ library GameConfig { function set( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -343,7 +425,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity @@ -363,7 +447,9 @@ library GameConfig { function _set( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -371,7 +457,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity @@ -392,7 +480,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -413,7 +503,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -438,7 +530,9 @@ library GameConfig { returns ( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -448,13 +542,17 @@ library GameConfig { tokenAddress = (address(Bytes.getBytes20(_blob, 20))); - globalSpawnIndex = (uint32(Bytes.getBytes4(_blob, 40))); + escapedStumpTokenAddress = (address(Bytes.getBytes20(_blob, 40))); + + globalSpawnIndex = (uint32(Bytes.getBytes4(_blob, 60))); + + globalEscapeIndex = (uint32(Bytes.getBytes4(_blob, 64))); - scaleDown = (uint32(Bytes.getBytes4(_blob, 44))); + scaleDown = (uint32(Bytes.getBytes4(_blob, 68))); - flowRate = (uint32(Bytes.getBytes4(_blob, 48))); + flowRate = (uint32(Bytes.getBytes4(_blob, 72))); - depotCapacity = (uint32(Bytes.getBytes4(_blob, 52))); + depotCapacity = (uint32(Bytes.getBytes4(_blob, 76))); } /** @@ -471,7 +569,9 @@ library GameConfig { ( _table.adminAddress, _table.tokenAddress, + _table.escapedStumpTokenAddress, _table.globalSpawnIndex, + _table.globalEscapeIndex, _table.scaleDown, _table.flowRate, _table.depotCapacity @@ -503,12 +603,24 @@ library GameConfig { function encodeStatic( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity ) internal pure returns (bytes memory) { - return abi.encodePacked(adminAddress, tokenAddress, globalSpawnIndex, scaleDown, flowRate, depotCapacity); + return + abi.encodePacked( + adminAddress, + tokenAddress, + escapedStumpTokenAddress, + globalSpawnIndex, + globalEscapeIndex, + scaleDown, + flowRate, + depotCapacity + ); } /** @@ -520,7 +632,9 @@ library GameConfig { function encode( address adminAddress, address tokenAddress, + address escapedStumpTokenAddress, uint32 globalSpawnIndex, + uint32 globalEscapeIndex, uint32 scaleDown, uint32 flowRate, uint32 depotCapacity @@ -528,7 +642,9 @@ library GameConfig { bytes memory _staticData = encodeStatic( adminAddress, tokenAddress, + escapedStumpTokenAddress, globalSpawnIndex, + globalEscapeIndex, scaleDown, flowRate, depotCapacity diff --git a/packages/contracts/src/codegen/world/IEscapeSystem.sol b/packages/contracts/src/codegen/world/IEscapeSystem.sol new file mode 100644 index 00000000..fbc01a0a --- /dev/null +++ b/packages/contracts/src/codegen/world/IEscapeSystem.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title IEscapeSystem + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface IEscapeSystem { + function escape() external; +} diff --git a/packages/contracts/src/codegen/world/IWorld.sol b/packages/contracts/src/codegen/world/IWorld.sol index cc3dc56d..da0015ae 100644 --- a/packages/contracts/src/codegen/world/IWorld.sol +++ b/packages/contracts/src/codegen/world/IWorld.sol @@ -15,6 +15,7 @@ import { IResolveSystem } from "./IResolveSystem.sol"; import { IDepotSystem } from "./IDepotSystem.sol"; import { IOfferSystem } from "./IOfferSystem.sol"; import { IOrderSystem } from "./IOrderSystem.sol"; +import { IEscapeSystem } from "./IEscapeSystem.sol"; import { INameSystem } from "./INameSystem.sol"; import { IRewardSystem } from "./IRewardSystem.sol"; import { ISpawnSystem } from "./ISpawnSystem.sol"; @@ -39,6 +40,7 @@ interface IWorld is IDepotSystem, IOfferSystem, IOrderSystem, + IEscapeSystem, INameSystem, IRewardSystem, ISpawnSystem, diff --git a/packages/contracts/src/constants.sol b/packages/contracts/src/constants.sol index 13cf2ffa..9fc4695d 100644 --- a/packages/contracts/src/constants.sol +++ b/packages/contracts/src/constants.sol @@ -10,3 +10,5 @@ uint32 constant ONE_DAY = 24 * ONE_HOUR; uint32 constant FLOW_RATE = 1000; uint32 constant NUMBER_OF_DEPOTS = 3; uint32 constant DEPOT_CAPACITY = 10000; + +bytes14 constant ESCAPED_STUMP_TOKEN_NAMESPACE = "EscapedStumpT"; diff --git a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol new file mode 100644 index 00000000..130a1b71 --- /dev/null +++ b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; + +import { TokenURI } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol"; +import { _tokenUriTableId } from "@latticexyz/world-modules/src/modules/erc721-puppet/utils.sol"; +import { Name } from "../codegen/index.sol"; +import { ESCAPED_STUMP_TOKEN_NAMESPACE } from "../constants.sol"; + +library LibEscapedStumpTokenURI { + string constant SVG_START = + '' + "" + ''; + string constant SVG_END = ""; + + function createURI(uint256 tokenId, bytes32 playerEntity) internal { + TokenURI.set(_tokenUriTableId(ESCAPED_STUMP_TOKEN_NAMESPACE), tokenId, _uri(playerEntity)); + } + + // TODO this uri is a placeholder + function _uri(bytes32 playerEntity) private view returns (string memory) { + string memory name = Name.get(playerEntity); + + string memory svg = string.concat( + SVG_START, + '', + name, + "", + SVG_END + ); + + // prettier-ignore + // (manual formatting to make json contents more readable) + string memory json = Base64.encode(abi.encodePacked( + '{"name": "', name, '",', + '"description": "Escaped stump",', + '"image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"}' + )); + + return string.concat("data:application/json;base64,", json); + } +} diff --git a/packages/contracts/src/libraries/init/LibInit.sol b/packages/contracts/src/libraries/init/LibInit.sol index 824e3ba0..669e2143 100644 --- a/packages/contracts/src/libraries/init/LibInit.sol +++ b/packages/contracts/src/libraries/init/LibInit.sol @@ -6,13 +6,15 @@ import { MATERIAL_TYPE } from "../../codegen/common.sol"; import { FLOW_RATE, DEPOT_CAPACITY } from "../../constants.sol"; library LibInit { - function init(address _adminAddress, address _tokenAddress) internal { + function init(address _adminAddress, address _tokenAddress, address _escapedStumpTokenAddress) internal { // Set game config GameConfig.set( GameConfigData({ adminAddress: _adminAddress, tokenAddress: _tokenAddress, + escapedStumpTokenAddress: _escapedStumpTokenAddress, globalSpawnIndex: 0, + globalEscapeIndex: 0, scaleDown: 100, flowRate: FLOW_RATE, depotCapacity: DEPOT_CAPACITY diff --git a/packages/contracts/src/systems/progression/EscapeSystem.sol b/packages/contracts/src/systems/progression/EscapeSystem.sol new file mode 100644 index 00000000..fa6e8daa --- /dev/null +++ b/packages/contracts/src/systems/progression/EscapeSystem.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; +import { System } from "@latticexyz/world/src/System.sol"; +import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; +import { LibUtils } from "../../libraries/Libraries.sol"; +import { CarriedBy, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; + +contract EscapeSystem is System { + function escape() public { + bytes32 playerEntity = LibUtils.addressToEntityKey(_msgSender()); + + // Tutorial must be completed + require(Tutorial.get(playerEntity) != false, "not completed"); + // Must be named before escape + require(bytes(Name.get(playerEntity)).length > 0, "name empty"); + // Must not have already escaped + require(EscapeIndex.get(playerEntity) == 0, "already escaped"); + // TODO determine and add more escape conditions if needed + + uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; + GameConfig.setGlobalEscapeIndex(newEscapeIndex); + EscapeIndex.set(playerEntity, newEscapeIndex); + + // TODO validate podEntity in other systems (preferable), or delete entity and machine types here + CarriedBy.deleteRecord(playerEntity); + + IERC721Mintable token = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); + token.safeMint(_msgSender(), newEscapeIndex); + } +} From 0d8978f8abe8e409a614dbfcdc15d18ad63e5ab3 Mon Sep 17 00:00:00 2001 From: dk1a Date: Fri, 5 Apr 2024 18:01:52 +0300 Subject: [PATCH 2/8] adjustments for nft structure --- packages/contracts/mud.config.ts | 8 + packages/contracts/src/codegen/index.sol | 1 + .../src/codegen/tables/EscapeIndexRanked.sol | 211 ++++++++++++++++++ .../src/libraries/LibEscapedStumpTokenURI.sol | 69 ++++-- .../contracts/src/libraries/Libraries.sol | 1 + .../src/systems/progression/EscapeSystem.sol | 19 +- 6 files changed, 282 insertions(+), 27 deletions(-) create mode 100644 packages/contracts/src/codegen/tables/EscapeIndexRanked.sol diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index 02139028..bd20a438 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -39,6 +39,14 @@ export default defineWorld({ BuildTracker: "uint32[]", // How many machines of each type have been built in pod since its creation? SpawnIndex: "uint32", // How many players have spawned? EscapeIndex: "uint32", // How many players have escaped? + EscapeIndexRanked: { + key: ["completedOrdersRank", "pointsRank"], + schema: { + completedOrdersRank: "uint32", + pointsRank: "uint32", + value: "uint32" + } + }, // ... Tutorial: "bool", TutorialLevel: "uint32", diff --git a/packages/contracts/src/codegen/index.sol b/packages/contracts/src/codegen/index.sol index b80f9866..6dd8530c 100644 --- a/packages/contracts/src/codegen/index.sol +++ b/packages/contracts/src/codegen/index.sol @@ -14,6 +14,7 @@ import { BuildIndex } from "./tables/BuildIndex.sol"; import { BuildTracker } from "./tables/BuildTracker.sol"; import { SpawnIndex } from "./tables/SpawnIndex.sol"; import { EscapeIndex } from "./tables/EscapeIndex.sol"; +import { EscapeIndexRanked } from "./tables/EscapeIndexRanked.sol"; import { Tutorial } from "./tables/Tutorial.sol"; import { TutorialLevel } from "./tables/TutorialLevel.sol"; import { Order, OrderData } from "./tables/Order.sol"; diff --git a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol new file mode 100644 index 00000000..07c14eda --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeIndexRanked { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeIndexRanke", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x74620000000000000000000000000000457363617065496e64657852616e6b65); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (uint32, uint32) + Schema constant _keySchema = Schema.wrap(0x0008020003030000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint32) + Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](2); + keyNames[0] = "completedOrdersRank"; + keyNames[1] = "pointsRank"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Get value. + */ + function _get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(uint32 value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + EncodedLengths _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(uint32 completedOrdersRank, uint32 pointsRank) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + return _keyTuple; + } +} diff --git a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol index 130a1b71..1c5ef76e 100644 --- a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol +++ b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol @@ -2,40 +2,63 @@ pragma solidity >=0.8.24; import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +import { WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; + +import { Puppet } from "@latticexyz/world-modules/src/modules/puppet/Puppet.sol"; import { TokenURI } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol"; import { _tokenUriTableId } from "@latticexyz/world-modules/src/modules/erc721-puppet/utils.sol"; -import { Name } from "../codegen/index.sol"; -import { ESCAPED_STUMP_TOKEN_NAMESPACE } from "../constants.sol"; + +import { GameConfig, Name, Completed } from "../codegen/index.sol"; library LibEscapedStumpTokenURI { - string constant SVG_START = - '' - "" - ''; - string constant SVG_END = ""; - - function createURI(uint256 tokenId, bytes32 playerEntity) internal { - TokenURI.set(_tokenUriTableId(ESCAPED_STUMP_TOKEN_NAMESPACE), tokenId, _uri(playerEntity)); - } + using WorldResourceIdInstance for ResourceId; + + function initTokenURI(uint256 _tokenId, bytes32 _playerEntity, uint256 _points, uint256 _completedOrders) internal { + address token = GameConfig.getEscapedStumpTokenAddress(); + ResourceId systemId = Puppet(token).systemId(); + ResourceId tableId = _tokenUriTableId(systemId.getNamespace()); - // TODO this uri is a placeholder - function _uri(bytes32 playerEntity) private view returns (string memory) { - string memory name = Name.get(playerEntity); + string memory uri = _uri(_playerEntity, _points, _completedOrders); + TokenURI.set(tableId, _tokenId, uri); + } - string memory svg = string.concat( - SVG_START, - '', - name, - "", - SVG_END - ); + function _uri(bytes32 _playerEntity, uint256 _points, uint256 _completedOrders) private view returns (string memory) { + string memory playerName = Name.get(_playerEntity); + // TODO rank name + string memory rankName = "placeholder"; // prettier-ignore // (manual formatting to make json contents more readable) string memory json = Base64.encode(abi.encodePacked( - '{"name": "', name, '",', + // TODO should this be the player name or sth else? + '{"name": "', playerName, '",', + // TODO custom descriptions? '"description": "Escaped stump",', - '"image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"}' + '"attributes": [' + '{' + '"trait_type": "Player Name",' + '"value": "', playerName, '"' + '},' + '{' + '"trait_type": "Rank Name",' + '"value": "', rankName, '"' + '},' + '{' + '"trait_type": "Points",' + '"value": ', _points, + '},' + '{' + '"trait_type": "Completed Orders",' + '"value": ', _completedOrders, + '},' + '{' + '"trait_type": "Escape Block Number",' + '"value": ', block.number, + '}' + '],' + '"image": "https://PLACEHOLDER_IMAGE_URL/', '', '"}' )); return string.concat("data:application/json;base64,", json); diff --git a/packages/contracts/src/libraries/Libraries.sol b/packages/contracts/src/libraries/Libraries.sol index 1a89e686..d0cfbea8 100644 --- a/packages/contracts/src/libraries/Libraries.sol +++ b/packages/contracts/src/libraries/Libraries.sol @@ -12,5 +12,6 @@ import { LibOrder } from "./LibOrder.sol"; import { LibReset } from "./LibReset.sol"; import { LibOffer } from "./LibOffer.sol"; import { LibToken } from "./LibToken.sol"; +import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; import { LibInit } from "./init/LibInit.sol"; import { LibInitRecipes } from "./init/LibInitRecipes.sol"; diff --git a/packages/contracts/src/systems/progression/EscapeSystem.sol b/packages/contracts/src/systems/progression/EscapeSystem.sol index fa6e8daa..415fbb05 100644 --- a/packages/contracts/src/systems/progression/EscapeSystem.sol +++ b/packages/contracts/src/systems/progression/EscapeSystem.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.21; import { System } from "@latticexyz/world/src/System.sol"; import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; -import { LibUtils } from "../../libraries/Libraries.sol"; -import { CarriedBy, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; +import { LibToken, LibUtils, LibEscapedStumpTokenURI } from "../../libraries/Libraries.sol"; +import { CarriedBy, Completed, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; contract EscapeSystem is System { function escape() public { @@ -20,11 +20,22 @@ contract EscapeSystem is System { uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; GameConfig.setGlobalEscapeIndex(newEscapeIndex); EscapeIndex.set(playerEntity, newEscapeIndex); + uint256 tokenId = newEscapeIndex; // TODO validate podEntity in other systems (preferable), or delete entity and machine types here CarriedBy.deleteRecord(playerEntity); - IERC721Mintable token = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); - token.safeMint(_msgSender(), newEscapeIndex); + // Convert all tokens to points, transferring the tokens to world + uint256 points = LibToken.getTokenBalance(_msgSender()); + LibToken.transferToken(_world(), points); + + // Get the number of completed orders + uint256 completedOrders = Completed.length(playerEntity); + + // Mint the NFT + IERC721Mintable escapedStumpToken = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); + escapedStumpToken.safeMint(_msgSender(), tokenId); + // And initialize its uri with the appropriate image and metadata + LibEscapedStumpTokenURI.initTokenURI(tokenId, playerEntity, points, completedOrders); } } From 2c4f7560e48d5f3a36c4b6d5472375c59a735a98 Mon Sep 17 00:00:00 2001 From: dk1a Date: Mon, 8 Apr 2024 17:23:44 +0300 Subject: [PATCH 3/8] EscapeRankName, fixes --- packages/contracts/mud.config.ts | 11 +- packages/contracts/script/PostDeploy.s.sol | 4 +- packages/contracts/src/codegen/index.sol | 1 + .../src/codegen/tables/EscapeIndexRanked.sol | 130 +++-- .../src/codegen/tables/EscapeRankName.sol | 497 ++++++++++++++++++ .../contracts/src/codegen/tables/Recipe.sol | 32 ++ .../libraries/init/LibInitEscapeRankNames.sol | 42 ++ 7 files changed, 665 insertions(+), 52 deletions(-) create mode 100644 packages/contracts/src/codegen/tables/EscapeRankName.sol create mode 100644 packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index bd20a438..ab1cfe0f 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -40,13 +40,22 @@ export default defineWorld({ SpawnIndex: "uint32", // How many players have spawned? EscapeIndex: "uint32", // How many players have escaped? EscapeIndexRanked: { - key: ["completedOrdersRank", "pointsRank"], + key: ["playerEntity", "completedOrdersRank", "pointsRank"], schema: { + playerEntity: "bytes32", completedOrdersRank: "uint32", pointsRank: "uint32", value: "uint32" } }, + EscapeRankName: { + key: ["completedOrdersRank", "pointsRank"], + schema: { + completedOrdersRank: "uint32", + pointsRank: "uint32", + value: "string" + } + }, // ... Tutorial: "bool", TutorialLevel: "uint32", diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index 27e6974b..ba45ef11 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -17,7 +17,7 @@ import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOw import { MATERIAL_TYPE } from "../src/codegen/common.sol"; import { LibOrder, LibInitRecipes, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; -import { ONE_MINUTE, ONE_DAY, ONE_HOUR, ESCAPED_STUMP_TOKEN_NAMESPACE } from "../src/constants.sol"; +import { ONE_MINUTE, ONE_DAY, ONE_HOUR } from "../src/constants.sol"; uint256 constant POOL_SUPPLY = 1_000_000 wei; @@ -44,7 +44,7 @@ contract PostDeploy is Script { // Register ERC721 escaped stump token IERC721Mintable escapedStumpToken = registerERC721( world, - ESCAPED_STUMP_TOKEN_NAMESPACE, + "EscapedStumpT", ERC721MetadataData({ name: "TCM", symbol: "TCM", baseURI: "" }) ); diff --git a/packages/contracts/src/codegen/index.sol b/packages/contracts/src/codegen/index.sol index 6dd8530c..a7bad392 100644 --- a/packages/contracts/src/codegen/index.sol +++ b/packages/contracts/src/codegen/index.sol @@ -15,6 +15,7 @@ import { BuildTracker } from "./tables/BuildTracker.sol"; import { SpawnIndex } from "./tables/SpawnIndex.sol"; import { EscapeIndex } from "./tables/EscapeIndex.sol"; import { EscapeIndexRanked } from "./tables/EscapeIndexRanked.sol"; +import { EscapeRankName } from "./tables/EscapeRankName.sol"; import { Tutorial } from "./tables/Tutorial.sol"; import { TutorialLevel } from "./tables/TutorialLevel.sol"; import { Order, OrderData } from "./tables/Order.sol"; diff --git a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol index 07c14eda..aa70f654 100644 --- a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol +++ b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol @@ -23,8 +23,8 @@ library EscapeIndexRanked { FieldLayout constant _fieldLayout = FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); - // Hex-encoded key schema of (uint32, uint32) - Schema constant _keySchema = Schema.wrap(0x0008020003030000000000000000000000000000000000000000000000000000); + // Hex-encoded key schema of (bytes32, uint32, uint32) + Schema constant _keySchema = Schema.wrap(0x002803005f030300000000000000000000000000000000000000000000000000); // Hex-encoded value schema of (uint32) Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); @@ -33,9 +33,10 @@ library EscapeIndexRanked { * @return keyNames An array of strings with the names of key fields. */ function getKeyNames() internal pure returns (string[] memory keyNames) { - keyNames = new string[](2); - keyNames[0] = "completedOrdersRank"; - keyNames[1] = "pointsRank"; + keyNames = new string[](3); + keyNames[0] = "playerEntity"; + keyNames[1] = "completedOrdersRank"; + keyNames[2] = "pointsRank"; } /** @@ -64,10 +65,15 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function getValue( + bytes32 playerEntity, + uint32 completedOrdersRank, + uint32 pointsRank + ) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -76,10 +82,15 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function _getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function _getValue( + bytes32 playerEntity, + uint32 completedOrdersRank, + uint32 pointsRank + ) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -88,10 +99,15 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function get( + bytes32 playerEntity, + uint32 completedOrdersRank, + uint32 pointsRank + ) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -100,10 +116,15 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function _get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function _get( + bytes32 playerEntity, + uint32 completedOrdersRank, + uint32 pointsRank + ) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -112,10 +133,11 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function setValue(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function setValue(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -123,10 +145,11 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function _setValue(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function _setValue(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -134,10 +157,11 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function set(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function set(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -145,10 +169,11 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function _set(uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function _set(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -156,10 +181,11 @@ library EscapeIndexRanked { /** * @notice Delete all data for given keys. */ - function deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function deleteRecord(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreSwitch.deleteRecord(_tableId, _keyTuple); } @@ -167,10 +193,11 @@ library EscapeIndexRanked { /** * @notice Delete all data for given keys. */ - function _deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function _deleteRecord(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } @@ -201,10 +228,15 @@ library EscapeIndexRanked { /** * @notice Encode keys as a bytes32 array using this table's field layout. */ - function encodeKeyTuple(uint32 completedOrdersRank, uint32 pointsRank) internal pure returns (bytes32[] memory) { - bytes32[] memory _keyTuple = new bytes32[](2); - _keyTuple[0] = bytes32(uint256(completedOrdersRank)); - _keyTuple[1] = bytes32(uint256(pointsRank)); + function encodeKeyTuple( + bytes32 playerEntity, + uint32 completedOrdersRank, + uint32 pointsRank + ) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = playerEntity; + _keyTuple[1] = bytes32(uint256(completedOrdersRank)); + _keyTuple[2] = bytes32(uint256(pointsRank)); return _keyTuple; } diff --git a/packages/contracts/src/codegen/tables/EscapeRankName.sol b/packages/contracts/src/codegen/tables/EscapeRankName.sol new file mode 100644 index 00000000..61763bb9 --- /dev/null +++ b/packages/contracts/src/codegen/tables/EscapeRankName.sol @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library EscapeRankName { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "EscapeRankName", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x7462000000000000000000000000000045736361706552616e6b4e616d650000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0000000100000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (uint32, uint32) + Schema constant _keySchema = Schema.wrap(0x0008020003030000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (string) + Schema constant _valueSchema = Schema.wrap(0x00000001c5000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](2); + keyNames[0] = "completedOrdersRank"; + keyNames[1] = "pointsRank"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function _getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get value. + */ + function _get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Set value. + */ + function setValue(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function _setValue(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function set(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Set value. + */ + function _set(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((value))); + } + + /** + * @notice Get the length of value. + */ + function lengthValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function _lengthValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function length(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of value. + */ + function _length(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemValue( + uint32 completedOrdersRank, + uint32 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemValue( + uint32 completedOrdersRank, + uint32 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItem( + uint32 completedOrdersRank, + uint32 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of value. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItem( + uint32 completedOrdersRank, + uint32 pointsRank, + uint256 _index + ) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to value. + */ + function pushValue(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function _pushValue(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function push(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to value. + */ + function _push(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Pop a slice from value. + */ + function popValue(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function _popValue(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function pop(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from value. + */ + function _pop(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Update a slice of value at `_index`. + */ + function updateValue(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function _updateValue(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function update(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of value at `_index`. + */ + function _update(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths(string memory value) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(bytes(value).length); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic(string memory value) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((value))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(string memory value) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData; + EncodedLengths _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(uint32 completedOrdersRank, uint32 pointsRank) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); + + return _keyTuple; + } +} diff --git a/packages/contracts/src/codegen/tables/Recipe.sol b/packages/contracts/src/codegen/tables/Recipe.sol index a3ef9ab6..5cc0afe1 100644 --- a/packages/contracts/src/codegen/tables/Recipe.sol +++ b/packages/contracts/src/codegen/tables/Recipe.sol @@ -171,6 +171,14 @@ library Recipe { _keyTuple[0] = bytes32(uint256(uint8(machineType))); _keyTuple[1] = bytes32(uint256(input)); + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + uint256 dynamicLength = _byteLength / 1; + uint256 staticLength = 2; + + if (_index < staticLength && _index >= dynamicLength) { + return (uint8(bytes1(new bytes(0)))); + } + unchecked { bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); return (uint8(bytes1(_blob))); @@ -186,6 +194,14 @@ library Recipe { _keyTuple[0] = bytes32(uint256(uint8(machineType))); _keyTuple[1] = bytes32(uint256(input)); + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + uint256 dynamicLength = _byteLength / 1; + uint256 staticLength = 2; + + if (_index < staticLength && _index >= dynamicLength) { + return (uint8(bytes1(new bytes(0)))); + } + unchecked { bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); return (uint8(bytes1(_blob))); @@ -201,6 +217,14 @@ library Recipe { _keyTuple[0] = bytes32(uint256(uint8(machineType))); _keyTuple[1] = bytes32(uint256(input)); + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + uint256 dynamicLength = _byteLength / 1; + uint256 staticLength = 2; + + if (_index < staticLength && _index >= dynamicLength) { + return (uint8(bytes1(new bytes(0)))); + } + unchecked { bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); return (uint8(bytes1(_blob))); @@ -216,6 +240,14 @@ library Recipe { _keyTuple[0] = bytes32(uint256(uint8(machineType))); _keyTuple[1] = bytes32(uint256(input)); + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + uint256 dynamicLength = _byteLength / 1; + uint256 staticLength = 2; + + if (_index < staticLength && _index >= dynamicLength) { + return (uint8(bytes1(new bytes(0)))); + } + unchecked { bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); return (uint8(bytes1(_blob))); diff --git a/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol b/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol new file mode 100644 index 00000000..f2f65621 --- /dev/null +++ b/packages/contracts/src/libraries/init/LibInitEscapeRankNames.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { EscapeRankName } from "../../codegen/index.sol"; + +library LibInitEscapeRankNames { + function init() internal { + // These represent the names of 25 grid cells, which determine the coolness of an escaped stump NFT + // The first key is completed orders rank, higher is better, 5 in total + // The second key is points spent rank, higher is better, 5 in total + + // TODO replace name placeholders + EscapeRankName.set(0, 0, "few orders, few points"); + EscapeRankName.set(0, 1, "0_1"); + EscapeRankName.set(0, 2, "0_2"); + EscapeRankName.set(0, 3, "0_3"); + EscapeRankName.set(0, 4, "few orders, many points"); + + EscapeRankName.set(1, 0, "1_0"); + EscapeRankName.set(1, 1, "1_1"); + EscapeRankName.set(1, 2, "1_2"); + EscapeRankName.set(1, 3, "1_3"); + EscapeRankName.set(1, 4, "1_4"); + + EscapeRankName.set(2, 0, "2_0"); + EscapeRankName.set(2, 1, "2_1"); + EscapeRankName.set(2, 2, "2_2"); + EscapeRankName.set(2, 3, "2_3"); + EscapeRankName.set(2, 4, "2_4"); + + EscapeRankName.set(3, 0, "3_0"); + EscapeRankName.set(3, 1, "3_1"); + EscapeRankName.set(3, 2, "3_2"); + EscapeRankName.set(3, 3, "3_3"); + EscapeRankName.set(3, 4, "3_4"); + + EscapeRankName.set(4, 0, "many orders, few points"); + EscapeRankName.set(4, 1, "4_1"); + EscapeRankName.set(4, 2, "4_2"); + EscapeRankName.set(4, 3, "4_3"); + EscapeRankName.set(4, 4, "many orders, many points"); + } +} From cc997e26cdd6732e681365a9bb094aeb41272439 Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 10 Apr 2024 19:38:13 +0300 Subject: [PATCH 4/8] LibEscape, fixes, ranked index --- packages/contracts/mud.config.ts | 11 +- .../src/codegen/tables/EscapeIndexRanked.sol | 130 +++++++----------- .../src/codegen/tables/EscapeRankName.sol | 79 ++++++----- packages/contracts/src/constants.sol | 11 ++ .../contracts/src/libraries/LibEscape.sol | 60 ++++++++ .../src/libraries/LibEscapedStumpTokenURI.sol | 58 +++++++- .../contracts/src/libraries/Libraries.sol | 1 + .../src/systems/progression/EscapeSystem.sol | 21 +-- 8 files changed, 225 insertions(+), 146 deletions(-) create mode 100644 packages/contracts/src/libraries/LibEscape.sol diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index ab1cfe0f..fdc2f502 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -40,19 +40,18 @@ export default defineWorld({ SpawnIndex: "uint32", // How many players have spawned? EscapeIndex: "uint32", // How many players have escaped? EscapeIndexRanked: { - key: ["playerEntity", "completedOrdersRank", "pointsRank"], + key: ["completedOrdersRank", "pointsRank"], schema: { - playerEntity: "bytes32", - completedOrdersRank: "uint32", - pointsRank: "uint32", + completedOrdersRank: "uint256", + pointsRank: "uint256", value: "uint32" } }, EscapeRankName: { key: ["completedOrdersRank", "pointsRank"], schema: { - completedOrdersRank: "uint32", - pointsRank: "uint32", + completedOrdersRank: "uint256", + pointsRank: "uint256", value: "string" } }, diff --git a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol index aa70f654..a30f0aeb 100644 --- a/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol +++ b/packages/contracts/src/codegen/tables/EscapeIndexRanked.sol @@ -23,8 +23,8 @@ library EscapeIndexRanked { FieldLayout constant _fieldLayout = FieldLayout.wrap(0x0004010004000000000000000000000000000000000000000000000000000000); - // Hex-encoded key schema of (bytes32, uint32, uint32) - Schema constant _keySchema = Schema.wrap(0x002803005f030300000000000000000000000000000000000000000000000000); + // Hex-encoded key schema of (uint256, uint256) + Schema constant _keySchema = Schema.wrap(0x004002001f1f0000000000000000000000000000000000000000000000000000); // Hex-encoded value schema of (uint32) Schema constant _valueSchema = Schema.wrap(0x0004010003000000000000000000000000000000000000000000000000000000); @@ -33,10 +33,9 @@ library EscapeIndexRanked { * @return keyNames An array of strings with the names of key fields. */ function getKeyNames() internal pure returns (string[] memory keyNames) { - keyNames = new string[](3); - keyNames[0] = "playerEntity"; - keyNames[1] = "completedOrdersRank"; - keyNames[2] = "pointsRank"; + keyNames = new string[](2); + keyNames[0] = "completedOrdersRank"; + keyNames[1] = "pointsRank"; } /** @@ -65,15 +64,10 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function getValue( - bytes32 playerEntity, - uint32 completedOrdersRank, - uint32 pointsRank - ) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -82,15 +76,10 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function _getValue( - bytes32 playerEntity, - uint32 completedOrdersRank, - uint32 pointsRank - ) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function _getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -99,15 +88,10 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function get( - bytes32 playerEntity, - uint32 completedOrdersRank, - uint32 pointsRank - ) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -116,15 +100,10 @@ library EscapeIndexRanked { /** * @notice Get value. */ - function _get( - bytes32 playerEntity, - uint32 completedOrdersRank, - uint32 pointsRank - ) internal view returns (uint32 value) { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function _get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); return (uint32(bytes4(_blob))); @@ -133,11 +112,10 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function setValue(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function setValue(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -145,11 +123,10 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function _setValue(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function _setValue(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -157,11 +134,10 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function set(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function set(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -169,11 +145,10 @@ library EscapeIndexRanked { /** * @notice Set value. */ - function _set(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank, uint32 value) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function _set(uint256 completedOrdersRank, uint256 pointsRank, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } @@ -181,11 +156,10 @@ library EscapeIndexRanked { /** * @notice Delete all data for given keys. */ - function deleteRecord(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreSwitch.deleteRecord(_tableId, _keyTuple); } @@ -193,11 +167,10 @@ library EscapeIndexRanked { /** * @notice Delete all data for given keys. */ - function _deleteRecord(bytes32 playerEntity, uint32 completedOrdersRank, uint32 pointsRank) internal { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function _deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } @@ -228,15 +201,10 @@ library EscapeIndexRanked { /** * @notice Encode keys as a bytes32 array using this table's field layout. */ - function encodeKeyTuple( - bytes32 playerEntity, - uint32 completedOrdersRank, - uint32 pointsRank - ) internal pure returns (bytes32[] memory) { - bytes32[] memory _keyTuple = new bytes32[](3); - _keyTuple[0] = playerEntity; - _keyTuple[1] = bytes32(uint256(completedOrdersRank)); - _keyTuple[2] = bytes32(uint256(pointsRank)); + function encodeKeyTuple(uint256 completedOrdersRank, uint256 pointsRank) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(completedOrdersRank)); + _keyTuple[1] = bytes32(uint256(pointsRank)); return _keyTuple; } diff --git a/packages/contracts/src/codegen/tables/EscapeRankName.sol b/packages/contracts/src/codegen/tables/EscapeRankName.sol index 61763bb9..b4d080c4 100644 --- a/packages/contracts/src/codegen/tables/EscapeRankName.sol +++ b/packages/contracts/src/codegen/tables/EscapeRankName.sol @@ -23,8 +23,8 @@ library EscapeRankName { FieldLayout constant _fieldLayout = FieldLayout.wrap(0x0000000100000000000000000000000000000000000000000000000000000000); - // Hex-encoded key schema of (uint32, uint32) - Schema constant _keySchema = Schema.wrap(0x0008020003030000000000000000000000000000000000000000000000000000); + // Hex-encoded key schema of (uint256, uint256) + Schema constant _keySchema = Schema.wrap(0x004002001f1f0000000000000000000000000000000000000000000000000000); // Hex-encoded value schema of (string) Schema constant _valueSchema = Schema.wrap(0x00000001c5000000000000000000000000000000000000000000000000000000); @@ -64,7 +64,7 @@ library EscapeRankName { /** * @notice Get value. */ - function getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + function getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -76,7 +76,7 @@ library EscapeRankName { /** * @notice Get value. */ - function _getValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + function _getValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -88,7 +88,7 @@ library EscapeRankName { /** * @notice Get value. */ - function get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + function get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -100,7 +100,7 @@ library EscapeRankName { /** * @notice Get value. */ - function _get(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (string memory value) { + function _get(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (string memory value) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -112,7 +112,7 @@ library EscapeRankName { /** * @notice Set value. */ - function setValue(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + function setValue(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -123,7 +123,7 @@ library EscapeRankName { /** * @notice Set value. */ - function _setValue(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + function _setValue(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -134,7 +134,7 @@ library EscapeRankName { /** * @notice Set value. */ - function set(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + function set(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -145,7 +145,7 @@ library EscapeRankName { /** * @notice Set value. */ - function _set(uint32 completedOrdersRank, uint32 pointsRank, string memory value) internal { + function _set(uint256 completedOrdersRank, uint256 pointsRank, string memory value) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -156,7 +156,7 @@ library EscapeRankName { /** * @notice Get the length of value. */ - function lengthValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + function lengthValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -170,7 +170,7 @@ library EscapeRankName { /** * @notice Get the length of value. */ - function _lengthValue(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + function _lengthValue(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -184,7 +184,7 @@ library EscapeRankName { /** * @notice Get the length of value. */ - function length(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + function length(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -198,7 +198,7 @@ library EscapeRankName { /** * @notice Get the length of value. */ - function _length(uint32 completedOrdersRank, uint32 pointsRank) internal view returns (uint256) { + function _length(uint256 completedOrdersRank, uint256 pointsRank) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -214,8 +214,8 @@ library EscapeRankName { * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. */ function getItemValue( - uint32 completedOrdersRank, - uint32 pointsRank, + uint256 completedOrdersRank, + uint256 pointsRank, uint256 _index ) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](2); @@ -233,8 +233,8 @@ library EscapeRankName { * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. */ function _getItemValue( - uint32 completedOrdersRank, - uint32 pointsRank, + uint256 completedOrdersRank, + uint256 pointsRank, uint256 _index ) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](2); @@ -252,8 +252,8 @@ library EscapeRankName { * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. */ function getItem( - uint32 completedOrdersRank, - uint32 pointsRank, + uint256 completedOrdersRank, + uint256 pointsRank, uint256 _index ) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](2); @@ -271,8 +271,8 @@ library EscapeRankName { * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. */ function _getItem( - uint32 completedOrdersRank, - uint32 pointsRank, + uint256 completedOrdersRank, + uint256 pointsRank, uint256 _index ) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](2); @@ -288,7 +288,7 @@ library EscapeRankName { /** * @notice Push a slice to value. */ - function pushValue(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + function pushValue(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -299,7 +299,7 @@ library EscapeRankName { /** * @notice Push a slice to value. */ - function _pushValue(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + function _pushValue(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -310,7 +310,7 @@ library EscapeRankName { /** * @notice Push a slice to value. */ - function push(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + function push(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -321,7 +321,7 @@ library EscapeRankName { /** * @notice Push a slice to value. */ - function _push(uint32 completedOrdersRank, uint32 pointsRank, string memory _slice) internal { + function _push(uint256 completedOrdersRank, uint256 pointsRank, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -332,7 +332,7 @@ library EscapeRankName { /** * @notice Pop a slice from value. */ - function popValue(uint32 completedOrdersRank, uint32 pointsRank) internal { + function popValue(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -343,7 +343,7 @@ library EscapeRankName { /** * @notice Pop a slice from value. */ - function _popValue(uint32 completedOrdersRank, uint32 pointsRank) internal { + function _popValue(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -354,7 +354,7 @@ library EscapeRankName { /** * @notice Pop a slice from value. */ - function pop(uint32 completedOrdersRank, uint32 pointsRank) internal { + function pop(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -365,7 +365,7 @@ library EscapeRankName { /** * @notice Pop a slice from value. */ - function _pop(uint32 completedOrdersRank, uint32 pointsRank) internal { + function _pop(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -376,7 +376,7 @@ library EscapeRankName { /** * @notice Update a slice of value at `_index`. */ - function updateValue(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + function updateValue(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -390,7 +390,12 @@ library EscapeRankName { /** * @notice Update a slice of value at `_index`. */ - function _updateValue(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + function _updateValue( + uint256 completedOrdersRank, + uint256 pointsRank, + uint256 _index, + string memory _slice + ) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -404,7 +409,7 @@ library EscapeRankName { /** * @notice Update a slice of value at `_index`. */ - function update(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + function update(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -418,7 +423,7 @@ library EscapeRankName { /** * @notice Update a slice of value at `_index`. */ - function _update(uint32 completedOrdersRank, uint32 pointsRank, uint256 _index, string memory _slice) internal { + function _update(uint256 completedOrdersRank, uint256 pointsRank, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -432,7 +437,7 @@ library EscapeRankName { /** * @notice Delete all data for given keys. */ - function deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + function deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -443,7 +448,7 @@ library EscapeRankName { /** * @notice Delete all data for given keys. */ - function _deleteRecord(uint32 completedOrdersRank, uint32 pointsRank) internal { + function _deleteRecord(uint256 completedOrdersRank, uint256 pointsRank) internal { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); @@ -487,7 +492,7 @@ library EscapeRankName { /** * @notice Encode keys as a bytes32 array using this table's field layout. */ - function encodeKeyTuple(uint32 completedOrdersRank, uint32 pointsRank) internal pure returns (bytes32[] memory) { + function encodeKeyTuple(uint256 completedOrdersRank, uint256 pointsRank) internal pure returns (bytes32[] memory) { bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = bytes32(uint256(completedOrdersRank)); _keyTuple[1] = bytes32(uint256(pointsRank)); diff --git a/packages/contracts/src/constants.sol b/packages/contracts/src/constants.sol index bba9858a..9e88aee5 100644 --- a/packages/contracts/src/constants.sol +++ b/packages/contracts/src/constants.sol @@ -10,3 +10,14 @@ uint32 constant ONE_DAY = 24 * ONE_HOUR; uint32 constant FLOW_RATE = 1000; uint32 constant NUMBER_OF_DEPOTS = 3; uint32 constant DEPOT_CAPACITY = 25000; + +function COMPLETED_ORDERS_THRESHOLDS() pure returns (uint256[4] memory) { + return [uint256(10), 15, 20, 25]; +} + +function POINTS_THRESHOLDS() pure returns (uint256[4] memory) { + return [uint256(10_000), 15_000, 20_000, 25_000]; +} + +// For the purpose of image retrieval, ranked indexes start at 1 and end at this cap inclusively +uint32 constant ESCAPE_INDEX_RANKED_IMAGE_CAP = 11; diff --git a/packages/contracts/src/libraries/LibEscape.sol b/packages/contracts/src/libraries/LibEscape.sol new file mode 100644 index 00000000..f3d8bc92 --- /dev/null +++ b/packages/contracts/src/libraries/LibEscape.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; +import { WorldContextConsumerLib } from "@latticexyz/world/src/WorldContext.sol"; + +import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; + +import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; +import { GameConfig, EscapeIndex, EscapeIndexRanked } from "../codegen/index.sol"; +import { COMPLETED_ORDERS_THRESHOLDS, POINTS_THRESHOLDS } from "../constants.sol"; + +library LibEscape { + function escapePod(bytes32 _playerEntity, uint256 _completedOrders, uint256 _points) internal { + // Increment global escape index + uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; + GameConfig.setGlobalEscapeIndex(newEscapeIndex); + // Mark the player entity as escaped + EscapeIndex.set(_playerEntity, newEscapeIndex); + + // Calculate ranks + uint256 completedOrdersRank = _rank(COMPLETED_ORDERS_THRESHOLDS(), _completedOrders); + uint256 pointsRank = _rank(POINTS_THRESHOLDS(), _points); + + // Increment ranked escape index (separate for each combination of ranks) + uint32 newEscapeIndexRanked = EscapeIndexRanked.get(completedOrdersRank, pointsRank) + 1; + EscapeIndexRanked.set(completedOrdersRank, pointsRank, newEscapeIndexRanked); + + // Derive tokenId from the global escape index, which is unique + uint256 tokenId = newEscapeIndex; + + // Generate and set TokenURI for this token + LibEscapedStumpTokenURI.initTokenURI( + tokenId, + _playerEntity, + _completedOrders, + _points, + completedOrdersRank, + pointsRank, + newEscapeIndexRanked + ); + + // Mint the NFT + IERC721Mintable escapedStumpToken = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); + escapedStumpToken.safeMint(WorldContextConsumerLib._msgSender(), tokenId); + } + + /** + * @notice Rank is index+1 of the highest threshold less-than-or-equal the provided value. + * 0 if value doesn't exceed any thresholds. + * @param _thresholds Ascending thresholds. + * @param _value Value to compare to thresholds. + */ + function _rank(uint256[4] memory _thresholds, uint256 _value) private pure returns (uint256) { + for (uint256 i = _thresholds.length - 1; i >= 0; i--) { + if (_value >= _thresholds[i]) { + return i + 1; + } + } + return 0; + } +} diff --git a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol index 1c5ef76e..25dafc14 100644 --- a/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol +++ b/packages/contracts/src/libraries/LibEscapedStumpTokenURI.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.24; import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; @@ -10,24 +11,66 @@ import { Puppet } from "@latticexyz/world-modules/src/modules/puppet/Puppet.sol" import { TokenURI } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol"; import { _tokenUriTableId } from "@latticexyz/world-modules/src/modules/erc721-puppet/utils.sol"; -import { GameConfig, Name, Completed } from "../codegen/index.sol"; +import { GameConfig, Name, EscapeRankName, Completed } from "../codegen/index.sol"; +import { ESCAPE_INDEX_RANKED_IMAGE_CAP } from "../constants.sol"; library LibEscapedStumpTokenURI { using WorldResourceIdInstance for ResourceId; - function initTokenURI(uint256 _tokenId, bytes32 _playerEntity, uint256 _points, uint256 _completedOrders) internal { + function initTokenURI( + uint256 _tokenId, + bytes32 _playerEntity, + uint256 _completedOrders, + uint256 _points, + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) internal { address token = GameConfig.getEscapedStumpTokenAddress(); ResourceId systemId = Puppet(token).systemId(); ResourceId tableId = _tokenUriTableId(systemId.getNamespace()); - string memory uri = _uri(_playerEntity, _points, _completedOrders); + string memory uri = _uri( + _playerEntity, + _completedOrders, + _points, + _completedOrdersRank, + _pointsRank, + _escapeIndexRanked + ); TokenURI.set(tableId, _tokenId, uri); } - function _uri(bytes32 _playerEntity, uint256 _points, uint256 _completedOrders) private view returns (string memory) { + function _getImageName( + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) private pure returns (string memory) { + uint32 cappedIndex = _escapeIndexRanked; + if (cappedIndex > ESCAPE_INDEX_RANKED_IMAGE_CAP) { + cappedIndex = ESCAPE_INDEX_RANKED_IMAGE_CAP; + } + return + string.concat( + Strings.toString(_completedOrdersRank), + "_", + Strings.toString(_pointsRank), + "_", + Strings.toString(cappedIndex) + ); + } + + function _uri( + bytes32 _playerEntity, + uint256 _completedOrders, + uint256 _points, + uint256 _completedOrdersRank, + uint256 _pointsRank, + uint32 _escapeIndexRanked + ) private view returns (string memory) { string memory playerName = Name.get(_playerEntity); - // TODO rank name - string memory rankName = "placeholder"; + string memory rankName = EscapeRankName.get(_completedOrdersRank, _pointsRank); + string memory imageName = _getImageName(_completedOrdersRank, _pointsRank, _escapeIndexRanked); // prettier-ignore // (manual formatting to make json contents more readable) @@ -58,7 +101,8 @@ library LibEscapedStumpTokenURI { '"value": ', block.number, '}' '],' - '"image": "https://PLACEHOLDER_IMAGE_URL/', '', '"}' + // TODO real image url + '"image": "https://PLACEHOLDER_IMAGE_URL/', imageName, '"}' )); return string.concat("data:application/json;base64,", json); diff --git a/packages/contracts/src/libraries/Libraries.sol b/packages/contracts/src/libraries/Libraries.sol index d0cfbea8..78a67edf 100644 --- a/packages/contracts/src/libraries/Libraries.sol +++ b/packages/contracts/src/libraries/Libraries.sol @@ -12,6 +12,7 @@ import { LibOrder } from "./LibOrder.sol"; import { LibReset } from "./LibReset.sol"; import { LibOffer } from "./LibOffer.sol"; import { LibToken } from "./LibToken.sol"; +import { LibEscape } from "./LibEscape.sol"; import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; import { LibInit } from "./init/LibInit.sol"; import { LibInitRecipes } from "./init/LibInitRecipes.sol"; diff --git a/packages/contracts/src/systems/progression/EscapeSystem.sol b/packages/contracts/src/systems/progression/EscapeSystem.sol index 415fbb05..834829b8 100644 --- a/packages/contracts/src/systems/progression/EscapeSystem.sol +++ b/packages/contracts/src/systems/progression/EscapeSystem.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; import { System } from "@latticexyz/world/src/System.sol"; -import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol"; -import { LibToken, LibUtils, LibEscapedStumpTokenURI } from "../../libraries/Libraries.sol"; +import { LibToken, LibUtils, LibEscape } from "../../libraries/Libraries.sol"; import { CarriedBy, Completed, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; contract EscapeSystem is System { @@ -17,25 +16,17 @@ contract EscapeSystem is System { require(EscapeIndex.get(playerEntity) == 0, "already escaped"); // TODO determine and add more escape conditions if needed - uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; - GameConfig.setGlobalEscapeIndex(newEscapeIndex); - EscapeIndex.set(playerEntity, newEscapeIndex); - uint256 tokenId = newEscapeIndex; - // TODO validate podEntity in other systems (preferable), or delete entity and machine types here CarriedBy.deleteRecord(playerEntity); + // Get the number of completed orders + uint256 completedOrders = Completed.length(playerEntity); + // Convert all tokens to points, transferring the tokens to world uint256 points = LibToken.getTokenBalance(_msgSender()); LibToken.transferToken(_world(), points); - // Get the number of completed orders - uint256 completedOrders = Completed.length(playerEntity); - - // Mint the NFT - IERC721Mintable escapedStumpToken = IERC721Mintable(GameConfig.getEscapedStumpTokenAddress()); - escapedStumpToken.safeMint(_msgSender(), tokenId); - // And initialize its uri with the appropriate image and metadata - LibEscapedStumpTokenURI.initTokenURI(tokenId, playerEntity, points, completedOrders); + // Update indexes and mint NFT for _msgSender() + LibEscape.escapePod(playerEntity, completedOrders, points); } } From d7d545194921dfbf36cae1a35816ae7775b6492a Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 10 Apr 2024 19:46:59 +0300 Subject: [PATCH 5/8] init rank names --- packages/contracts/script/PostDeploy.s.sol | 5 ++++- packages/contracts/src/libraries/Libraries.sol | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index 464300e7..73126292 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -16,7 +16,7 @@ import { ROOT_NAMESPACE_ID } from "@latticexyz/world/src/constants.sol"; import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOwner.sol"; import { MATERIAL_TYPE } from "../src/codegen/common.sol"; -import { LibOrder, LibInitRecipes, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; +import { LibOrder, LibInitRecipes, LibInitEscapeRankNames, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; import { ONE_MINUTE, ONE_DAY, ONE_HOUR } from "../src/constants.sol"; uint256 constant POOL_SUPPLY = 1_000_000 wei; @@ -55,6 +55,9 @@ contract PostDeploy is Script { // Initialize recipes LibInitRecipes.init(); + // Initialize rank names for escaped pod NFTs + LibInitEscapeRankNames.init(); + // Create offer LibOffer.create(MATERIAL_TYPE.BUG, 10000, 100); // 1:1 ratio : 100 $BUG => 10000 Bug (Shown as 100 Bugs with scale-down in UI) diff --git a/packages/contracts/src/libraries/Libraries.sol b/packages/contracts/src/libraries/Libraries.sol index b62bf2a1..5485962c 100644 --- a/packages/contracts/src/libraries/Libraries.sol +++ b/packages/contracts/src/libraries/Libraries.sol @@ -15,3 +15,4 @@ import { LibEscape } from "./LibEscape.sol"; import { LibEscapedStumpTokenURI } from "./LibEscapedStumpTokenURI.sol"; import { LibInit } from "./init/LibInit.sol"; import { LibInitRecipes } from "./init/LibInitRecipes.sol"; +import { LibInitEscapeRankNames } from "./init/LibInitEscapeRankNames.sol"; From 1178abcc2f77109b89f9afaf1c7e6340709d460b Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 10 Apr 2024 20:48:38 +0300 Subject: [PATCH 6/8] tests, fixes --- .../src/codegen/world/IEscapeSystem.sol | 2 +- .../contracts/src/libraries/LibEscape.sol | 2 +- .../src/systems/progression/EscapeSystem.sol | 6 +-- .../systems/progression/EscapeSystem.t.sol | 49 +++++++++++++++++++ 4 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 packages/contracts/test/systems/progression/EscapeSystem.t.sol diff --git a/packages/contracts/src/codegen/world/IEscapeSystem.sol b/packages/contracts/src/codegen/world/IEscapeSystem.sol index fbc01a0a..66a823ae 100644 --- a/packages/contracts/src/codegen/world/IEscapeSystem.sol +++ b/packages/contracts/src/codegen/world/IEscapeSystem.sol @@ -9,5 +9,5 @@ pragma solidity >=0.8.24; * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. */ interface IEscapeSystem { - function escape() external; + function escapePod() external; } diff --git a/packages/contracts/src/libraries/LibEscape.sol b/packages/contracts/src/libraries/LibEscape.sol index f3d8bc92..aac3a0b2 100644 --- a/packages/contracts/src/libraries/LibEscape.sol +++ b/packages/contracts/src/libraries/LibEscape.sol @@ -9,7 +9,7 @@ import { GameConfig, EscapeIndex, EscapeIndexRanked } from "../codegen/index.sol import { COMPLETED_ORDERS_THRESHOLDS, POINTS_THRESHOLDS } from "../constants.sol"; library LibEscape { - function escapePod(bytes32 _playerEntity, uint256 _completedOrders, uint256 _points) internal { + function incrementIndexesAndMint(bytes32 _playerEntity, uint256 _completedOrders, uint256 _points) internal { // Increment global escape index uint32 newEscapeIndex = GameConfig.getGlobalEscapeIndex() + 1; GameConfig.setGlobalEscapeIndex(newEscapeIndex); diff --git a/packages/contracts/src/systems/progression/EscapeSystem.sol b/packages/contracts/src/systems/progression/EscapeSystem.sol index 834829b8..30c3cbb2 100644 --- a/packages/contracts/src/systems/progression/EscapeSystem.sol +++ b/packages/contracts/src/systems/progression/EscapeSystem.sol @@ -5,11 +5,11 @@ import { LibToken, LibUtils, LibEscape } from "../../libraries/Libraries.sol"; import { CarriedBy, Completed, EscapeIndex, GameConfig, Tutorial, Name } from "../../codegen/index.sol"; contract EscapeSystem is System { - function escape() public { + function escapePod() public { bytes32 playerEntity = LibUtils.addressToEntityKey(_msgSender()); // Tutorial must be completed - require(Tutorial.get(playerEntity) != false, "not completed"); + require(Tutorial.get(playerEntity) == false, "not completed"); // Must be named before escape require(bytes(Name.get(playerEntity)).length > 0, "name empty"); // Must not have already escaped @@ -27,6 +27,6 @@ contract EscapeSystem is System { LibToken.transferToken(_world(), points); // Update indexes and mint NFT for _msgSender() - LibEscape.escapePod(playerEntity, completedOrders, points); + LibEscape.incrementIndexesAndMint(playerEntity, completedOrders, points); } } diff --git a/packages/contracts/test/systems/progression/EscapeSystem.t.sol b/packages/contracts/test/systems/progression/EscapeSystem.t.sol new file mode 100644 index 00000000..e7f76345 --- /dev/null +++ b/packages/contracts/test/systems/progression/EscapeSystem.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity >=0.8.24; +import { IERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721.sol"; +import { ROOT_NAMESPACE_ID } from "@latticexyz/world/src/constants.sol"; +import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOwner.sol"; +import { BaseTest } from "../../BaseTest.sol"; +import "../../../src/codegen/index.sol"; +import "../../../src/libraries/Libraries.sol"; +import { COMPLETED_ORDERS_THRESHOLDS, POINTS_THRESHOLDS } from "../../../src/constants.sol"; + +contract EscapeSystemTest is BaseTest { + function testAllRankNamesExist() public { + for ( + uint256 completedOrdersRank; + completedOrdersRank <= COMPLETED_ORDERS_THRESHOLDS().length; + completedOrdersRank++ + ) { + for (uint256 pointsRank; pointsRank <= POINTS_THRESHOLDS().length; pointsRank++) { + string memory rankName = EscapeRankName.get(completedOrdersRank, pointsRank); + assertGt(bytes(rankName).length, 0); + } + } + } + + function testEscape() public { + vm.startPrank(alice); + bytes32 playerEntity = world.spawn(); + world.start(); + vm.stopPrank(); + + _skipTutorial(playerEntity); + + vm.startPrank(alice); + world.name("test name"); + world.escapePod(); + vm.stopPrank(); + + assertEq(CarriedBy.get(playerEntity), bytes32(0)); + + uint256 tokenId = uint256(GameConfig.getGlobalEscapeIndex()); + IERC721 escapedStumpToken = IERC721(GameConfig.getEscapedStumpTokenAddress()); + assertEq(escapedStumpToken.ownerOf(tokenId), alice); + assertEq(EscapeIndexRanked.get(0, 0), 1); + } + + function _skipTutorial(bytes32 _playerEntity) private adminPrank { + TutorialLevel.set(_playerEntity, 3); + } +} From ae030fde5674afa891224e1f60d435021fa41576 Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 10 Apr 2024 21:03:02 +0300 Subject: [PATCH 7/8] fix loop --- packages/contracts/src/libraries/LibEscape.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/contracts/src/libraries/LibEscape.sol b/packages/contracts/src/libraries/LibEscape.sol index aac3a0b2..2ffedc25 100644 --- a/packages/contracts/src/libraries/LibEscape.sol +++ b/packages/contracts/src/libraries/LibEscape.sol @@ -50,7 +50,9 @@ library LibEscape { * @param _value Value to compare to thresholds. */ function _rank(uint256[4] memory _thresholds, uint256 _value) private pure returns (uint256) { - for (uint256 i = _thresholds.length - 1; i >= 0; i--) { + uint256 i = _thresholds.length; + while (i > 0) { + i--; if (_value >= _thresholds[i]) { return i + 1; } From c5960f070f5fe853c0200ea30561be0e2b74403c Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 10 Apr 2024 22:54:53 +0300 Subject: [PATCH 8/8] fix token ownership --- packages/contracts/script/PostDeploy.s.sol | 7 ++++++- .../contracts/src/codegen/world/IRewardSystem.sol | 15 --------------- 2 files changed, 6 insertions(+), 16 deletions(-) delete mode 100644 packages/contracts/src/codegen/world/IRewardSystem.sol diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index 73126292..8e60f0b2 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -12,8 +12,9 @@ import { ERC721MetadataData } from "@latticexyz/world-modules/src/modules/erc721 import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; import { ROOT_NAMESPACE_ID } from "@latticexyz/world/src/constants.sol"; -import { NamespaceOwner } from "@latticexyz/world/src/codegen/tables/NamespaceOwner.sol"; +import { NamespaceOwner } from "@latticexyz/world/src/codegen/index.sol"; import { MATERIAL_TYPE } from "../src/codegen/common.sol"; import { LibOrder, LibInitRecipes, LibInitEscapeRankNames, LibInit, LibOffer } from "../src/libraries/Libraries.sol"; @@ -48,6 +49,10 @@ contract PostDeploy is Script { ERC721MetadataData({ name: "TCM", symbol: "TCM", baseURI: "" }) ); + // Transfer token namespaces to World + world.transferOwnership(WorldResourceIdLib.encodeNamespace("Token"), worldAddress); + world.transferOwnership(WorldResourceIdLib.encodeNamespace("EscapedStumpT"), worldAddress); + // Initialize gameConfig and tutorial levels // Root namespace owner is admin LibInit.init(NamespaceOwner.get(ROOT_NAMESPACE_ID), address(token), address(escapedStumpToken)); diff --git a/packages/contracts/src/codegen/world/IRewardSystem.sol b/packages/contracts/src/codegen/world/IRewardSystem.sol deleted file mode 100644 index 14234850..00000000 --- a/packages/contracts/src/codegen/world/IRewardSystem.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.24; - -/* Autogenerated file. Do not edit manually. */ - -/** - * @title IRewardSystem - * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) - * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. - */ -interface IRewardSystem { - function reward() external; - - function charge() external; -}