diff --git a/.gitignore b/.gitignore index 1e30ccce..d157e895 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,7 @@ **/**/worlds.json # MUD Client generated static data **/dist +# Worlds.json +**/worlds.json .nx/cache diff --git a/core/smart-object-framework/mud.config.ts b/core/smart-object-framework/mud.config.ts index ccdb5b34..96b67170 100644 --- a/core/smart-object-framework/mud.config.ts +++ b/core/smart-object-framework/mud.config.ts @@ -1,6 +1,7 @@ import { mudConfig } from "@latticexyz/world/register"; export default mudConfig({ + namespace: "SmartObject_v0", excludeSystems: ["EveSystem"], systems: { EntityCore: { @@ -27,8 +28,9 @@ export default mudConfig({ keySchema: { "typeId": "uint8" }, valueSchema: { doesExists: "bool", - typeName: "bytes32" - } + typeName: "bytes32", + }, + tableIdArgument: true, }, /** * Used to register an entity by its type @@ -38,7 +40,8 @@ export default mudConfig({ valueSchema: { doesExists: "bool", //Tracks the entity which is no longer valid entityType: "uint8" - } + }, + tableIdArgument: true, }, /** * Used to enforce association/tagging possibility by entity types @@ -49,29 +52,32 @@ export default mudConfig({ keySchema: { "entityType": "uint8", "taggedEntityType": "uint8" }, valueSchema: { isAllowed: "bool" - } + }, + tableIdArgument: true, }, /** * Used to tag/map an entity by its tagged entityIds * eg: Similar objects can be grouped as Class, and tagged with a classId. * One entity can be tagged with multiple classIds */ - EntityMapTable: { + EntityMap: { keySchema: { "entityId": "uint256" }, valueSchema: { taggedEntityIds: "uint256[]" - } + }, + tableIdArgument: true, }, /** * Used to associate a entity with a specific set of modules and hooks * to inherit the functionality of those modules(systems) and hooks */ - EntityAssociationTable: { + EntityAssociation: { keySchema: { "entityId": "uint256" }, valueSchema: { moduleIds: "uint256[]", hookIds: "uint256[]" - } + }, + tableIdArgument: true, }, /************************ @@ -86,18 +92,20 @@ export default mudConfig({ //Can add functions registered in this system if we need granular control moduleName: "bytes16", doesExists: "bool", - } + }, + tableIdArgument: true, }, /** * Only used for lookup purpose to find the moduleIds associated with a system * TODO - Do we need this table? */ - ModuleSystemLookupTable: { + ModuleSystemLookup: { keySchema: { "moduleId": "uint256" }, valueSchema: { systemIds: "bytes32[]" - } + }, + tableIdArgument: true, }, /************************ @@ -113,29 +121,32 @@ export default mudConfig({ isHook: "bool", systemId: "ResourceId", //Callback systemId of the hook functionSelector: "bytes4" //Callback functionId of the hook - } + }, + tableIdArgument: true, }, /** * Used to map the function to be executed before a existing function in a system by hookId */ - HookTargetBeforeTable: { + HookTargetBefore: { keySchema: { "hookId": "uint256", "targetId": "uint256" }, // targetId - uint256(keccak(systemSelector, functionId)) valueSchema: { hasHook: "bool", systemSelector: "ResourceId", //Target system to hook against functionSelector: "bytes4" //Target function to hook against - } + }, + tableIdArgument: true, }, /** * Used to map the function to be executed after a existing function in a system by hookId */ - HookTargetAfterTable: { + HookTargetAfter: { keySchema: { "hookId": "uint256", "targetId": "uint256" }, valueSchema: { hasHook: "bool", systemSelector: "ResourceId", //Target system to hook against functionSelector: "bytes4" //Target function to hook against - } + }, + tableIdArgument: true, }, }, }); diff --git a/core/smart-object-framework/src/SmartObjectFrameworkModule.sol b/core/smart-object-framework/src/SmartObjectFrameworkModule.sol new file mode 100644 index 00000000..7d8e7934 --- /dev/null +++ b/core/smart-object-framework/src/SmartObjectFrameworkModule.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { Module } from "@latticexyz/world/src/Module.sol"; +import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; + +import { SMART_OBJECT_MODULE_NAME as MODULE_NAME, SMART_OBJECT_MODULE_NAMESPACE as MODULE_NAMESPACE } from "./constants.sol"; +import { Utils } from "./utils.sol"; + +import { EntityCore } from "./systems/core/EntityCore.sol"; +import { ModuleCore } from "./systems/core/ModuleCore.sol"; +import { HookCore } from "./systems/core/HookCore.sol"; + +import { EntityAssociation } from "./codegen/tables/EntityAssociation.sol"; +import { EntityMap } from "./codegen/tables/EntityMap.sol"; +import { EntityTable } from "./codegen/tables/EntityTable.sol"; +import { EntityType } from "./codegen/tables/EntityType.sol"; +import { EntityTypeAssociation } from "./codegen/tables/EntityTypeAssociation.sol"; +import { HookTable } from "./codegen/tables/HookTable.sol"; +import { HookTargetAfter } from "./codegen/tables/HookTargetAfter.sol"; +import { HookTargetBefore } from "./codegen/tables/HookTargetBefore.sol"; +import { ModuleSystemLookup } from "./codegen/tables/ModuleSystemLookup.sol"; +import { ModuleTable } from "./codegen/tables/ModuleTable.sol"; + +contract SmartObjectFrameworkModule is Module { + error SmartObjectFrameworkModule_InvalidNamespace(bytes14 namespace); + + address immutable registrationLibrary = address(new SmartObjectFrameworkModuleRegistrationLibrary()); + + function getName() public pure returns (bytes16) { + return MODULE_NAME; + } + + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + return super.supportsInterface(interfaceId); + } + + function _requireDependencies() internal view { + // Require other modules to be installed + // (not the case here) + // + // if (!isInstalled(bytes16("MODULE_NAME"), new bytes(0))) { + // revert Module_MissingDependency(string(bytes.concat("MODULE_NAME"))); + // } + } + + function install(bytes memory encodedArgs) public { + // Require the module to not be installed with these args yet + requireNotInstalled(__self, encodedArgs); + + // Extract args + bytes14 namespace = abi.decode(encodedArgs, (bytes14)); + + // Require the namespace to not be the module's namespace + if (namespace == MODULE_NAMESPACE) { + revert SmartObjectFrameworkModule_InvalidNamespace(namespace); + } + + // Require dependencies + _requireDependencies(); + + // Register the smart object framework's tables and systems + IBaseWorld world = IBaseWorld(_world()); + (bool success, bytes memory returnedData) = registrationLibrary.delegatecall( + abi.encodeCall(SmartObjectFrameworkModuleRegistrationLibrary.register, (world, namespace)) + ); + if (!success) revertWithBytes(returnedData); + + // Transfer ownership of the namespace to the caller + ResourceId namespaceId = WorldResourceIdLib.encodeNamespace(namespace); + world.transferOwnership(namespaceId, _msgSender()); + } + + // would be a very bad idea (see issue #5 on Github) + function installRoot(bytes memory) public pure { + revert Module_RootInstallNotSupported(); + } +} + +contract SmartObjectFrameworkModuleRegistrationLibrary { + using Utils for bytes14; + + /** + * Register systems and tables for a new smart object framework in a given namespace + */ + function register(IBaseWorld world, bytes14 namespace) public { + // Register the namespace + world.registerNamespace(WorldResourceIdLib.encodeNamespace(namespace)); + // Register the tables + EntityAssociation.register(namespace.entityAssociationTableId()); + EntityMap.register(namespace.entityMapTableId()); + EntityTable.register(namespace.entityTableTableId()); + EntityType.register(namespace.entityTypeTableId()); + EntityTypeAssociation.register(namespace.entityTypeAssociationTableId()); + HookTable.register(namespace.hookTableTableId()); + HookTargetAfter.register(namespace.hookTargetAfterTableId()); + HookTargetBefore.register(namespace.hookTargetBeforeTableId()); + ModuleSystemLookup.register(namespace.moduleSystemLookupTableId()); + ModuleTable.register(namespace.moduleTableTableId()); + + // Register a new Systems suite + world.registerSystem(namespace.entityCoreSystemId(), new EntityCore(), true); + world.registerSystem(namespace.moduleCoreSystemId(), new ModuleCore(), true); + world.registerSystem(namespace.hookCoreSystemId(), new HookCore(), true); + } +} diff --git a/core/smart-object-framework/src/SmartObjectLib.sol b/core/smart-object-framework/src/SmartObjectLib.sol new file mode 100644 index 00000000..6f64c815 --- /dev/null +++ b/core/smart-object-framework/src/SmartObjectLib.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; + +import { Utils } from "./utils.sol"; +import { HookType } from "./types.sol"; + +import { IEntityCore } from "./interfaces/IEntityCore.sol"; +import { IHookCore } from "./interfaces/IHookCore.sol"; +import { IModuleCore } from "./interfaces/IModuleCore.sol"; + +/** + * @title Smart Object Framework Library (makes interacting with the underlying Systems cleaner) + * Works similarly to direct calls to world, without having to deal with dynamic method's function selectors due to namespacing. + * @dev To preserve _msgSender() and other context-dependant properties, Library methods like those MUST be `internal`. + * That way, the compiler is forced to inline the method's implementation in the contract they're imported into. + * + * TODO: the way we generate the interfaces used below is brittle; it's a semi-manual process + * (generate with `worldgen` while setting `namespace` in `mud.config.ts` to "") + * changes to any Core contract won't reflect in either the library, or the interfaces it imports + */ +library SmartObjectLib { + using Utils for bytes14; + + struct World { + IBaseWorld iface; + bytes14 namespace; + } + + // EntityCore methods + function registerEntityType(World memory world, uint8 entityTypeId, bytes32 entityType) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.registerEntityType, (entityTypeId, entityType)) + ); + } + + function registerEntity(World memory world, uint256 entityId, uint8 entityType) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.registerEntity, (entityId, entityType)) + ); + } + + function registerEntities(World memory world, uint256[] memory entityId, uint8[] memory entityType) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.registerEntities, (entityId, entityType)) + ); + } + + function registerEntityTypeAssociation(World memory world, uint8 entityType, uint8 tagEntityType) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.registerEntityTypeAssociation, (entityType, tagEntityType)) + ); + } + + function tagEntity(World memory world, uint256 entityId, uint256 entityTagId) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.tagEntity, (entityId, entityTagId)) + ); + } + + function tagEntities(World memory world, uint256 entityId, uint256[] memory entityTagIds) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.tagEntities, (entityId, entityTagIds)) + ); + } + + function removeEntityTag(World memory world, uint256 entityId, uint256 entityTagId) internal { + world.iface.call( + world.namespace.entityCoreSystemId(), + abi.encodeCall(IEntityCore.removeEntityTag, (entityId, entityTagId)) + ); + } + + // HookCore methods + function registerHook(World memory world, ResourceId systemId, bytes4 functionId) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.registerHook, (systemId, functionId)) + ); + } + + function addHook( + World memory world, + uint256 hookId, + HookType hookType, + ResourceId systemId, + bytes4 functionSelector + ) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.addHook, (hookId, hookType, systemId, functionSelector)) + ); + } + + function removeHook( + World memory world, + uint256 hookId, + HookType hookType, + ResourceId systemId, + bytes4 functionSelector + ) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.removeHook, (hookId, hookType, systemId, functionSelector)) + ); + } + + function associateHook(World memory world, uint256 entityId, uint256 hookId) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.associateHook, (entityId, hookId)) + ); + } + + function associateHooks(World memory world, uint256 entityId, uint256[] memory hookIds) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.associateHooks, (entityId, hookIds)) + ); + } + + function removeEntityHookAssociation(World memory world, uint256 entityId, uint256 hookId) internal { + world.iface.call( + world.namespace.hookCoreSystemId(), + abi.encodeCall(IHookCore.removeEntityHookAssociation, (entityId, hookId)) + ); + } + + // ModuleCore methods + function registerEVEModule(World memory world, uint256 moduleId, bytes16 moduleName, ResourceId systemId) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.registerEVEModule, (moduleId, moduleName, systemId)) + ); + } + + function registerEVEModules( + World memory world, + uint256 moduleId, + bytes16 moduleName, + ResourceId[] memory systemIds + ) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.registerEVEModules, (moduleId, moduleName, systemIds)) + ); + } + + function associateModule(World memory world, uint256 entityId, uint256 moduleId) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.associateModule, (entityId, moduleId)) + ); + } + + function associateModules(World memory world, uint256 entityId, uint256[] memory moduleIds) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.associateModules, (entityId, moduleIds)) + ); + } + + function removeEntityModuleAssociation(World memory world, uint256 entityId, uint256 moduleId) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.removeEntityModuleAssociation, (entityId, moduleId)) + ); + } + + function removeSystemModuleAssociation(World memory world, ResourceId systemId, uint256 moduleId) internal { + world.iface.call( + world.namespace.moduleCoreSystemId(), + abi.encodeCall(IModuleCore.removeSystemModuleAssociation, (systemId, moduleId)) + ); + } +} diff --git a/core/smart-object-framework/src/constants.sol b/core/smart-object-framework/src/constants.sol index 596224de..ccb02885 100644 --- a/core/smart-object-framework/src/constants.sol +++ b/core/smart-object-framework/src/constants.sol @@ -1,5 +1,38 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { RESOURCE_NAMESPACE } from "@latticexyz/world/src/worldResourceTypes.sol"; + uint8 constant INVALID_ID = 0; uint256 constant EMPTY_MODULE_ID = uint256(keccak256("EMPTY_MODULE_ID")); + +bytes16 constant SMART_OBJECT_MODULE_NAME = "SmartObjModule"; +bytes14 constant SMART_OBJECT_MODULE_NAMESPACE = "SmartObjModule"; + +// TODO: this needs to match with the namespace used during `world.installModule(smartObjectModule, abi.encode("namespace"))` +// and/or match the `namespace` field in mud.config.ts, depending how you deploy the Smart Object Framework +// (e.g. through `mud deploy` or inside a post-deployment Forge script) +// it's a bit of an inconvenience, but refactoring the namespace used to access the right tables in EveSystem dynamically would +// require to read some form of storage, and that makes up for a lot of read operations and it's costly +// so it's best to just hard-code this for now +bytes14 constant SMART_OBJECT_DEPLOYMENT_NAMESPACE = "SmartObject_v0"; + +ResourceId constant SMART_OBJECT_MODULE_NAMESPACE_ID = ResourceId.wrap( + bytes32(abi.encodePacked(RESOURCE_NAMESPACE, SMART_OBJECT_MODULE_NAMESPACE)) +); + +bytes16 constant ENTITY_TABLE_NAME = "EntityTable"; +bytes16 constant MODULE_TABLE_NAME = "ModuleTable"; +bytes16 constant HOOK_TABLE_NAME = "HookTable"; +bytes16 constant ENTITY_TYPE_NAME = "EntityType"; +bytes16 constant ENTITY_TYPE_ASSOCIATION_NAME = "EntityTypeAssoci"; +bytes16 constant ENTITY_MAP_NAME = "EntityMap"; +bytes16 constant ENTITY_ASSOCIATION_NAME = "EntityAssociatio"; +bytes16 constant MODULE_SYSTEM_LOOKUP_NAME = "ModuleSystemLook"; +bytes16 constant HOOK_TARGET_BEFORE_NAME = "HookTargetBefore"; +bytes16 constant HOOK_TARGET_AFTER_NAME = "HookTargetAfter"; + +bytes16 constant ENTITY_CORE_SYSTEM_NAME = "EntityCoreSystem"; +bytes16 constant MODULE_CORE_SYSTEM_NAME = "ModuleCoreSystem"; +bytes16 constant HOOK_CORE_SYSTEM_NAME = "HookCoreSystem"; diff --git a/core/smart-object-framework/src/interfaces/IEntityCore.sol b/core/smart-object-framework/src/interfaces/IEntityCore.sol new file mode 100644 index 00000000..50dd0d63 --- /dev/null +++ b/core/smart-object-framework/src/interfaces/IEntityCore.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/** + * @title IEntityCore + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + * Needs to match corresponding System exhaustively + */ +interface IEntityCore { + function registerEntityType(uint8 entityTypeId, bytes32 entityType) external; + + function registerEntity(uint256 entityId, uint8 entityType) external; + + function registerEntities(uint256[] memory entityId, uint8[] memory entityType) external; + + function registerEntityTypeAssociation(uint8 entityType, uint8 tagEntityType) external; + + function tagEntity(uint256 entityId, uint256 entityTagId) external; + + function tagEntities(uint256 entityId, uint256[] memory entityTagIds) external; + + function removeEntityTag(uint256 entityId, uint256 entityTagId) external; +} \ No newline at end of file diff --git a/core/smart-object-framework/src/interfaces/IHookCore.sol b/core/smart-object-framework/src/interfaces/IHookCore.sol new file mode 100644 index 00000000..34fc12e4 --- /dev/null +++ b/core/smart-object-framework/src/interfaces/IHookCore.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; +import { HookType } from "../types.sol"; + +/** + * @title IHookCore + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + * Needs to match corresponding System exhaustively + */ +interface IHookCore { + function registerHook(ResourceId systemId, bytes4 functionId) external; + + function addHook(uint256 hookId, HookType hookType, ResourceId systemId, bytes4 functionSelector) external; + + function removeHook(uint256 hookId, HookType hookType, ResourceId systemId, bytes4 functionSelector) external; + + function associateHook(uint256 entityId, uint256 hookId) external; + + function associateHooks(uint256 entityId, uint256[] memory hookIds) external; + + function removeEntityHookAssociation(uint256 entityId, uint256 hookId) external; +} \ No newline at end of file diff --git a/core/smart-object-framework/src/interfaces/IModuleCore.sol b/core/smart-object-framework/src/interfaces/IModuleCore.sol new file mode 100644 index 00000000..f6892a07 --- /dev/null +++ b/core/smart-object-framework/src/interfaces/IModuleCore.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; + +/** + * @title IModuleCore + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + * Needs to match corresponding System exhaustively + */ +interface IModuleCore { + function registerEVEModule(uint256 moduleId, bytes16 moduleName, ResourceId systemId) external; + + function registerEVEModules(uint256 moduleId, bytes16 moduleName, ResourceId[] memory systemIds) external; + + function associateModule(uint256 entityId, uint256 moduleId) external; + + function associateModules(uint256 entityId, uint256[] memory moduleIds) external; + + function removeEntityModuleAssociation(uint256 entityId, uint256 moduleId) external; + + function removeSystemModuleAssociation(ResourceId systemId, uint256 moduleId) external; +} \ No newline at end of file diff --git a/core/smart-object-framework/src/systems/core/EntityCore.sol b/core/smart-object-framework/src/systems/core/EntityCore.sol index 62c64afc..c32d45b3 100644 --- a/core/smart-object-framework/src/systems/core/EntityCore.sol +++ b/core/smart-object-framework/src/systems/core/EntityCore.sol @@ -4,17 +4,21 @@ pragma solidity >=0.8.21; import { EntityTable } from "../../codegen/tables/EntityTable.sol"; import { EntityType } from "../../codegen/tables/EntityType.sol"; import { EntityTypeAssociation } from "../../codegen/tables/EntityTypeAssociation.sol"; -import { EntityMapTable } from "../../codegen/tables/EntityMapTable.sol"; +import { EntityMap } from "../../codegen/tables/EntityMap.sol"; import { ICustomErrorSystem } from "../../codegen/world/ICustomErrorSystem.sol"; import { EveSystem } from "../internal/EveSystem.sol"; import { INVALID_ID } from "../../constants.sol"; +import { Utils } from "../../utils.sol"; + /** * @title EntityCore * @dev EntityCore is a system that manages entities such as Classes and objects. * */ contract EntityCore is EveSystem { + using Utils for bytes14; + // Modifiers modifier requireValidEntityId(uint256 entityId) { if (entityId == INVALID_ID) revert ICustomErrorSystem.InvalidEntityId(); @@ -22,7 +26,7 @@ contract EntityCore is EveSystem { } modifier requireEntityTypeExists(uint8 entityType) { - if (EntityType.getDoesExists(entityType) == false) + if (EntityType.getDoesExists(_namespace().entityTypeTableId(), entityType) == false) revert ICustomErrorSystem.EntityTypeNotRegistered(entityType, "EntityCore: EntityType not registered"); _; } @@ -44,11 +48,11 @@ contract EntityCore is EveSystem { function registerEntity(uint256 entityId, uint8 entityType) external { _registerEntity(entityId, entityType); } - + /** - * @notice Overloaded function to register multiple entities + * @notice Overloaded function to register multiple entities */ - function registerEntity(uint256[] memory entityId, uint8[] memory entityType) external { + function registerEntities(uint256[] memory entityId, uint8[] memory entityType) external { if (entityId.length != entityType.length) revert ICustomErrorSystem.InvalidArrayLength( entityId.length, @@ -85,7 +89,7 @@ contract EntityCore is EveSystem { /** * @notice Overloaded function to tagEntity under multiple entities */ - function tagEntity(uint256 entityId, uint256[] memory entityTagIds) external { + function tagEntities(uint256 entityId, uint256[] memory entityTagIds) external { for (uint256 i = 0; i < entityTagIds.length; i++) { _tagEntity(entityId, entityTagIds[i]); } @@ -101,24 +105,24 @@ contract EntityCore is EveSystem { } function _registerEntityType(uint8 entityTypeId, bytes32 entityType) internal { - if (EntityType.getDoesExists(entityTypeId) == true) + if (EntityType.getDoesExists(_namespace().entityTypeTableId(), entityTypeId) == true) revert ICustomErrorSystem.EntityTypeAlreadyRegistered(entityTypeId, "EntityCore: EntityType already registered"); - EntityType.set(entityTypeId, true, entityType); + EntityType.set(_namespace().entityTypeTableId(), entityTypeId, true, entityType); } function _registerEntity( uint256 entityId, uint8 entityType ) internal requireValidEntityId(entityId) requireEntityTypeExists(entityType) { - if (EntityTable.getDoesExists(entityId) == true) + if (EntityTable.getDoesExists(_namespace().entityTableTableId(), entityId) == true) revert ICustomErrorSystem.EntityAlreadyRegistered(entityId, "EntityCore: Entity already registered"); - EntityTable.set(entityId, true, entityType); + EntityTable.set(_namespace().entityTableTableId(), entityId, true, entityType); } function _registerEntityTypeAssociation(uint8 entityType, uint8 tagEntityType) internal { - EntityTypeAssociation.set(entityType, tagEntityType, true); + EntityTypeAssociation.set(_namespace().entityTypeAssociationTableId(), entityType, tagEntityType, true); } function _tagEntity(uint256 entityId, uint256 entityTagId) internal { @@ -126,33 +130,33 @@ contract EntityCore is EveSystem { _requireEntityRegistered(entityTagId); _requireAssociationAllowed(entityId, entityTagId); - uint256[] memory taggedEntities = EntityMapTable.get(entityId); + uint256[] memory taggedEntities = EntityMap.get(_namespace().entityMapTableId(), entityId); (, bool exists) = findIndex(taggedEntities, entityTagId); if (exists) revert ICustomErrorSystem.EntityAlreadyTagged(entityId, entityTagId, "EntityCore: Entity already tagged"); - EntityMapTable.pushTaggedEntityIds(entityId, entityTagId); + EntityMap.pushTaggedEntityIds(_namespace().entityMapTableId(), entityId, entityTagId); } function _removeEntityTag(uint256 entityId, uint256 entityTagId) internal { //TODO Have to figure out a clean way to remove an element from an array - uint256[] memory taggedEntities = EntityMapTable.get(entityId); + uint256[] memory taggedEntities = EntityMap.get(_namespace().entityMapTableId(), entityId); (uint256 index, bool exists) = findIndex(taggedEntities, entityTagId); if (exists) { // Swap the element with the last one and pop the last element uint256 lastIndex = taggedEntities.length - 1; if (index != lastIndex) { - EntityMapTable.update(entityId, index, taggedEntities[lastIndex]); + EntityMap.update(_namespace().entityMapTableId(), entityId, index, taggedEntities[lastIndex]); } - EntityMapTable.pop(entityId); + EntityMap.pop(_namespace().entityMapTableId(), entityId); } } function _requireAssociationAllowed(uint256 entityId, uint256 entityTagId) internal view { - uint8 entityType = EntityTable.getEntityType(entityId); - uint8 tagEntityType = EntityTable.getEntityType(entityTagId); + uint8 entityType = EntityTable.getEntityType(_namespace().entityTableTableId(), entityId); + uint8 tagEntityType = EntityTable.getEntityType(_namespace().entityTableTableId(), entityTagId); - if (EntityTypeAssociation.get(entityType, tagEntityType) == false) + if (EntityTypeAssociation.get(_namespace().entityTypeAssociationTableId(), entityType, tagEntityType) == false) revert ICustomErrorSystem.EntityTypeAssociationNotAllowed( entityType, tagEntityType, diff --git a/core/smart-object-framework/src/systems/core/HookCore.sol b/core/smart-object-framework/src/systems/core/HookCore.sol index d89a7a5a..16c90115 100644 --- a/core/smart-object-framework/src/systems/core/HookCore.sol +++ b/core/smart-object-framework/src/systems/core/HookCore.sol @@ -2,16 +2,20 @@ pragma solidity >=0.8.21; import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; -import { EntityMapTable } from "../../codegen/tables/EntityMapTable.sol"; -import { EntityAssociationTable } from "../../codegen/tables/EntityAssociationTable.sol"; +import { EntityMap } from "../../codegen/tables/EntityMap.sol"; +import { EntityAssociation } from "../../codegen/tables/EntityAssociation.sol"; import { HookTable } from "../../codegen/tables/HookTable.sol"; import { ICustomErrorSystem } from "../../codegen/world/ICustomErrorSystem.sol"; -import { HookTargetBeforeTable } from "../../codegen/tables/HookTargetBeforeTable.sol"; -import { HookTargetAfterTable } from "../../codegen/tables/HookTargetAfterTable.sol"; +import { HookTargetBefore } from "../../codegen/tables/HookTargetBefore.sol"; +import { HookTargetAfter } from "../../codegen/tables/HookTargetAfter.sol"; import { EveSystem } from "../internal/EveSystem.sol"; import { HookType } from "../../types.sol"; +import { Utils } from "../../utils.sol"; + contract HookCore is EveSystem { + using Utils for bytes14; + /** * @notice Register the hook function to execute before or after a EVE function * @param systemId is the ResourceId of the system @@ -75,43 +79,43 @@ contract HookCore is EveSystem { function _registerHook(ResourceId systemId, bytes4 functionId) internal { uint256 hookId = uint256(keccak256(abi.encodePacked(systemId, functionId))); - if (HookTable.getIsHook(hookId)) + if (HookTable.getIsHook(_namespace().hookTableTableId(), hookId)) revert ICustomErrorSystem.HookAlreadyRegistered(hookId, "HookCore: Hook already registered"); - HookTable.set(hookId, true, systemId, functionId); + HookTable.set(_namespace().hookTableTableId(), hookId, true, systemId, functionId); } function _addHook(uint256 hookId, HookType hookType, ResourceId systemId, bytes4 functionSelector) internal { - if (!HookTable.getIsHook(hookId)) + if (!HookTable.getIsHook(_namespace().hookTableTableId(), hookId)) revert ICustomErrorSystem.HookNotRegistered(hookId, "HookCore: Hook not registered"); uint256 targetId = uint256(keccak256(abi.encodePacked(systemId, functionSelector))); if (hookType == HookType.BEFORE) { - HookTargetBeforeTable.set(hookId, targetId, true, systemId, functionSelector); + HookTargetBefore.set(_namespace().hookTargetBeforeTableId(), hookId, targetId, true, systemId, functionSelector); } else if (hookType == HookType.AFTER) { - HookTargetAfterTable.set(hookId, targetId, true, systemId, functionSelector); + HookTargetAfter.set(_namespace().hookTargetAfterTableId(), hookId, targetId, true, systemId, functionSelector); } } function _removeHook(uint256 hookId, HookType hookType, ResourceId systemId, bytes4 functionSelector) internal { - if (!HookTable.getIsHook(hookId)) + if (!HookTable.getIsHook(_namespace().hookTableTableId(), hookId)) revert ICustomErrorSystem.HookNotRegistered(hookId, "HookCore: Hook not registered"); uint256 targetId = uint256(keccak256(abi.encodePacked(systemId, functionSelector))); if (hookType == HookType.BEFORE) { - HookTargetBeforeTable.deleteRecord(hookId, targetId); + HookTargetBefore.deleteRecord(_namespace().hookTargetBeforeTableId(), hookId, targetId); } else if (hookType == HookType.AFTER) { - HookTargetAfterTable.deleteRecord(hookId, targetId); + HookTargetAfter.deleteRecord(_namespace().hookTargetAfterTableId(), hookId, targetId); } } function _associateHook(uint256 entityId, uint256 hookId) internal { _requireEntityRegistered(entityId); - if (!HookTable.getIsHook(hookId)) + if (!HookTable.getIsHook(_namespace().hookTableTableId(), hookId)) revert ICustomErrorSystem.HookNotRegistered(hookId, "HookCore: Hook not registered"); - if (EntityMapTable.get(entityId).length > 0) { - uint256[] memory taggedEntityIds = EntityMapTable.get(entityId); + if (EntityMap.get(_namespace().entityMapTableId(), entityId).length > 0) { + uint256[] memory taggedEntityIds = EntityMap.get(_namespace().entityMapTableId(), entityId); for (uint256 i = 0; i < taggedEntityIds.length; i++) { _requireHookeNotAssociated(taggedEntityIds[i], hookId); } @@ -119,11 +123,11 @@ contract HookCore is EveSystem { _requireHookeNotAssociated(entityId, hookId); } - EntityAssociationTable.pushHookIds(entityId, hookId); + EntityAssociation.pushHookIds(_namespace().entityAssociationTableId(), entityId, hookId); } function _requireHookeNotAssociated(uint256 entityId, uint256 hookId) internal view { - uint256[] memory hookIds = EntityAssociationTable.getHookIds(entityId); + uint256[] memory hookIds = EntityAssociation.getHookIds(_namespace().entityAssociationTableId(), entityId); (, bool exists) = findIndex(hookIds, hookId); if (exists) revert ICustomErrorSystem.EntityAlreadyAssociated( @@ -134,15 +138,15 @@ contract HookCore is EveSystem { } function _removeEntityHookAssociation(uint256 entityId, uint256 hookId) internal { - uint256[] memory hookIds = EntityAssociationTable.getHookIds(entityId); + uint256[] memory hookIds = EntityAssociation.getHookIds(_namespace().entityAssociationTableId(), entityId); (uint256 index, bool exists) = findIndex(hookIds, hookId); if (exists) { //Swap the last element to the index and pop the last element uint256 lastIndex = hookIds.length - 1; if (index != lastIndex) { - EntityAssociationTable.updateHookIds(entityId, index, hookIds[lastIndex]); + EntityAssociation.updateHookIds(_namespace().entityAssociationTableId(), entityId, index, hookIds[lastIndex]); } - EntityAssociationTable.popModuleIds(entityId); + EntityAssociation.popModuleIds(_namespace().entityAssociationTableId(), entityId); } } } diff --git a/core/smart-object-framework/src/systems/core/ModuleCore.sol b/core/smart-object-framework/src/systems/core/ModuleCore.sol index 86f1a2a5..d48dac1e 100644 --- a/core/smart-object-framework/src/systems/core/ModuleCore.sol +++ b/core/smart-object-framework/src/systems/core/ModuleCore.sol @@ -3,15 +3,18 @@ pragma solidity >=0.8.21; import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; -import { EntityAssociationTable } from "../../codegen/tables/EntityAssociationTable.sol"; -import { EntityMapTable } from "../../codegen/tables/EntityMapTable.sol"; +import { EntityAssociation } from "../../codegen/tables/EntityAssociation.sol"; +import { EntityMap } from "../../codegen/tables/EntityMap.sol"; import { ModuleTable } from "../../codegen/tables/ModuleTable.sol"; -import { ModuleSystemLookupTable } from "../../codegen/tables/ModuleSystemLookupTable.sol"; +import { ModuleSystemLookup } from "../../codegen/tables/ModuleSystemLookup.sol"; import { ICustomErrorSystem } from "../../codegen/world/ICustomErrorSystem.sol"; import { EveSystem } from "../internal/EveSystem.sol"; -import { INVALID_ID } from "../../constants.sol"; + +import { Utils } from "../../utils.sol"; contract ModuleCore is EveSystem { + using Utils for bytes14; + /** * @notice Registers a system * @param moduleId The identifier for the module @@ -26,7 +29,7 @@ contract ModuleCore is EveSystem { /** * @notice Overloaded funciton for registerEVEModule */ - function registerEVEModule(uint256 moduleId, bytes16 moduleName, ResourceId[] memory systemIds) external { + function registerEVEModules(uint256 moduleId, bytes16 moduleName, ResourceId[] memory systemIds) external { for (uint256 i = 0; i < systemIds.length; i++) { _requireResourceRegistered(moduleId, systemIds[i]); _registerEVEModule(moduleId, systemIds[i], moduleName); @@ -78,15 +81,15 @@ contract ModuleCore is EveSystem { } function _registerEVEModule(uint256 moduleId, ResourceId systemId, bytes16 moduleName) internal { - if (ModuleTable.getDoesExists(moduleId, systemId)) + if (ModuleTable.getDoesExists(_namespace().moduleTableTableId(), moduleId, systemId)) revert ICustomErrorSystem.SystemAlreadyAssociatedWithModule( moduleId, systemId, "ModuleCore: System already associated with the module" ); - ModuleTable.set(moduleId, systemId, moduleName, true); - ModuleSystemLookupTable.pushSystemIds(moduleId, ResourceId.unwrap(systemId)); + ModuleTable.set(_namespace().moduleTableTableId(), moduleId, systemId, moduleName, true); + ModuleSystemLookup.pushSystemIds(_namespace().moduleSystemLookupTableId(), moduleId, ResourceId.unwrap(systemId)); } function _associateModule(uint256 entityId, uint256 moduleId) internal { @@ -96,8 +99,8 @@ contract ModuleCore is EveSystem { //Check if the entity is tagged to a taggedEntityType, //if yes then the check module is already part of the taggedEntityType to ensure unique moduleId association //If no then associate the entity with the module - if (EntityMapTable.get(entityId).length > 0) { - uint256[] memory taggedEntityIds = EntityMapTable.get(entityId); + if (EntityMap.get(_namespace().entityMapTableId(), entityId).length > 0) { + uint256[] memory taggedEntityIds = EntityMap.get(_namespace().entityMapTableId(), entityId); for (uint256 i = 0; i < taggedEntityIds.length; i++) { _requireModuleNotAssociated(taggedEntityIds[i], moduleId); } @@ -105,11 +108,11 @@ contract ModuleCore is EveSystem { _requireModuleNotAssociated(entityId, moduleId); } - EntityAssociationTable.pushModuleIds(entityId, moduleId); + EntityAssociation.pushModuleIds(_namespace().entityAssociationTableId(), entityId, moduleId); } function _requireModuleNotAssociated(uint256 entityId, uint256 moduleId) internal view { - uint256[] memory moduleIds = EntityAssociationTable.getModuleIds(entityId); + uint256[] memory moduleIds = EntityAssociation.getModuleIds(_namespace().entityAssociationTableId(), entityId); (, bool exists) = findIndex(moduleIds, moduleId); if (exists) revert ICustomErrorSystem.EntityAlreadyAssociated( @@ -120,35 +123,48 @@ contract ModuleCore is EveSystem { } function _removeEntityModuleAssociation(uint256 entityId, uint256 moduleId) internal { - uint256[] memory moduleIds = EntityAssociationTable.getModuleIds(entityId); + uint256[] memory moduleIds = EntityAssociation.getModuleIds(_namespace().entityAssociationTableId(), entityId); (uint256 index, bool exists) = findIndex(moduleIds, moduleId); if (exists) { //Swap the last element to the index and pop the last element uint256 lastIndex = moduleIds.length - 1; if (index != lastIndex) { - EntityAssociationTable.updateModuleIds(entityId, index, moduleIds[lastIndex]); + EntityAssociation.updateModuleIds( + _namespace().entityAssociationTableId(), + entityId, + index, + moduleIds[lastIndex] + ); } - EntityAssociationTable.popModuleIds(entityId); + EntityAssociation.popModuleIds(_namespace().entityAssociationTableId(), entityId); } } function _removeSystemModuleAssociation(ResourceId systemId, uint256 moduleId) internal { bytes32 unwrappedSystemId = ResourceId.unwrap(systemId); - require(ModuleTable.getDoesExists(moduleId, systemId), "ModuleCore: Module not registered"); - ModuleTable.deleteRecord(moduleId, systemId); + require( + ModuleTable.getDoesExists(_namespace().moduleTableTableId(), moduleId, systemId), + "ModuleCore: Module not registered" + ); + ModuleTable.deleteRecord(_namespace().moduleTableTableId(), moduleId, systemId); //update lookup table //TODO remove this after discussion - bytes32[] memory systemIds = ModuleSystemLookupTable.getSystemIds(moduleId); + bytes32[] memory systemIds = ModuleSystemLookup.getSystemIds(_namespace().moduleSystemLookupTableId(), moduleId); (uint256 index, bool exists) = findIndex(systemIds, unwrappedSystemId); if (exists) { //Swap the last element to the index and pop the last element uint256 lastIndex = systemIds.length - 1; if (index != lastIndex) { - ModuleSystemLookupTable.updateSystemIds(moduleId, index, unwrappedSystemId); + ModuleSystemLookup.updateSystemIds( + _namespace().moduleSystemLookupTableId(), + moduleId, + index, + unwrappedSystemId + ); } - ModuleSystemLookupTable.popSystemIds(moduleId); + ModuleSystemLookup.popSystemIds(_namespace().moduleSystemLookupTableId(), moduleId); } } } diff --git a/core/smart-object-framework/src/systems/internal/EveSystem.sol b/core/smart-object-framework/src/systems/internal/EveSystem.sol index 9b1ccf37..4d37342d 100644 --- a/core/smart-object-framework/src/systems/internal/EveSystem.sol +++ b/core/smart-object-framework/src/systems/internal/EveSystem.sol @@ -1,26 +1,40 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; +import { SystemRegistry } from "@latticexyz/world/src/codegen/tables/SystemRegistry.sol"; import { System } from "@latticexyz/world/src/System.sol"; import { FunctionSelectors } from "@latticexyz/world/src/codegen/tables/FunctionSelectors.sol"; import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; +import { WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; + import { IWorld } from "../../codegen/world/IWorld.sol"; import { EntityTable } from "../../codegen/tables/EntityTable.sol"; import { ModuleTable } from "../../codegen/tables/ModuleTable.sol"; -import { EntityMapTable } from "../../codegen/tables/EntityMapTable.sol"; -import { EntityAssociationTable } from "../../codegen/tables/EntityAssociationTable.sol"; -import { HookTargetBeforeTable } from "../../codegen/tables/HookTargetBeforeTable.sol"; -import { HookTargetAfterTable } from "../../codegen/tables/HookTargetAfterTable.sol"; -import { ModuleSystemLookupTable } from "../../codegen/tables/ModuleSystemLookupTable.sol"; +import { EntityMap } from "../../codegen/tables/EntityMap.sol"; +import { EntityAssociation } from "../../codegen/tables/EntityAssociation.sol"; +import { HookTargetBefore } from "../../codegen/tables/HookTargetBefore.sol"; +import { HookTargetAfter } from "../../codegen/tables/HookTargetAfter.sol"; +import { ModuleSystemLookup } from "../../codegen/tables/ModuleSystemLookup.sol"; import { HookTable } from "../../codegen/tables/HookTable.sol"; import { ICustomErrorSystem } from "../../codegen/world//ICustomErrorSystem.sol"; import { HookTableData } from "../../codegen/tables/HookTable.sol"; +import { Utils } from "../../utils.sol"; +import { SMART_OBJECT_DEPLOYMENT_NAMESPACE as CORE_NAMESPACE } from "../../constants.sol"; + /** * @title EveSystem * @notice This is the base system which has all the helper functions for the other systems to inherit + * + * TODO: the references to `CORE_NAMESPACE` are kind of an anti-pattern because the whole point is to not rely on hard-coded values + * Once that contract is inherited to non-core Systems, there's currently no satisfying way to dynamically retrieve that namespace + * ideally, this would be some global constant set at Core Systems' deployment, + * Or, contracts inheriting from EveSystem would need to explicitely target a deployed Core Systems' namespace */ contract EveSystem is System { + using WorldResourceIdInstance for ResourceId; + using Utils for bytes14; + /** * @notice Executes the function only if the entity is associated with the module * @dev Module association is defined by the systems registered in the ModuleTable @@ -62,13 +76,13 @@ contract EveSystem is System { * @param entityId is the id of an object or class */ function _requireEntityRegistered(uint256 entityId) internal view { - if (!EntityTable.getDoesExists(entityId)) + if (!EntityTable.getDoesExists(CORE_NAMESPACE.entityTableTableId(), entityId)) revert ICustomErrorSystem.EntityNotRegistered(entityId, "EveSystem: Entity is not registered"); } function _requireModuleRegistered(uint256 moduleId) internal view { //check if the module is registered - if (ModuleSystemLookupTable.getSystemIds(moduleId).length == 0) + if (ModuleSystemLookup.getSystemIds(CORE_NAMESPACE.moduleSystemLookupTableId(), moduleId).length == 0) revert ICustomErrorSystem.ModuleNotRegistered(moduleId, "EveSystem: Module not registered"); } @@ -82,9 +96,9 @@ contract EveSystem is System { uint256[] memory moduleIds = _getModuleIds(entityId); //Check if the entity is tagged to a entityType and get the moduleIds for the entity - bool isEntityTagged = EntityMapTable.get(entityId).length > 0; + bool isEntityTagged = EntityMap.get(CORE_NAMESPACE.entityMapTableId(), entityId).length > 0; if (isEntityTagged) { - uint256[] memory taggedEntityIds = EntityMapTable.get(entityId); + uint256[] memory taggedEntityIds = EntityMap.get(CORE_NAMESPACE.entityMapTableId(), entityId); for (uint256 i = 0; i < taggedEntityIds.length; i++) { uint256[] memory taggedModuleIds = _getModuleIds(taggedEntityIds[i]); moduleIds = appendUint256Arrays(moduleIds, taggedModuleIds); @@ -101,7 +115,7 @@ contract EveSystem is System { } function _getModuleIds(uint256 entityId) internal view returns (uint256[] memory) { - return EntityAssociationTable.getModuleIds(entityId); + return EntityAssociation.getModuleIds(CORE_NAMESPACE.entityAssociationTableId(), entityId); } function _validateModules(uint256[] memory moduleIds, ResourceId systemId, bytes4 functionSelector) internal view { @@ -110,7 +124,7 @@ contract EveSystem is System { //TODO Below logic can be optimized by using supportsInterface as well for (uint256 i = 0; i < moduleIds.length; i++) { - bool systemExists = ModuleTable.getDoesExists(moduleIds[i], systemId); + bool systemExists = ModuleTable.getDoesExists(CORE_NAMESPACE.moduleTableTableId(), moduleIds[i], systemId); if (systemExists) { isModuleFound = true; bytes32 registeredSystemId = ResourceId.unwrap(FunctionSelectors.getSystemId(functionSelector)); @@ -128,14 +142,17 @@ contract EveSystem is System { } function _getHookIds(uint256 entityId) internal view returns (uint256[] memory hookIds) { - hookIds = EntityAssociationTable.getHookIds(entityId); + hookIds = EntityAssociation.getHookIds(CORE_NAMESPACE.entityAssociationTableId(), entityId); //Check if the entity is tagged to a entity and get the moduleIds for the taggedEntity - bool isEntityTagged = EntityMapTable.get(entityId).length > 0; + bool isEntityTagged = EntityMap.get(CORE_NAMESPACE.entityMapTableId(), entityId).length > 0; if (isEntityTagged) { - uint256[] memory entityTagIds = EntityMapTable.get(entityId); + uint256[] memory entityTagIds = EntityMap.get(CORE_NAMESPACE.entityMapTableId(), entityId); for (uint256 i = 0; i < entityTagIds.length; i++) { - uint256[] memory taggedHookIds = EntityAssociationTable.getHookIds(entityTagIds[i]); + uint256[] memory taggedHookIds = EntityAssociation.getHookIds( + CORE_NAMESPACE.entityAssociationTableId(), + entityTagIds[i] + ); hookIds = appendUint256Arrays(hookIds, taggedHookIds); } } @@ -148,7 +165,7 @@ contract EveSystem is System { bytes memory hookArgs ) internal { uint256 targetId = uint256(keccak256(abi.encodePacked(systemId, functionSelector))); - bool hasHook = HookTargetBeforeTable.getHasHook(hookId, targetId); + bool hasHook = HookTargetBefore.getHasHook(CORE_NAMESPACE.hookTargetBeforeTableId(), hookId, targetId); if (hasHook) { _executeHook(hookId, hookArgs); } @@ -161,14 +178,14 @@ contract EveSystem is System { bytes memory hookArgs ) internal { uint256 targetId = uint256(keccak256(abi.encodePacked(systemId, functionSelector))); - bool hasHook = HookTargetAfterTable.getHasHook(hookId, targetId); + bool hasHook = HookTargetAfter.getHasHook(CORE_NAMESPACE.hookTargetAfterTableId(), hookId, targetId); if (hasHook) { _executeHook(hookId, hookArgs); } } function _executeHook(uint256 hookId, bytes memory hookArgs) internal { - HookTableData memory hookData = HookTable.get(hookId); + HookTableData memory hookData = HookTable.get(CORE_NAMESPACE.hookTableTableId(), hookId); bytes memory funcSelectorAndArgs = abi.encodePacked(hookData.functionSelector, hookArgs); ResourceId systemId = hookData.systemId; //TODO replace with callFrom ? and get the delegator address from the hookrgs ? @@ -227,4 +244,9 @@ contract EveSystem is System { return newArray; } + + function _namespace() internal view returns (bytes14 namespace) { + ResourceId systemId = SystemRegistry.get(address(this)); + return systemId.getNamespace(); + } } diff --git a/core/smart-object-framework/src/utils.sol b/core/smart-object-framework/src/utils.sol index 3c86502c..671a8d25 100644 --- a/core/smart-object-framework/src/utils.sol +++ b/core/smart-object-framework/src/utils.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; -import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; +import { RESOURCE_SYSTEM, RESOURCE_TABLE } from "@latticexyz/world/src/worldResourceTypes.sol"; + +import "./constants.sol"; library Utils { using WorldResourceIdInstance for ResourceId; @@ -9,4 +11,57 @@ library Utils { function getSystemId(bytes14 namespace, bytes16 name) internal pure returns (ResourceId) { return WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: name }); } + + function entityCoreSystemId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: ENTITY_CORE_SYSTEM_NAME }); + } + + function moduleCoreSystemId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: MODULE_CORE_SYSTEM_NAME }); + } + + function hookCoreSystemId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: HOOK_CORE_SYSTEM_NAME }); + } + + function entityTypeTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: ENTITY_TYPE_NAME }); + } + + function entityTableTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: ENTITY_TABLE_NAME }); + } + + function entityTypeAssociationTableId(bytes14 namespace) internal pure returns (ResourceId) { + return + WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: ENTITY_TYPE_ASSOCIATION_NAME }); + } + + function entityMapTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: ENTITY_MAP_NAME }); + } + + function entityAssociationTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: ENTITY_ASSOCIATION_NAME }); + } + + function moduleTableTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: MODULE_TABLE_NAME }); + } + + function moduleSystemLookupTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: MODULE_SYSTEM_LOOKUP_NAME }); + } + + function hookTableTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: HOOK_TABLE_NAME }); + } + + function hookTargetBeforeTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: HOOK_TARGET_BEFORE_NAME }); + } + + function hookTargetAfterTableId(bytes14 namespace) internal pure returns (ResourceId) { + return WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: HOOK_TARGET_AFTER_NAME }); + } } diff --git a/core/smart-object-framework/test/EveSystemTest.t.sol b/core/smart-object-framework/test/EveSystemTest.t.sol index 8bee426c..17c95d92 100644 --- a/core/smart-object-framework/test/EveSystemTest.t.sol +++ b/core/smart-object-framework/test/EveSystemTest.t.sol @@ -9,19 +9,23 @@ import { InstalledModules } from "@latticexyz/world/src/codegen/tables/Installed import { Module } from "@latticexyz/world/src/Module.sol"; import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; import { System } from "@latticexyz/world/src/System.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FunctionSelectors } from "@latticexyz/world/src/codegen/tables/FunctionSelectors.sol"; import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; -import { IEntityCore } from "../src/codegen/world/IEntityCore.sol"; import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { World } from "@latticexyz/world/src/World.sol"; import { ICustomErrorSystem } from "../src/codegen/world/ICustomErrorSystem.sol"; import { EntityTable } from "../src/codegen/tables/EntityTable.sol"; -import { EntityMapTable } from "../src/codegen/tables/EntityMapTable.sol"; +import { EntityMap } from "../src/codegen/tables/EntityMap.sol"; import { ModuleTable } from "../src/codegen/tables/ModuleTable.sol"; import { HookTable } from "../src/codegen/tables/HookTable.sol"; import { EveSystem } from "../src/systems/internal/EveSystem.sol"; import { MODULE_NAME, TABLE_ID, SYSTEM_ID, NAMESPACE, NAMESPACE_ID, SYSTEM_NAME, HOOK_SYSTEM_ID, HOOK_SYSTEM_NAME, OBJECT, CLASS } from "./constants.sol"; import { HookType } from "../src/types.sol"; import { Utils } from "../src/utils.sol"; +import { SmartObjectFrameworkModule } from "../src/SmartObjectFrameworkModule.sol"; +import { SmartObjectLib } from "../src/SmartObjectLib.sol"; +import { createCoreModule } from "./createCoreModule.sol"; interface ISmartDeployableTestSystem { function echoSmartDeployable(uint256 _value) external view returns (uint256); @@ -61,32 +65,29 @@ contract SmartDeployableTestModule is Module { function installRoot(bytes memory args) public { // Naive check to ensure this is only installed once requireNotInstalled(__self, args); + IBaseWorld world = IBaseWorld(_world()); //Register namespace - (bool success, bytes memory data) = address(world).delegatecall( - abi.encodeCall(world.registerNamespace, (NAMESPACE_ID)) - ); + (bool success, bytes memory data) = address(world).call(abi.encodeCall(world.registerNamespace, (NAMESPACE_ID))); if (!success) revertWithBytes(data); // Register system - (success, data) = address(world).delegatecall( + (success, data) = address(world).call( abi.encodeCall(world.registerSystem, (SYSTEM_ID, smartDeployableTestSystem, true)) ); if (!success) revertWithBytes(data); - (success, data) = address(world).delegatecall( - abi.encodeCall(world.registerSystem, (HOOK_SYSTEM_ID, sampleHook, true)) - ); + (success, data) = address(world).call(abi.encodeCall(world.registerSystem, (HOOK_SYSTEM_ID, sampleHook, true))); if (!success) revertWithBytes(data); // Register system's functions - (success, data) = address(world).delegatecall( + (success, data) = address(world).call( abi.encodeCall(world.registerFunctionSelector, (HOOK_SYSTEM_ID, "echoSmartDeployabl(uint256)")) ); if (!success) revertWithBytes(data); - (success, data) = address(world).delegatecall( + (success, data) = address(world).call( abi.encodeCall(world.registerFunctionSelector, (HOOK_SYSTEM_ID, "echoSmartDeployableHook(uint256)")) ); if (!success) revertWithBytes(data); @@ -111,20 +112,46 @@ contract SmartDeployableTestModule is Module { } } -contract EveSystemTest is MudTest { +contract EveSystemTest is Test { + using Utils for bytes14; + using SmartObjectLib for SmartObjectLib.World; + uint256 classId1 = uint256(keccak256(abi.encodePacked("typeId12"))); uint256 classId2 = uint256(keccak256(abi.encodePacked("typeId13"))); uint256 singletonEntity = uint256(keccak256(abi.encode("item:--2345"))); uint256 singletonObject1 = uint256(keccak256(abi.encode("item:--12345"))); uint256 singletonObject2 = uint256(keccak256(abi.encode("item:--2345"))); uint256 singletonObject3 = uint256(keccak256(abi.encode("item:--345"))); - uint256 moduleId = uint256(keccak256(abi.encodePacked(address(smartDeployableTestModule)))); + uint256 moduleId; + + SmartDeployableTestModule smartDeployableTestModule; - SmartDeployableTestModule smartDeployableTestModule = new SmartDeployableTestModule(); + bytes14 constant SMART_OBJ_NAMESPACE = "SmartObject_v0"; + + IBaseWorld baseWorld; + SmartObjectLib.World smartObject; + SmartObjectFrameworkModule smartObjectModule; + + function setUp() public { + baseWorld = IBaseWorld(address(new World())); + baseWorld.initialize(createCoreModule()); + SmartObjectFrameworkModule module = new SmartObjectFrameworkModule(); + baseWorld.installModule(module, abi.encode(SMART_OBJ_NAMESPACE)); + StoreSwitch.setStoreAddress(address(baseWorld)); + smartObject = SmartObjectLib.World(baseWorld, SMART_OBJ_NAMESPACE); + + smartDeployableTestModule = new SmartDeployableTestModule(); + moduleId = uint256(keccak256(abi.encodePacked(address(smartDeployableTestModule)))); + } + + function testSetup() public { + setUp(); + assertEq(address(smartObject.iface), address(baseWorld)); + } function testWorldExists() public { uint256 codeSize; - address addr = worldAddress; + address addr = address(baseWorld); assembly { codeSize := extcodesize(addr) } @@ -132,7 +159,7 @@ contract EveSystemTest is MudTest { } function testInstallModule() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); world.installModule(smartDeployableTestModule, new bytes(0)); // Check that the module is installed @@ -140,14 +167,12 @@ contract EveSystemTest is MudTest { } function testRegisterEntity() public { - IWorld world = IWorld(worldAddress); - world.registerEntityType(CLASS, "Class"); - world.registerEntity(1, CLASS); - assertTrue(EntityTable.getEntityType(1) == CLASS); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntity(1, CLASS); + assertTrue(EntityTable.getEntityType(SMART_OBJ_NAMESPACE.entityTableTableId(), 1) == CLASS); } function testRevertEntityTypeNotRegistered() public { - IWorld world = IWorld(worldAddress); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.EntityTypeNotRegistered.selector, @@ -155,11 +180,11 @@ contract EveSystemTest is MudTest { "EntityCore: EntityType not registered" ) ); - world.registerEntity(1, CLASS); + smartObject.registerEntity(1, CLASS); } function testRevertIfEntityNotRegistered() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); world.installModule(smartDeployableTestModule, new bytes(0)); vm.expectRevert( @@ -169,28 +194,24 @@ contract EveSystemTest is MudTest { } function testTagEntity() public { - IWorld world = IWorld(worldAddress); - //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntityType(OBJECT, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.registerEntity(classId1, CLASS); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(singletonObject1, OBJECT); //Tag objects under a class - world.tagEntity(singletonObject1, classId1); - uint256[] memory entityTagIds = EntityMapTable.get(singletonObject1); + smartObject.tagEntity(singletonObject1, classId1); + uint256[] memory entityTagIds = EntityMap.get(SMART_OBJ_NAMESPACE.entityMapTableId(), singletonObject1); assertTrue(entityTagIds[0] == classId1); } function testTagMultipleEntities() public { - IWorld world = IWorld(worldAddress); - //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntityType(OBJECT, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); uint256[] memory entityIds = new uint256[](2); uint8[] memory entityTypes = new uint8[](2); @@ -198,29 +219,27 @@ contract EveSystemTest is MudTest { entityIds[1] = classId2; entityTypes[0] = CLASS; entityTypes[1] = CLASS; - world.registerEntity(entityIds, entityTypes); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntities(entityIds, entityTypes); + smartObject.registerEntity(singletonObject1, OBJECT); //Tag objects under a class - world.tagEntity(singletonObject1, entityIds); + smartObject.tagEntities(singletonObject1, entityIds); - uint256[] memory entityTagIds = EntityMapTable.get(singletonObject1); + uint256[] memory entityTagIds = EntityMap.get(SMART_OBJ_NAMESPACE.entityMapTableId(), singletonObject1); assertTrue(entityTagIds[0] == classId1); assertTrue(entityTagIds[1] == classId2); } function testRevertAlreadyTagged() public { - IWorld world = IWorld(worldAddress); - //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntityType(OBJECT, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.registerEntity(classId1, CLASS); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(singletonObject1, OBJECT); //Tag objects under a class - world.tagEntity(singletonObject1, classId1); + smartObject.tagEntity(singletonObject1, classId1); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.EntityAlreadyTagged.selector, @@ -229,18 +248,16 @@ contract EveSystemTest is MudTest { "EntityCore: Entity already tagged" ) ); - world.tagEntity(singletonObject1, classId1); + smartObject.tagEntity(singletonObject1, classId1); } function testRevertIfTaggingNotAllowed() public { - IWorld world = IWorld(worldAddress); - //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntityType(OBJECT, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.registerEntity(classId1, CLASS); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(singletonObject1, OBJECT); vm.expectRevert( abi.encodeWithSelector( @@ -250,26 +267,26 @@ contract EveSystemTest is MudTest { "EntityCore: EntityType association not allowed" ) ); - world.tagEntity(classId1, singletonObject1); + smartObject.tagEntity(classId1, singletonObject1); } function testregisterEVEModule() public { - IWorld world = IWorld(worldAddress); - + IWorld world = IWorld(address(baseWorld)); + world.installModule(smartDeployableTestModule, new bytes(0)); //register module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); - assertTrue(ModuleTable.getDoesExists(moduleId, SYSTEM_ID)); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + assertTrue(ModuleTable.getDoesExists(SMART_OBJ_NAMESPACE.moduleTableTableId(), moduleId, SYSTEM_ID)); } function testRevertregisterEVEModuleIfSystemAlreadyRegistered() public { - IWorld world = IWorld(worldAddress); - + IWorld world = IWorld(address(baseWorld)); + world.installModule(smartDeployableTestModule, new bytes(0)); //register module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.SystemAlreadyAssociatedWithModule.selector, @@ -278,26 +295,26 @@ contract EveSystemTest is MudTest { "ModuleCore: System already associated with the module" ) ); - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); } function testObjectAssociate() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonObject1, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonObject1, moduleId); + smartObject.associateModule(singletonObject1, moduleId); - ModuleTable.getDoesExists(moduleId, SYSTEM_ID); + ModuleTable.getDoesExists(SMART_OBJ_NAMESPACE.moduleTableTableId(), moduleId, SYSTEM_ID); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (singletonObject1))), (uint256) @@ -306,23 +323,23 @@ contract EveSystemTest is MudTest { } function testClassAssociate() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); uint256 nonSingletonEntity = uint256(keccak256(abi.encode("item:72"))); //install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntity(nonSingletonEntity, CLASS); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntity(nonSingletonEntity, CLASS); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(nonSingletonEntity, moduleId); + smartObject.associateModule(nonSingletonEntity, moduleId); - ModuleTable.getDoesExists(moduleId, SYSTEM_ID); + ModuleTable.getDoesExists(SMART_OBJ_NAMESPACE.moduleTableTableId(), moduleId, SYSTEM_ID); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (nonSingletonEntity))), (uint256) @@ -331,14 +348,14 @@ contract EveSystemTest is MudTest { } function testRevertIfNotAssociated() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(CLASS, "Class"); - world.registerEntity(classId1, CLASS); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntity(classId1, CLASS); vm.expectRevert( abi.encodeWithSelector( @@ -351,36 +368,36 @@ contract EveSystemTest is MudTest { } function testObjectAssociateWithClass() public { - IWorld world = IWorld(worldAddress); - + IWorld world = IWorld(address(baseWorld)); + //install module world.installModule(smartDeployableTestModule, new bytes(0)); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //register entityType - world.registerEntityType(CLASS, "Class"); - world.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityType(CLASS, "Class"); + smartObject.registerEntityType(OBJECT, "Object"); //Allow tagging of entities - world.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); //register entityType - world.registerEntity(classId1, CLASS); - world.registerEntity(singletonObject1, OBJECT); - world.registerEntity(singletonObject2, OBJECT); - world.registerEntity(singletonObject3, OBJECT); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntity(singletonObject2, OBJECT); + smartObject.registerEntity(singletonObject3, OBJECT); //Tag objects under a class - world.tagEntity(singletonObject1, classId1); - world.tagEntity(singletonObject2, classId1); - world.tagEntity(singletonObject3, classId1); + smartObject.tagEntity(singletonObject1, classId1); + smartObject.tagEntity(singletonObject2, classId1); + smartObject.tagEntity(singletonObject3, classId1); //associate entity with module - world.associateModule(classId1, moduleId); + smartObject.associateModule(classId1, moduleId); - ModuleTable.getDoesExists(moduleId, SYSTEM_ID); + ModuleTable.getDoesExists(SMART_OBJ_NAMESPACE.moduleTableTableId(), moduleId, SYSTEM_ID); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (singletonObject1))), (uint256) @@ -394,24 +411,24 @@ contract EveSystemTest is MudTest { } function testRemoveEntityTag() public { - IWorld world = IWorld(worldAddress); - + IWorld world = IWorld(address(baseWorld)); + //install module world.installModule(smartDeployableTestModule, new bytes(0)); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //register entity - world.registerEntityType(1, "Class"); - world.registerEntityType(2, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.registerEntity(classId1, CLASS); - world.registerEntity(singletonObject1, OBJECT); - world.tagEntity(singletonObject1, classId1); + smartObject.registerEntityType(1, "Class"); + smartObject.registerEntityType(2, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(singletonObject1, OBJECT); + smartObject.tagEntity(singletonObject1, classId1); //associate entity with module - world.associateModule(classId1, moduleId); + smartObject.associateModule(classId1, moduleId); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (singletonObject1))), @@ -419,7 +436,7 @@ contract EveSystemTest is MudTest { ); assertTrue(value == singletonObject1); - world.removeEntityTag(singletonObject1, classId1); + smartObject.removeEntityTag(singletonObject1, classId1); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.EntityNotAssociatedWithModule.selector, @@ -431,20 +448,20 @@ contract EveSystemTest is MudTest { } function testRevertIfEntityAlreadyAssociated() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); vm.expectRevert( abi.encodeWithSelector( @@ -454,31 +471,31 @@ contract EveSystemTest is MudTest { "ModuleCore: Module already associated with the entity" ) ); - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); } function testRevertIfTaggedEntityIsAlreadyAssociated() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //register entity - world.registerEntityType(1, "Class"); - world.registerEntityType(2, "Object"); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.registerEntity(classId1, CLASS); - world.registerEntity(classId2, CLASS); + smartObject.registerEntityType(1, "Class"); + smartObject.registerEntityType(2, "Object"); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.registerEntity(classId1, CLASS); + smartObject.registerEntity(classId2, CLASS); - world.registerEntity(singletonObject1, OBJECT); - world.tagEntity(singletonObject1, classId1); - world.tagEntity(singletonObject1, classId2); + smartObject.registerEntity(singletonObject1, OBJECT); + smartObject.tagEntity(singletonObject1, classId1); + smartObject.tagEntity(singletonObject1, classId2); //associate entity with module - world.associateModule(classId1, moduleId); + smartObject.associateModule(classId1, moduleId); vm.expectRevert( abi.encodeWithSelector( @@ -488,17 +505,17 @@ contract EveSystemTest is MudTest { "ModuleCore: Module already associated with the entity" ) ); - world.associateModule(singletonObject1, moduleId); + smartObject.associateModule(singletonObject1, moduleId); } function testRevertIfModuleNotRegistered() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); - world.registerEntityType(2, "Object"); - world.registerEntity(singletonEntity, CLASS); + smartObject.registerEntityType(2, "Object"); + smartObject.registerEntity(singletonEntity, CLASS); vm.expectRevert( abi.encodeWithSelector( @@ -507,16 +524,16 @@ contract EveSystemTest is MudTest { "EveSystem: Module not registered" ) ); - world.associateModule(singletonEntity, singletonEntity); + smartObject.associateModule(singletonEntity, singletonEntity); } //TODO commenting until we resolve data corruption issue // function testRemoveEntity() public { - // IWorld world = IWorld(worldAddress); + // IWorld world = IWorld(address(baseWorld)); // uint256 singletonObject1 = uint256(keccak256(abi.encode("item:--12345"))); // //install module - // + // // world.installModule(smartDeployableTestModule, new bytes(0)); // // register system associated with module @@ -548,20 +565,20 @@ contract EveSystemTest is MudTest { // } function testRemoveEntityModuleAssociation() public { - IWorld world = IWorld(worldAddress); - + IWorld world = IWorld(address(baseWorld)); + //install module world.installModule(smartDeployableTestModule, new bytes(0)); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonObject1, OBJECT); //associate entity with module - world.associateModule(singletonObject1, moduleId); + smartObject.associateModule(singletonObject1, moduleId); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (singletonObject1))), @@ -570,7 +587,7 @@ contract EveSystemTest is MudTest { assertTrue(value == singletonObject1); //Remove module and check if it reverts - world.removeEntityModuleAssociation(singletonObject1, moduleId); + smartObject.removeEntityModuleAssociation(singletonObject1, moduleId); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.EntityNotAssociatedWithModule.selector, @@ -582,20 +599,20 @@ contract EveSystemTest is MudTest { } function testRemoveSystem() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); //install module world.installModule(smartDeployableTestModule, new bytes(0)); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonObject1, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonObject1, OBJECT); //associate entity with module - world.associateModule(singletonObject1, moduleId); + smartObject.associateModule(singletonObject1, moduleId); uint256 value = abi.decode( world.call(SYSTEM_ID, abi.encodeCall(SmartDeployableTestSystem.echoSmartDeployable, (singletonObject1))), @@ -604,7 +621,7 @@ contract EveSystemTest is MudTest { assertTrue(value == singletonObject1); //Remove module and check if it reverts - world.removeSystemModuleAssociation(SYSTEM_ID, moduleId); + smartObject.removeSystemModuleAssociation(SYSTEM_ID, moduleId); vm.expectRevert( abi.encodeWithSelector( ICustomErrorSystem.ModuleNotFound.selector, @@ -615,33 +632,33 @@ contract EveSystemTest is MudTest { } function testHook() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); // install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); //Hook bytes4 functionId = bytes4(keccak256(abi.encodePacked("echoSmartDeployableHook(uint256)"))); - world.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); + smartObject.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); uint256 hookId = uint256(keccak256(abi.encodePacked(ResourceId.unwrap(HOOK_SYSTEM_ID), functionId))); - assertTrue(HookTable.getIsHook(hookId)); + assertTrue(HookTable.getIsHook(SMART_OBJ_NAMESPACE.hookTableTableId(), hookId)); //asscoaite hook with a entity - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); //add the hook to be executed before/after a function - world.addHook( + smartObject.addHook( hookId, HookType.BEFORE, SYSTEM_ID, @@ -656,21 +673,21 @@ contract EveSystemTest is MudTest { } function testRevertHookAssociationIfHookNotRegistered() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); // install module - + world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); //Hook bytes4 functionId = bytes4(keccak256(abi.encodePacked("echoSmartDeployableHook(uint256)"))); @@ -679,38 +696,38 @@ contract EveSystemTest is MudTest { vm.expectRevert( abi.encodeWithSelector(ICustomErrorSystem.HookNotRegistered.selector, hookId, "HookCore: Hook not registered") ); - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); } function testRevertDuplicateHookAssociation() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); // install module - + world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); //Hook bytes4 functionId = bytes4(keccak256(abi.encodePacked("echoSmartDeployableHook(uint256)"))); - world.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); + smartObject.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); uint256 hookId = uint256(keccak256(abi.encodePacked(ResourceId.unwrap(HOOK_SYSTEM_ID), functionId))); - assertTrue(HookTable.getIsHook(hookId)); + assertTrue(HookTable.getIsHook(SMART_OBJ_NAMESPACE.hookTableTableId(), hookId)); //asscoaite hook with a entity - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); //add the hook to be executed before/after a function - world.addHook( + smartObject.addHook( hookId, HookType.BEFORE, SYSTEM_ID, @@ -731,43 +748,43 @@ contract EveSystemTest is MudTest { "HookCore: Hook already associated with the entity" ) ); - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); } function testRevertIfTaggedEntityHasHookAssociated() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); // install module - + world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntityType(CLASS, "Object"); - world.registerEntity(singletonEntity, OBJECT); - world.registerEntity(classId1, CLASS); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntityType(CLASS, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntity(classId1, CLASS); - world.registerEntityTypeAssociation(OBJECT, CLASS); - world.tagEntity(singletonEntity, classId1); + smartObject.registerEntityTypeAssociation(OBJECT, CLASS); + smartObject.tagEntity(singletonEntity, classId1); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(classId1, moduleId); + smartObject.associateModule(classId1, moduleId); //Hook bytes4 functionId = bytes4(keccak256(abi.encodePacked("echoSmartDeployableHook(uint256)"))); - world.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); + smartObject.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); uint256 hookId = uint256(keccak256(abi.encodePacked(ResourceId.unwrap(HOOK_SYSTEM_ID), functionId))); - assertTrue(HookTable.getIsHook(hookId)); + assertTrue(HookTable.getIsHook(SMART_OBJ_NAMESPACE.hookTableTableId(), hookId)); //asscoaite hook with a entity - world.associateHook(classId1, hookId); + smartObject.associateHook(classId1, hookId); //add the hook to be executed before/after a function - world.addHook( + smartObject.addHook( hookId, HookType.BEFORE, SYSTEM_ID, @@ -788,37 +805,37 @@ contract EveSystemTest is MudTest { "HookCore: Hook already associated with the entity" ) ); - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); } function testRemoveHook() public { - IWorld world = IWorld(worldAddress); + IWorld world = IWorld(address(baseWorld)); // install module world.installModule(smartDeployableTestModule, new bytes(0)); //register entity - world.registerEntityType(OBJECT, "Object"); - world.registerEntity(singletonEntity, OBJECT); + smartObject.registerEntityType(OBJECT, "Object"); + smartObject.registerEntity(singletonEntity, OBJECT); // register system associated with module - world.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); + smartObject.registerEVEModule(moduleId, MODULE_NAME, SYSTEM_ID); //associate entity with module - world.associateModule(singletonEntity, moduleId); + smartObject.associateModule(singletonEntity, moduleId); //Hook bytes4 functionId = bytes4(keccak256(abi.encodePacked("echoSmartDeployableHook(uint256)"))); - world.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); + smartObject.registerHook(Utils.getSystemId(NAMESPACE, HOOK_SYSTEM_NAME), functionId); uint256 hookId = uint256(keccak256(abi.encodePacked(ResourceId.unwrap(HOOK_SYSTEM_ID), functionId))); - assertTrue(HookTable.getIsHook(hookId)); + assertTrue(HookTable.getIsHook(SMART_OBJ_NAMESPACE.hookTableTableId(), hookId)); //asscoaite hook with a entity - world.associateHook(singletonEntity, hookId); + smartObject.associateHook(singletonEntity, hookId); //add the hook to be executed before/after a function - world.addHook( + smartObject.addHook( hookId, HookType.BEFORE, SYSTEM_ID, @@ -826,7 +843,7 @@ contract EveSystemTest is MudTest { ); //After remove hook there is no console.log of hook execution - world.removeHook( + smartObject.removeHook( hookId, HookType.BEFORE, SYSTEM_ID, diff --git a/core/smart-object-framework/test/createCoreModule.sol b/core/smart-object-framework/test/createCoreModule.sol new file mode 100644 index 00000000..4eb57b7b --- /dev/null +++ b/core/smart-object-framework/test/createCoreModule.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// cherry-picked from staged changes for next-17 release, unavailable in .16 +pragma solidity >=0.8.21; + +import { AccessManagementSystem } from "@latticexyz/world/src/modules/core/implementations/AccessManagementSystem.sol"; +import { BalanceTransferSystem } from "@latticexyz/world/src/modules/core/implementations/BalanceTransferSystem.sol"; +import { BatchCallSystem } from "@latticexyz/world/src/modules/core/implementations/BatchCallSystem.sol"; + +import { CoreModule } from "@latticexyz/world/src/modules/core/CoreModule.sol"; +import { CoreRegistrationSystem } from "@latticexyz/world/src/modules/core/CoreRegistrationSystem.sol"; + +function createCoreModule() returns (CoreModule) { + return + new CoreModule( + new AccessManagementSystem(), + new BalanceTransferSystem(), + new BatchCallSystem(), + new CoreRegistrationSystem() + ); +}