|
@@ -1,12 +1,13 @@
|
|
|
+use crate::ANCHOR_VERSION;
|
|
|
use crate::VERSION;
|
|
|
use anchor_cli::Files;
|
|
|
use anyhow::Result;
|
|
|
-use heck::{ToKebabCase, ToSnakeCase, ToUpperCamelCase};
|
|
|
+use heck::{ToSnakeCase, ToUpperCamelCase};
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
/// Create a component from the given name.
|
|
|
pub fn create_component(name: &str) -> Result<()> {
|
|
|
- let program_path = Path::new("programs").join(name);
|
|
|
+ let program_path = Path::new("programs-ecs/components").join(name);
|
|
|
let common_files = vec![
|
|
|
(
|
|
|
PathBuf::from("Cargo.toml".to_string()),
|
|
@@ -22,7 +23,7 @@ pub fn create_component(name: &str) -> Result<()> {
|
|
|
|
|
|
/// Create a system from the given name.
|
|
|
pub(crate) fn create_system(name: &str) -> Result<()> {
|
|
|
- let program_path = Path::new("programs").join(name);
|
|
|
+ let program_path = Path::new("programs-ecs/systems").join(name);
|
|
|
let common_files = vec![
|
|
|
(
|
|
|
PathBuf::from("Cargo.toml".to_string()),
|
|
@@ -36,7 +37,7 @@ pub(crate) fn create_system(name: &str) -> Result<()> {
|
|
|
anchor_cli::create_files(&[common_files, template_files].concat())
|
|
|
}
|
|
|
|
|
|
-/// Create a program with a single `lib.rs` file.
|
|
|
+/// Create a component which holds position data.
|
|
|
fn create_component_template_simple(name: &str, program_path: &Path) -> Files {
|
|
|
vec![(
|
|
|
program_path.join("src").join("lib.rs"),
|
|
@@ -52,7 +53,7 @@ pub mod {} {{
|
|
|
}}
|
|
|
|
|
|
#[account]
|
|
|
-#[bolt_account(component_id = "{}")]
|
|
|
+#[bolt_account(component_id = "")]
|
|
|
pub struct {} {{
|
|
|
pub x: i64,
|
|
|
pub y: i64,
|
|
@@ -64,13 +65,12 @@ pub struct {} {{
|
|
|
anchor_cli::rust_template::get_or_create_program_id(name),
|
|
|
name.to_upper_camel_case(),
|
|
|
name.to_snake_case(),
|
|
|
- name.to_kebab_case(),
|
|
|
name.to_upper_camel_case(),
|
|
|
),
|
|
|
)]
|
|
|
}
|
|
|
|
|
|
-/// Create a program with a single `lib.rs` file.
|
|
|
+/// Create a system which operates on a Position component.
|
|
|
fn create_system_template_simple(name: &str, program_path: &Path) -> Files {
|
|
|
vec![(
|
|
|
program_path.join("src").join("lib.rs"),
|
|
@@ -85,10 +85,8 @@ pub mod {} {{
|
|
|
use super::*;
|
|
|
|
|
|
pub fn execute(ctx: Context<Component>, args: Vec<u8>) -> Result<Position> {{
|
|
|
-
|
|
|
let mut position = Position::from_account_info(&ctx.accounts.position)?;
|
|
|
position.x += 1;
|
|
|
-
|
|
|
Ok(position)
|
|
|
}}
|
|
|
}}
|
|
@@ -105,7 +103,6 @@ pub struct Position {{
|
|
|
pub x: i64,
|
|
|
pub y: i64,
|
|
|
pub z: i64,
|
|
|
- #[max_len(20)]
|
|
|
pub description: String,
|
|
|
}}
|
|
|
"#,
|
|
@@ -118,7 +115,9 @@ pub struct Position {{
|
|
|
const fn workspace_manifest() -> &'static str {
|
|
|
r#"[workspace]
|
|
|
members = [
|
|
|
- "programs/*"
|
|
|
+ "programs/*",
|
|
|
+ "programs-ecs/components/*",
|
|
|
+ "programs-ecs/systems/*"
|
|
|
]
|
|
|
resolver = "2"
|
|
|
|
|
@@ -133,6 +132,292 @@ codegen-units = 1
|
|
|
"#
|
|
|
}
|
|
|
|
|
|
+pub fn package_json(jest: bool) -> String {
|
|
|
+ if jest {
|
|
|
+ format!(
|
|
|
+ r#"{{
|
|
|
+ "scripts": {{
|
|
|
+ "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
+ "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
+ }},
|
|
|
+ "dependencies": {{
|
|
|
+ "@coral-xyz/anchor": "^{VERSION}"
|
|
|
+ }},
|
|
|
+ "devDependencies": {{
|
|
|
+ "jest": "^29.0.3",
|
|
|
+ "prettier": "^2.6.2"
|
|
|
+ }}
|
|
|
+ }}
|
|
|
+ "#
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ format!(
|
|
|
+ r#"{{
|
|
|
+ "scripts": {{
|
|
|
+ "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
+ "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
+ }},
|
|
|
+ "dependencies": {{
|
|
|
+ "@coral-xyz/anchor": "^{VERSION}"
|
|
|
+ }},
|
|
|
+ "devDependencies": {{
|
|
|
+ "chai": "^4.3.4",
|
|
|
+ "mocha": "^9.0.3",
|
|
|
+ "prettier": "^2.6.2",
|
|
|
+ "@metaplex-foundation/beet": "^0.7.1",
|
|
|
+ "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
+ "bolt-sdk": "latest"
|
|
|
+ }}
|
|
|
+}}
|
|
|
+"#
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ts_package_json(jest: bool) -> String {
|
|
|
+ if jest {
|
|
|
+ format!(
|
|
|
+ r#"{{
|
|
|
+ "scripts": {{
|
|
|
+ "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
+ "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
+ }},
|
|
|
+ "dependencies": {{
|
|
|
+ "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
+ }},
|
|
|
+ "devDependencies": {{
|
|
|
+ "@types/bn.js": "^5.1.0",
|
|
|
+ "@types/jest": "^29.0.3",
|
|
|
+ "jest": "^29.0.3",
|
|
|
+ "prettier": "^2.6.2",
|
|
|
+ "ts-jest": "^29.0.2",
|
|
|
+ "typescript": "^4.3.5",
|
|
|
+ "@metaplex-foundation/beet": "^0.7.1",
|
|
|
+ "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
+ "bolt-sdk": "latest"
|
|
|
+ }}
|
|
|
+ }}
|
|
|
+ "#
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ format!(
|
|
|
+ r#"{{
|
|
|
+ "scripts": {{
|
|
|
+ "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
+ "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
+ }},
|
|
|
+ "dependencies": {{
|
|
|
+ "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
+ }},
|
|
|
+ "devDependencies": {{
|
|
|
+ "chai": "^4.3.4",
|
|
|
+ "mocha": "^9.0.3",
|
|
|
+ "ts-mocha": "^10.0.0",
|
|
|
+ "@types/bn.js": "^5.1.0",
|
|
|
+ "@types/chai": "^4.3.0",
|
|
|
+ "@types/mocha": "^9.0.0",
|
|
|
+ "typescript": "^4.3.5",
|
|
|
+ "prettier": "^2.6.2",
|
|
|
+ "@metaplex-foundation/beet": "^0.7.1",
|
|
|
+ "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
+ "bolt-sdk": "latest"
|
|
|
+ }}
|
|
|
+}}
|
|
|
+"#
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn mocha(name: &str) -> String {
|
|
|
+ format!(
|
|
|
+ r#"const anchor = require("@coral-xyz/anchor");
|
|
|
+const boltSdk = require("bolt-sdk");
|
|
|
+const {{
|
|
|
+ createInitializeNewWorldInstruction,
|
|
|
+ FindWorldPda,
|
|
|
+ FindWorldRegistryPda,
|
|
|
+ Registry,
|
|
|
+ World
|
|
|
+}} = boltSdk;
|
|
|
+
|
|
|
+describe("{}", () => {{
|
|
|
+ // Configure the client to use the local cluster.
|
|
|
+ const provider = anchor.AnchorProvider.env();
|
|
|
+ anchor.setProvider(provider);
|
|
|
+
|
|
|
+ it("InitializeNewWorld", async () => {{
|
|
|
+ const registry = await Registry.fromAccountAddress(provider.connection, registryPda);
|
|
|
+ worldId = new anchor.BN(registry.worlds);
|
|
|
+ worldPda = FindWorldPda(new anchor.BN(worldId))
|
|
|
+ const initializeWorldIx = createInitializeNewWorldInstruction(
|
|
|
+ {{
|
|
|
+ world: worldPda,
|
|
|
+ registry: registryPda,
|
|
|
+ payer: provider.wallet.publicKey,
|
|
|
+ }});
|
|
|
+
|
|
|
+ const tx = new anchor.web3.Transaction().add(initializeWorldIx);
|
|
|
+ const txSign = await provider.sendAndConfirm(tx);
|
|
|
+ console.log(`Initialized a new world (ID=${{worldId}}). Initialization signature: ${{txSign}}`);
|
|
|
+ }});
|
|
|
+ }});
|
|
|
+}});
|
|
|
+"#,
|
|
|
+ name,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+pub fn jest(name: &str) -> String {
|
|
|
+ format!(
|
|
|
+ r#"const anchor = require("@coral-xyz/anchor");
|
|
|
+const boltSdk = require("bolt-sdk");
|
|
|
+const {{
|
|
|
+ createInitializeNewWorldInstruction,
|
|
|
+ FindWorldPda,
|
|
|
+ FindWorldRegistryPda,
|
|
|
+ Registry,
|
|
|
+ World
|
|
|
+}} = boltSdk;
|
|
|
+
|
|
|
+describe("{}", () => {{
|
|
|
+ // Configure the client to use the local cluster.
|
|
|
+ const provider = anchor.AnchorProvider.env();
|
|
|
+ anchor.setProvider(provider);
|
|
|
+
|
|
|
+ // Constants used to test the program.
|
|
|
+ const registryPda = FindWorldRegistryPda();
|
|
|
+ let worldId: anchor.BN;
|
|
|
+ let worldPda: PublicKey;
|
|
|
+
|
|
|
+ it("InitializeNewWorld", async () => {{
|
|
|
+ const registry = await Registry.fromAccountAddress(provider.connection, registryPda);
|
|
|
+ worldId = new anchor.BN(registry.worlds);
|
|
|
+ worldPda = FindWorldPda(new anchor.BN(worldId))
|
|
|
+ const initializeWorldIx = createInitializeNewWorldInstruction(
|
|
|
+ {{
|
|
|
+ world: worldPda,
|
|
|
+ registry: registryPda,
|
|
|
+ payer: provider.wallet.publicKey,
|
|
|
+ }});
|
|
|
+
|
|
|
+ const tx = new anchor.web3.Transaction().add(initializeWorldIx);
|
|
|
+ const txSign = await provider.sendAndConfirm(tx);
|
|
|
+ console.log(`Initialized a new world (ID=${{worldId}}). Initialization signature: ${{txSign}}`);
|
|
|
+ }});
|
|
|
+ }});
|
|
|
+"#,
|
|
|
+ name,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+pub fn ts_mocha(name: &str) -> String {
|
|
|
+ format!(
|
|
|
+ r#"import * as anchor from "@coral-xyz/anchor";
|
|
|
+import {{ Program }} from "@coral-xyz/anchor";
|
|
|
+import {{ PublicKey }} from "@solana/web3.js";
|
|
|
+import {{ Position }} from "../target/types/position";
|
|
|
+import {{ Movement }} from "../target/types/movement";
|
|
|
+import {{
|
|
|
+ createInitializeNewWorldInstruction,
|
|
|
+ FindWorldPda,
|
|
|
+ FindWorldRegistryPda,
|
|
|
+ FindEntityPda,
|
|
|
+ Registry,
|
|
|
+ World,
|
|
|
+ createAddEntityInstruction,
|
|
|
+ createInitializeComponentInstruction,
|
|
|
+ FindComponentPda, createApplyInstruction
|
|
|
+}} from "bolt-sdk"
|
|
|
+import {{expect}} from "chai";
|
|
|
+
|
|
|
+describe("{}", () => {{
|
|
|
+ // Configure the client to use the local cluster.
|
|
|
+ const provider = anchor.AnchorProvider.env();
|
|
|
+ anchor.setProvider(provider);
|
|
|
+
|
|
|
+ // Constants used to test the program.
|
|
|
+ const registryPda = FindWorldRegistryPda();
|
|
|
+ let worldId: anchor.BN;
|
|
|
+ let worldPda: PublicKey;
|
|
|
+ let entityPda: PublicKey;
|
|
|
+
|
|
|
+ const positionComponent = anchor.workspace.Position as Program<Position>;
|
|
|
+ const systemMovement = anchor.workspace.Movement as Program<Movement>;
|
|
|
+
|
|
|
+ it("InitializeNewWorld", async () => {{
|
|
|
+ const registry = await Registry.fromAccountAddress(provider.connection, registryPda);
|
|
|
+ worldId = new anchor.BN(registry.worlds);
|
|
|
+ worldPda = FindWorldPda(new anchor.BN(worldId))
|
|
|
+ const initializeWorldIx = createInitializeNewWorldInstruction(
|
|
|
+ {{
|
|
|
+ world: worldPda,
|
|
|
+ registry: registryPda,
|
|
|
+ payer: provider.wallet.publicKey,
|
|
|
+ }});
|
|
|
+
|
|
|
+ const tx = new anchor.web3.Transaction().add(initializeWorldIx);
|
|
|
+ const txSign = await provider.sendAndConfirm(tx);
|
|
|
+ console.log(`Initialized a new world (ID=${{worldId}}). Initialization signature: ${{txSign}}`);
|
|
|
+ }});
|
|
|
+
|
|
|
+ it("Add an entity", async () => {{
|
|
|
+ const world = await World.fromAccountAddress(provider.connection, worldPda);
|
|
|
+ const entityId = new anchor.BN(world.entities);
|
|
|
+ entityPda = FindEntityPda(worldId, entityId);
|
|
|
+
|
|
|
+ let createEntityIx = createAddEntityInstruction({{
|
|
|
+ world: worldPda,
|
|
|
+ payer: provider.wallet.publicKey,
|
|
|
+ entity: entityPda,
|
|
|
+ }});
|
|
|
+ const tx = new anchor.web3.Transaction().add(createEntityIx);
|
|
|
+ const txSign = await provider.sendAndConfirm(tx);
|
|
|
+ console.log(`Initialized a new Entity (ID=${{worldId}}). Initialization signature: ${{txSign}}`);
|
|
|
+ }});
|
|
|
+
|
|
|
+ it("Add a component", async () => {{
|
|
|
+ const positionComponentPda = FindComponentPda(positionComponent.programId, entityPda, "");
|
|
|
+ let initComponentIx = createInitializeComponentInstruction({{
|
|
|
+ payer: provider.wallet.publicKey,
|
|
|
+ entity: entityPda,
|
|
|
+ data: positionComponentPda,
|
|
|
+ componentProgram: positionComponent.programId,
|
|
|
+ }});
|
|
|
+
|
|
|
+ const tx = new anchor.web3.Transaction().add(initComponentIx);
|
|
|
+ const txSign = await provider.sendAndConfirm(tx);
|
|
|
+ console.log(`Initialized a new component. Initialization signature: ${{txSign}}`);
|
|
|
+ }});
|
|
|
+
|
|
|
+ it("Apply a system", async () => {{
|
|
|
+ const positionComponentPda = FindComponentPda(positionComponent.programId, entityPda, "");
|
|
|
+ // Check that the component has been initialized and x is 0
|
|
|
+ let positionData = await positionComponent.account.position.fetch(
|
|
|
+ positionComponentPda
|
|
|
+ );
|
|
|
+ expect(positionData.x.toNumber()).to.eq(0);
|
|
|
+ let applySystemIx = createApplyInstruction({{
|
|
|
+ componentProgram: positionComponent.programId,
|
|
|
+ boltSystem: systemMovement.programId,
|
|
|
+ boltComponent: positionComponentPda,
|
|
|
+ }}, {{args: new Uint8Array()}});
|
|
|
+
|
|
|
+ const tx = new anchor.web3.Transaction().add(applySystemIx);
|
|
|
+ await provider.sendAndConfirm(tx);
|
|
|
+
|
|
|
+ // Check that the system has been applied and x is > 0
|
|
|
+ positionData = await positionComponent.account.position.fetch(
|
|
|
+ positionComponentPda
|
|
|
+ );
|
|
|
+ expect(positionData.x.toNumber()).to.gt(0);
|
|
|
+ }});
|
|
|
+
|
|
|
+}});
|
|
|
+"#,
|
|
|
+ name.to_upper_camel_case(),
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
fn cargo_toml(name: &str) -> String {
|
|
|
format!(
|
|
|
r#"[package]
|
|
@@ -168,3 +453,28 @@ fn xargo_toml() -> &'static str {
|
|
|
features = []
|
|
|
"#
|
|
|
}
|
|
|
+pub fn git_ignore() -> &'static str {
|
|
|
+ r#"
|
|
|
+.anchor
|
|
|
+.bolt
|
|
|
+.DS_Store
|
|
|
+target
|
|
|
+**/*.rs.bk
|
|
|
+node_modules
|
|
|
+test-ledger
|
|
|
+.yarn
|
|
|
+"#
|
|
|
+}
|
|
|
+
|
|
|
+pub fn prettier_ignore() -> &'static str {
|
|
|
+ r#"
|
|
|
+.anchor
|
|
|
+.bolt
|
|
|
+.DS_Store
|
|
|
+target
|
|
|
+node_modules
|
|
|
+dist
|
|
|
+build
|
|
|
+test-ledger
|
|
|
+"#
|
|
|
+}
|