Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(store,world): enable namespaces input #2968

Merged
merged 18 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .changeset/seven-melons-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
"@latticexyz/cli": minor
"@latticexyz/store": minor
"@latticexyz/world": minor
---

MUD projects can now use multiple namespaces via a new top-level `namespaces` config option.

```ts
import { defineWorld } from "@latticexyz/world";

export default defineWorld({
namespaces: {
app: {
tables: { ... },
systems: { ... },
},
},
});
```

Once you use the top-level `namespaces` config option, your project will be in "multiple namespaces mode", which expects a source directory structure similar to the config structure: a top-level `namespaces` directory with nested namespace directories that correspond to each namespace label in the config.

```
mud-project/
├─ mud.config.ts
└─ src/
└─ namespaces/
└─ app/
├─ TasksSystem.sol
└─ codegen/
├─ tables/
└─ Tasks.sol
```
38 changes: 23 additions & 15 deletions examples/multiple-namespaces/mud.config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { defineWorld } from "@latticexyz/world";

export default defineWorld({
namespace: "game",
codegen: { namespaceDirectories: true },
tables: {
Health: {
schema: {
player: "address",
value: "uint32",
namespaces: {
game: {
tables: {
Health: {
schema: {
player: "address",
value: "uint32",
},
key: ["player"],
},
Position: {
schema: {
player: "address",
x: "int32",
y: "int32",
},
key: ["player"],
},
},
key: ["player"],
},
Position: {
schema: {
player: "address",
x: "int32",
y: "int32",
systems: {
HiddenSystem: {
openAccess: false,
accessList: ["MoveSystem"],
},
},
key: ["player"],
},
},
});
7 changes: 0 additions & 7 deletions examples/multiple-namespaces/src/codegen/index.sol

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/multiple-namespaces/src/codegen/world/IWorld.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
import { System } from "@latticexyz/world/src/System.sol";
import { Health } from "../codegen/game/tables/Health.sol";
import { Health } from "./codegen/tables/Health.sol";

contract HealSystem is System {
function heal(address player) public {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
import { System } from "@latticexyz/world/src/System.sol";

contract HiddenSystem is System {
function hide() public {
// Do nothing for now
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
import { System } from "@latticexyz/world/src/System.sol";
import { Position } from "../codegen/game/tables/Position.sol";
import { Position } from "./codegen/tables/Position.sol";

contract MoveSystem is System {
function move(address player, int32 x, int32 y) public {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/multiple-namespaces/test/HealthTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import { MudTest } from "@latticexyz/world/test/MudTest.t.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { Health } from "../src/codegen/game/tables/Health.sol";
import { Health } from "../src/namespaces/game/codegen/tables/Health.sol";

contract HealthTest is MudTest {
function testHealth(address player) public {
Expand Down
15 changes: 15 additions & 0 deletions examples/multiple-namespaces/test/HiddenSystemTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

import "forge-std/Test.sol";
import { MudTest } from "@latticexyz/world/test/MudTest.t.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { Position, PositionData } from "../src/namespaces/game/codegen/tables/Position.sol";

contract HiddenSystemTest is MudTest {
function testHide() public {
vm.expectRevert();
IWorld(worldAddress).game__hide();
}
}
2 changes: 1 addition & 1 deletion examples/multiple-namespaces/test/PositionTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import { MudTest } from "@latticexyz/world/test/MudTest.t.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { Position, PositionData } from "../src/codegen/game/tables/Position.sol";
import { Position, PositionData } from "../src/namespaces/game/codegen/tables/Position.sol";

contract PositionTest is MudTest {
function testPosition(address player, int32 x, int32 y) public {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ export async function deploy({
...libraries.map((library) => ({
bytecode: library.prepareDeploy(deployerAddress, libraries).bytecode,
deployedBytecodeSize: library.deployedBytecodeSize,
label: `${library.path}:${library.name} library`,
debugLabel: `${library.path}:${library.name} library`,
})),
...systems.map((system) => ({
bytecode: system.prepareDeploy(deployerAddress, libraries).bytecode,
deployedBytecodeSize: system.deployedBytecodeSize,
label: `${resourceToLabel(system)} system`,
debugLabel: `${resourceToLabel(system)} system`,
})),
...modules.map((mod) => ({
bytecode: mod.prepareDeploy(deployerAddress, libraries).bytecode,
deployedBytecodeSize: mod.deployedBytecodeSize,
label: `${mod.name} module`,
debugLabel: `${mod.name} module`,
})),
],
});
Expand Down
17 changes: 9 additions & 8 deletions packages/cli/src/deploy/ensureContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,43 @@ import { wait } from "@latticexyz/common/utils";
export type Contract = {
bytecode: Hex;
deployedBytecodeSize: number;
label?: string;
debugLabel?: string;
};

export async function ensureContract({
client,
deployerAddress,
bytecode,
deployedBytecodeSize,
label = "contract",
debugLabel = "contract",
}: {
readonly client: Client<Transport, Chain | undefined, Account>;
readonly deployerAddress: Hex;
} & Contract): Promise<readonly Hex[]> {
if (bytecode.includes("__$")) {
throw new Error(`Found unlinked public library in ${label} bytecode`);
throw new Error(`Found unlinked public library in ${debugLabel} bytecode`);
}

const address = getCreate2Address({ from: deployerAddress, salt, bytecode });

const contractCode = await getBytecode(client, { address, blockTag: "pending" });
if (contractCode) {
debug("found", label, "at", address);
debug("found", debugLabel, "at", address);
return [];
}

if (deployedBytecodeSize > contractSizeLimit) {
console.warn(
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
`\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
);
} else if (deployedBytecodeSize > contractSizeLimit * 0.95) {
console.warn(
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
// eslint-disable-next-line max-len
`\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
);
}

debug("deploying", label, "at", address);
debug("deploying", debugLabel, "at", address);
return [
await pRetry(
() =>
Expand All @@ -57,7 +58,7 @@ export async function ensureContract({
retries: 3,
onFailedAttempt: async (error) => {
const delay = error.attemptNumber * 500;
debug(`failed to deploy ${label}, retrying in ${delay}ms...`);
debug(`failed to deploy ${debugLabel}, retrying in ${delay}ms...`);
await wait(delay);
},
},
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function ensureModules({
contracts: modules.map((mod) => ({
bytecode: mod.prepareDeploy(deployerAddress, libraries).bytecode,
deployedBytecodeSize: mod.deployedBytecodeSize,
label: `${mod.name} module`,
debugLabel: `${mod.name} module`,
})),
});

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export async function ensureSystems({
contracts: missingSystems.map((system) => ({
bytecode: system.prepareDeploy(deployerAddress, libraries).bytecode,
deployedBytecodeSize: system.deployedBytecodeSize,
label: `${resourceToLabel(system)} system`,
debugLabel: `${resourceToLabel(system)} system`,
})),
});

Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/deploy/getWorldContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,31 +48,31 @@ export function getWorldContracts(deployerAddress: Hex) {
AccessManagementSystem: {
bytecode: accessManagementSystemBytecode,
deployedBytecodeSize: accessManagementSystemDeployedBytecodeSize,
label: "access management system",
debugLabel: "access management system",
address: accessManagementSystem,
},
BalanceTransferSystem: {
bytecode: balanceTransferSystemBytecode,
deployedBytecodeSize: balanceTransferSystemDeployedBytecodeSize,
label: "balance transfer system",
debugLabel: "balance transfer system",
address: balanceTransferSystem,
},
BatchCallSystem: {
bytecode: batchCallSystemBytecode,
deployedBytecodeSize: batchCallSystemDeployedBytecodeSize,
label: "batch call system",
debugLabel: "batch call system",
address: batchCallSystem,
},
RegistrationSystem: {
bytecode: registrationBytecode,
deployedBytecodeSize: registrationDeployedBytecodeSize,
label: "core registration system",
debugLabel: "core registration system",
address: registration,
},
InitModule: {
bytecode: initModuleBytecode,
deployedBytecodeSize: initModuleDeployedBytecodeSize,
label: "core module",
debugLabel: "core module",
address: initModule,
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/getWorldFactoryContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function getWorldFactoryContracts(deployerAddress: Hex) {
WorldFactory: {
bytecode: worldFactoryBytecode,
deployedBytecodeSize: worldFactoryDeployedBytecodeSize,
label: "world factory",
debugLabel: "world factory",
address: worldFactory,
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/getWorldProxyFactoryContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function getWorldProxyFactoryContracts(deployerAddress: Hex) {
WorldProxyFactory: {
bytecode: worldProxyFactoryBytecode,
deployedBytecodeSize: worldProxyFactoryDeployedBytecodeSize,
label: "world proxy factory",
debugLabel: "world proxy factory",
address: worldProxyFactory,
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/store/mud.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defineStore } from "./ts/config/v2/store";

export default defineStore({
namespace: "store",
codegen: {
storeImportPath: "./src",
},
namespace: "store",
userTypes: {
ResourceId: { filePath: "./src/ResourceId.sol", type: "bytes32" },
FieldLayout: { filePath: "./src/FieldLayout.sol", type: "bytes32" },
Expand Down
12 changes: 6 additions & 6 deletions packages/store/ts/codegen/tablegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export async function tablegen({ rootDir, config }: TablegenOptions) {

await Promise.all(
Object.values(config.namespaces).map(async (namespace) => {
// TODO: get this value from config once multiple namespaces are supported
const multipleNamespaces = false;
const sourceDir = multipleNamespaces
const sourceDir = config.multipleNamespaces
? path.join(config.sourceDirectory, "namespaces", namespace.label)
: config.sourceDirectory;
const codegenDir = path.join(sourceDir, config.codegen.outputDirectory);
Expand Down Expand Up @@ -60,9 +58,11 @@ export async function tablegen({ rootDir, config }: TablegenOptions) {
}),
);

const codegenIndexPath = path.join(rootDir, codegenDir, config.codegen.indexFilename);
const source = renderTableIndex(codegenIndexPath, tableOptions);
await formatAndWriteSolidity(source, codegenIndexPath, "Generated table index");
if (config.codegen.indexFilename !== false && tableOptions.length > 0) {
const codegenIndexPath = path.join(rootDir, codegenDir, config.codegen.indexFilename);
const source = renderTableIndex(codegenIndexPath, tableOptions);
await formatAndWriteSolidity(source, codegenIndexPath, "Generated table index");
}
}),
);
}
2 changes: 0 additions & 2 deletions packages/store/ts/config/v2/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export const CODEGEN_DEFAULTS = {
storeImportPath: "@latticexyz/store/src",
userTypesFilename: "common.sol",
outputDirectory: "codegen",
// TODO: default to true if using top-level `namespaces` key (once its migrated to store)
namespaceDirectories: false,
indexFilename: "index.sol",
} as const satisfies CodegenInput;

Expand Down
Loading
Loading