diff --git a/packages/world/src/IWorldErrors.sol b/packages/world/src/IWorldErrors.sol index 29aed90045..7308db3d23 100644 --- a/packages/world/src/IWorldErrors.sol +++ b/packages/world/src/IWorldErrors.sol @@ -42,6 +42,12 @@ interface IWorldErrors { */ error World_InvalidResourceId(ResourceId resourceId, string resourceIdString); + /** + * @notice Raised when an namespace contains an invalid sequence of characters ("__"). + * @param namespace The invalid namespace. + */ + error World_InvalidNamespace(bytes14 namespace); + /** * @notice Raised when trying to register a system that already exists. * @param system The address of the system. diff --git a/packages/world/src/WorldResourceId.sol b/packages/world/src/WorldResourceId.sol index 4c6b340103..eb91ddd245 100644 --- a/packages/world/src/WorldResourceId.sol +++ b/packages/world/src/WorldResourceId.sol @@ -7,6 +7,7 @@ import { ResourceId, ResourceIdInstance, TYPE_BITS } from "@latticexyz/store/src import { ROOT_NAMESPACE, ROOT_NAME } from "./constants.sol"; import { RESOURCE_NAMESPACE, MASK_RESOURCE_NAMESPACE } from "./worldResourceTypes.sol"; +uint256 constant NAMESPACE_BYTES = 14; uint256 constant NAMESPACE_BITS = 14 * 8; // 14 bytes * 8 bits per byte uint256 constant NAME_BITS = 16 * 8; // 16 bytes * 8 bits per byte diff --git a/packages/world/src/requireNamespace.sol b/packages/world/src/requireNamespace.sol index 7de2c8088c..dd5e78c361 100644 --- a/packages/world/src/requireNamespace.sol +++ b/packages/world/src/requireNamespace.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { ResourceId, WorldResourceIdInstance } from "./WorldResourceId.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; + +import { ResourceId, WorldResourceIdInstance, NAMESPACE_BYTES } from "./WorldResourceId.sol"; import { RESOURCE_NAMESPACE } from "./worldResourceTypes.sol"; import { IWorldErrors } from "./IWorldErrors.sol"; @@ -13,7 +15,22 @@ using WorldResourceIdInstance for ResourceId; * @param resourceId The resource ID to verify. */ function requireNamespace(ResourceId resourceId) pure { + requireValidCharacters(resourceId.getNamespace()); + if (ResourceId.unwrap(resourceId) != ResourceId.unwrap(resourceId.getNamespaceId())) { revert IWorldErrors.World_InvalidResourceType(RESOURCE_NAMESPACE, resourceId, resourceId.toString()); } } + +/** + * @notice Checks if a given namespace string is valid. + * @dev Reverts with IWorldErrors.World_InvalidNamespace if the namespace includes the reserved separator string ("__"). + * @param namespace The namespace to verify. + */ +function requireValidCharacters(bytes14 namespace) pure { + for (uint256 i; i < NAMESPACE_BYTES - 1; i++) { + if (Bytes.slice1(namespace, i) == bytes1("_") && Bytes.slice1(namespace, i + 1) == bytes1("_")) { + revert IWorldErrors.World_InvalidNamespace(namespace); + } + } +} diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 179ccbc6b2..e3bfd4ce9b 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -345,6 +345,12 @@ contract WorldTest is Test, GasReporter { world.registerNamespace(namespaceId); } + function testRegisterInvalidNamespace() public { + bytes14 invalid_namespace = "invld__nmsp"; + vm.expectRevert(abi.encodeWithSelector(IWorldErrors.World_InvalidNamespace.selector, invalid_namespace)); + world.registerNamespace(WorldResourceIdLib.encodeNamespace(invalid_namespace)); + } + function testRegisterCoreNamespacesRevert() public { vm.expectRevert( abi.encodeWithSelector(