123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- use crate::ANCHOR_VERSION;
- use crate::VERSION;
- use anchor_cli::Files;
- use anyhow::Result;
- 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-ecs/components").join(name);
- let common_files = vec![
- (
- PathBuf::from("Cargo.toml".to_string()),
- workspace_manifest().to_string(),
- ),
- (program_path.join("Cargo.toml"), cargo_toml(name)),
- (program_path.join("Xargo.toml"), xargo_toml().to_string()),
- ] as Files;
- let template_files = create_component_template_simple(name, &program_path);
- anchor_cli::create_files(&[common_files, template_files].concat())
- }
- /// Create a system from the given name.
- pub(crate) fn create_system(name: &str) -> Result<()> {
- let program_path = Path::new("programs-ecs/systems").join(name);
- let common_files = vec![
- (
- PathBuf::from("Cargo.toml".to_string()),
- workspace_manifest().to_string(),
- ),
- (program_path.join("Cargo.toml"), cargo_toml(name)),
- (program_path.join("Xargo.toml"), xargo_toml().to_string()),
- ] as Files;
- let template_files = create_system_template_simple(name, &program_path);
- anchor_cli::create_files(&[common_files, template_files].concat())
- }
- /// 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"),
- format!(
- r#"use bolt_lang::*;
- declare_id!("{}");
- #[component]
- pub struct {} {{
- pub x: i64,
- pub y: i64,
- pub z: i64,
- #[max_len(20)]
- pub description: String,
- }}
- "#,
- anchor_cli::rust_template::get_or_create_program_id(name),
- name.to_upper_camel_case(),
- ),
- )]
- }
- /// 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"),
- format!(
- r#"use bolt_lang::*;
- declare_id!("{}");
- #[system]
- pub mod {} {{
- 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)
- }}
- }}
- // Define the Account to parse from the component
- #[derive(Accounts)]
- pub struct Component<'info> {{
- /// CHECK: check that the component is the expected account
- pub position: AccountInfo<'info>,
- }}
- #[component_deserialize]
- pub struct Position {{
- pub x: i64,
- pub y: i64,
- pub z: i64,
- pub description: String,
- }}
- "#,
- anchor_cli::rust_template::get_or_create_program_id(name),
- name.to_snake_case(),
- ),
- )]
- }
- const fn workspace_manifest() -> &'static str {
- r#"[workspace]
- members = [
- "programs/*",
- "programs-ecs/components/*",
- "programs-ecs/systems/*"
- ]
- resolver = "2"
- [profile.release]
- overflow-checks = true
- lto = "fat"
- codegen-units = 1
- [profile.release.build-override]
- opt-level = 3
- incremental = false
- 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]
- name = "{0}"
- version = "0.1.0"
- description = "Created with Bolt"
- edition = "2021"
- [lib]
- crate-type = ["cdylib", "lib"]
- name = "{1}"
- [features]
- no-entrypoint = []
- no-idl = []
- no-log-ix-name = []
- cpi = ["no-entrypoint"]
- default = []
- idl-build = ["anchor-lang/idl-build"]
- [dependencies]
- bolt-lang = "{2}"
- anchor-lang = "{3}"
- "#,
- name,
- name.to_snake_case(),
- VERSION,
- anchor_cli::VERSION,
- )
- }
- fn xargo_toml() -> &'static str {
- r#"[target.bpfel-unknown-unknown.dependencies.std]
- 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
- "#
- }
|