├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── backend ├── Anchor.toml ├── Cargo.lock ├── Cargo.toml ├── package.json ├── programs-ecs │ ├── components │ │ └── game │ │ │ ├── Cargo.toml │ │ │ ├── Xargo.toml │ │ │ └── src │ │ │ └── lib.rs │ └── systems │ │ ├── command │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ │ └── lib.rs │ │ ├── finish │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ │ └── lib.rs │ │ ├── generate │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ │ └── lib.rs │ │ ├── join │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ │ └── lib.rs │ │ ├── start │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ │ └── lib.rs │ │ └── tick │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ └── lib.rs ├── tests │ ├── backend.ts │ └── fixtures │ │ ├── registry.json │ │ └── world.so ├── tsconfig.json └── yarn.lock └── frontend ├── README.md ├── index.d.ts ├── package.json ├── src ├── components │ ├── game │ │ ├── GameError.tsx │ │ ├── GamePlayer.scss │ │ ├── GamePlayer.tsx │ │ ├── grid │ │ │ ├── GameGridCell.scss │ │ │ ├── GameGridCell.tsx │ │ │ ├── GameGridCellCapital.png │ │ │ ├── GameGridCellCity.png │ │ │ ├── GameGridCellField.png │ │ │ ├── GameGridCellMountain.png │ │ │ ├── GameGridRows.scss │ │ │ └── GameGridRows.tsx │ │ ├── lobby │ │ │ ├── GameLobbyPlayer.tsx │ │ │ └── GameLobbyRoot.tsx │ │ └── play │ │ │ ├── GamePlayMap.tsx │ │ │ └── GamePlayRoot.tsx │ ├── menu │ │ ├── MenuBalance.tsx │ │ ├── MenuBar.scss │ │ ├── MenuBar.tsx │ │ ├── MenuSession.tsx │ │ └── MenuWallet.tsx │ ├── page │ │ ├── PageCreate.tsx │ │ ├── PageHome.tsx │ │ └── PagePlay.tsx │ └── util │ │ ├── Button.scss │ │ ├── Button.tsx │ │ ├── ForEach.tsx │ │ ├── If.tsx │ │ ├── Text.scss │ │ └── Text.tsx ├── engine │ ├── MagicBlockEngine.ts │ ├── MagicBlockEngineProvider.tsx │ └── MagicBlockQueue.ts ├── index.html ├── index.scss ├── index.tsx └── states │ ├── gameCreate.ts │ ├── gameFetch.ts │ ├── gameList.ts │ ├── gameListen.ts │ ├── gameLog.ts │ ├── gamePrograms.ts │ ├── gameSystemCommand.ts │ ├── gameSystemFinish.ts │ ├── gameSystemGenerate.ts │ ├── gameSystemJoin.ts │ ├── gameSystemStart.ts │ ├── gameSystemTick.ts │ └── gameWorld.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .anchor 3 | .bolt 4 | .DS_Store 5 | target 6 | **/*.rs.bk 7 | node_modules 8 | test-ledger 9 | .yarn 10 | build 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#491B47", 4 | "titleBar.activeBackground": "#662664", 5 | "titleBar.activeForeground": "#FDF9FC" 6 | } 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Magicblock Pte. Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # MagicBlock Engine Example 3 | 4 | This repository contains an example for a fully featured game running 100% serverless using Solana's devnet and MagicBlock's devnet. 5 | 6 | Demo available [here](https://magicblock-labs.github.io/solana-generals/#/) 7 | 8 | # Demo 9 | 10 | Run the frontend that should already be pointing to solana's devnet: 11 | 12 | ```bash 13 | cd backend 14 | bolt build 15 | cd ../frontend 16 | npm install 17 | npm run dev 18 | ``` 19 | 20 | You can then head to: https://faucet.solana.com/ to get some devnet SOL you can use to fund your player's account to start testing. 21 | 22 | # Code organization 23 | 24 | ## Backend 25 | 26 | The `backend` folder contains all smart contract code, it uses MagicBlock's BOLT. 27 | 28 | The smart contracts should already be deployed on devnet. 29 | 30 | You can also locally run the integration tests: 31 | 32 | ```bash 33 | cd backend 34 | bolt test 35 | ``` 36 | 37 | Some of the important code pieces are: 38 | 39 | - `backend/programs-ecs/components` - Contains the definition for the game datastructure 40 | - `backend/programs-ecs/systems` - Contains all possible game mutations available 41 | - `backend/tests` - Integration tests and example on how the game can be used 42 | 43 | ## Frontend 44 | 45 | The `frontend` folder contains an implementation for a React based web UI to interact with the smart contracts. 46 | 47 | Some of the important pieces of code are: 48 | 49 | - `frontend/src/states` - Utility functions to interact with the smart contract 50 | - `frontend/src/components/menu` - Wallet manipulation code (including session keys) 51 | - `frontend/src/components/page` - High level components for each page of the application 52 | 53 | Reading some of those will help with the overall understanding of the architechture 54 | -------------------------------------------------------------------------------- /backend/Anchor.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | 3 | [features] 4 | resolution = true 5 | skip-lint = false 6 | 7 | [programs.devnet] 8 | game = "C5iL81s4Fu6SnkQEfixFZpKPRQ32fqVizpotoLVTxA2n" 9 | 10 | command = "YGKbhp7S1cCvvheyQ8rcuECKUR1SVpKpHjnqCqdP1cm" 11 | finish = "HBdGPJycpHjjJ149T3RQGtQWjSC39MVpcKYF6JJvaF6e" 12 | generate = "3KFBmeDYJgrB9SB8jfsrXQN5wNvKinneJgNnpa2KgRw7" 13 | join = "3zMXokc8DYYAairrtAKZKPJZKHmWKRdj6G8bm8ZZVi9g" 14 | start = "Cu8JkUA9a5msGWNChAuhBJ9PTE6FdevwHNgPyxbABkUL" 15 | tick = "8tKAapRKPrNkxXwcArbSAnBHieYnX6M2WoTxukbCQtTa" 16 | 17 | [registry] 18 | url = "https://api.apr.dev" 19 | 20 | [provider] 21 | cluster = "devnet" 22 | wallet = "~/.config/solana/id.json" 23 | 24 | [workspace] 25 | members = ["programs-ecs/components/*", "programs-ecs/systems/*"] 26 | 27 | [scripts] 28 | test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" 29 | 30 | [test] 31 | startup_wait = 5000 32 | shutdown_wait = 2000 33 | upgradeable = false 34 | 35 | [[test.genesis]] 36 | address = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n" 37 | program = "tests/fixtures/world.so" 38 | upgradeable = false 39 | 40 | [test.validator] 41 | bind_address = "0.0.0.0" 42 | url = "https://rpc.magicblock.app/devnet/" 43 | ledger = ".bolt/test-ledger" 44 | rpc_port = 8899 45 | 46 | [[test.validator.account]] 47 | address = "EHLkWwAT9oebVv9ht3mtqrvHhRVMKrt54tF3MfHTey2K" 48 | filename = "tests/fixtures/registry.json" 49 | -------------------------------------------------------------------------------- /backend/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.8" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" 10 | dependencies = [ 11 | "getrandom 0.2.15", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "ahash" 18 | version = "0.8.11" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 21 | dependencies = [ 22 | "cfg-if", 23 | "getrandom 0.2.15", 24 | "once_cell", 25 | "version_check", 26 | "zerocopy", 27 | ] 28 | 29 | [[package]] 30 | name = "aho-corasick" 31 | version = "1.1.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 34 | dependencies = [ 35 | "memchr", 36 | ] 37 | 38 | [[package]] 39 | name = "anchor-attribute-access-control" 40 | version = "0.30.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "47fe28365b33e8334dd70ae2f34a43892363012fe239cf37d2ee91693575b1f8" 43 | dependencies = [ 44 | "anchor-syn", 45 | "proc-macro2", 46 | "quote", 47 | "syn 1.0.109", 48 | ] 49 | 50 | [[package]] 51 | name = "anchor-attribute-account" 52 | version = "0.30.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "3c288d496168268d198d9b53ee9f4f9d260a55ba4df9877ea1d4486ad6109e0f" 55 | dependencies = [ 56 | "anchor-syn", 57 | "bs58 0.5.1", 58 | "proc-macro2", 59 | "quote", 60 | "syn 1.0.109", 61 | ] 62 | 63 | [[package]] 64 | name = "anchor-attribute-constant" 65 | version = "0.30.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "49b77b6948d0eeaaa129ce79eea5bbbb9937375a9241d909ca8fb9e006bb6e90" 68 | dependencies = [ 69 | "anchor-syn", 70 | "quote", 71 | "syn 1.0.109", 72 | ] 73 | 74 | [[package]] 75 | name = "anchor-attribute-error" 76 | version = "0.30.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "4d20bb569c5a557c86101b944721d865e1fd0a4c67c381d31a44a84f07f84828" 79 | dependencies = [ 80 | "anchor-syn", 81 | "quote", 82 | "syn 1.0.109", 83 | ] 84 | 85 | [[package]] 86 | name = "anchor-attribute-event" 87 | version = "0.30.1" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "4cebd8d0671a3a9dc3160c48598d652c34c77de6be4d44345b8b514323284d57" 90 | dependencies = [ 91 | "anchor-syn", 92 | "proc-macro2", 93 | "quote", 94 | "syn 1.0.109", 95 | ] 96 | 97 | [[package]] 98 | name = "anchor-attribute-program" 99 | version = "0.30.1" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "efb2a5eb0860e661ab31aff7bb5e0288357b176380e985bade4ccb395981b42d" 102 | dependencies = [ 103 | "anchor-lang-idl", 104 | "anchor-syn", 105 | "anyhow", 106 | "bs58 0.5.1", 107 | "heck", 108 | "proc-macro2", 109 | "quote", 110 | "serde_json", 111 | "syn 1.0.109", 112 | ] 113 | 114 | [[package]] 115 | name = "anchor-derive-accounts" 116 | version = "0.30.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "04368b5abef4266250ca8d1d12f4dff860242681e4ec22b885dcfe354fd35aa1" 119 | dependencies = [ 120 | "anchor-syn", 121 | "quote", 122 | "syn 1.0.109", 123 | ] 124 | 125 | [[package]] 126 | name = "anchor-derive-serde" 127 | version = "0.30.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "e0bb0e0911ad4a70cab880cdd6287fe1e880a1a9d8e4e6defa8e9044b9796a6c" 130 | dependencies = [ 131 | "anchor-syn", 132 | "borsh-derive-internal 0.10.4", 133 | "proc-macro2", 134 | "quote", 135 | "syn 1.0.109", 136 | ] 137 | 138 | [[package]] 139 | name = "anchor-derive-space" 140 | version = "0.30.1" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "5ef415ff156dc82e9ecb943189b0cb241b3a6bfc26a180234dc21bd3ef3ce0cb" 143 | dependencies = [ 144 | "proc-macro2", 145 | "quote", 146 | "syn 1.0.109", 147 | ] 148 | 149 | [[package]] 150 | name = "anchor-lang" 151 | version = "0.30.1" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "6620c9486d9d36a4389cab5e37dc34a42ed0bfaa62e6a75a2999ce98f8f2e373" 154 | dependencies = [ 155 | "anchor-attribute-access-control", 156 | "anchor-attribute-account", 157 | "anchor-attribute-constant", 158 | "anchor-attribute-error", 159 | "anchor-attribute-event", 160 | "anchor-attribute-program", 161 | "anchor-derive-accounts", 162 | "anchor-derive-serde", 163 | "anchor-derive-space", 164 | "anchor-lang-idl", 165 | "arrayref", 166 | "base64 0.21.7", 167 | "bincode", 168 | "borsh 0.10.4", 169 | "bytemuck", 170 | "getrandom 0.2.15", 171 | "solana-program", 172 | "thiserror", 173 | ] 174 | 175 | [[package]] 176 | name = "anchor-lang-idl" 177 | version = "0.1.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "31cf97b4e6f7d6144a05e435660fcf757dbc3446d38d0e2b851d11ed13625bba" 180 | dependencies = [ 181 | "anchor-lang-idl-spec", 182 | "anyhow", 183 | "heck", 184 | "regex", 185 | "serde", 186 | "serde_json", 187 | "sha2 0.10.8", 188 | ] 189 | 190 | [[package]] 191 | name = "anchor-lang-idl-spec" 192 | version = "0.1.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "2bdf143115440fe621bdac3a29a1f7472e09f6cd82b2aa569429a0c13f103838" 195 | dependencies = [ 196 | "anyhow", 197 | "serde", 198 | ] 199 | 200 | [[package]] 201 | name = "anchor-syn" 202 | version = "0.30.1" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "f99daacb53b55cfd37ce14d6c9905929721137fd4c67bbab44a19802aecb622f" 205 | dependencies = [ 206 | "anyhow", 207 | "bs58 0.5.1", 208 | "cargo_toml", 209 | "heck", 210 | "proc-macro2", 211 | "quote", 212 | "serde", 213 | "serde_json", 214 | "sha2 0.10.8", 215 | "syn 1.0.109", 216 | "thiserror", 217 | ] 218 | 219 | [[package]] 220 | name = "anyhow" 221 | version = "1.0.93" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" 224 | 225 | [[package]] 226 | name = "ark-bn254" 227 | version = "0.4.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" 230 | dependencies = [ 231 | "ark-ec", 232 | "ark-ff", 233 | "ark-std", 234 | ] 235 | 236 | [[package]] 237 | name = "ark-ec" 238 | version = "0.4.2" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" 241 | dependencies = [ 242 | "ark-ff", 243 | "ark-poly", 244 | "ark-serialize", 245 | "ark-std", 246 | "derivative", 247 | "hashbrown 0.13.2", 248 | "itertools", 249 | "num-traits", 250 | "zeroize", 251 | ] 252 | 253 | [[package]] 254 | name = "ark-ff" 255 | version = "0.4.2" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" 258 | dependencies = [ 259 | "ark-ff-asm", 260 | "ark-ff-macros", 261 | "ark-serialize", 262 | "ark-std", 263 | "derivative", 264 | "digest 0.10.7", 265 | "itertools", 266 | "num-bigint", 267 | "num-traits", 268 | "paste", 269 | "rustc_version", 270 | "zeroize", 271 | ] 272 | 273 | [[package]] 274 | name = "ark-ff-asm" 275 | version = "0.4.2" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" 278 | dependencies = [ 279 | "quote", 280 | "syn 1.0.109", 281 | ] 282 | 283 | [[package]] 284 | name = "ark-ff-macros" 285 | version = "0.4.2" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" 288 | dependencies = [ 289 | "num-bigint", 290 | "num-traits", 291 | "proc-macro2", 292 | "quote", 293 | "syn 1.0.109", 294 | ] 295 | 296 | [[package]] 297 | name = "ark-poly" 298 | version = "0.4.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" 301 | dependencies = [ 302 | "ark-ff", 303 | "ark-serialize", 304 | "ark-std", 305 | "derivative", 306 | "hashbrown 0.13.2", 307 | ] 308 | 309 | [[package]] 310 | name = "ark-serialize" 311 | version = "0.4.2" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" 314 | dependencies = [ 315 | "ark-serialize-derive", 316 | "ark-std", 317 | "digest 0.10.7", 318 | "num-bigint", 319 | ] 320 | 321 | [[package]] 322 | name = "ark-serialize-derive" 323 | version = "0.4.2" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" 326 | dependencies = [ 327 | "proc-macro2", 328 | "quote", 329 | "syn 1.0.109", 330 | ] 331 | 332 | [[package]] 333 | name = "ark-std" 334 | version = "0.4.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" 337 | dependencies = [ 338 | "num-traits", 339 | "rand 0.8.5", 340 | ] 341 | 342 | [[package]] 343 | name = "arrayref" 344 | version = "0.3.9" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 347 | 348 | [[package]] 349 | name = "arrayvec" 350 | version = "0.7.6" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 353 | 354 | [[package]] 355 | name = "autocfg" 356 | version = "1.4.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 359 | 360 | [[package]] 361 | name = "base64" 362 | version = "0.12.3" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 365 | 366 | [[package]] 367 | name = "base64" 368 | version = "0.21.7" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 371 | 372 | [[package]] 373 | name = "bincode" 374 | version = "1.3.3" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 377 | dependencies = [ 378 | "serde", 379 | ] 380 | 381 | [[package]] 382 | name = "bitflags" 383 | version = "2.6.0" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 386 | dependencies = [ 387 | "serde", 388 | ] 389 | 390 | [[package]] 391 | name = "bitmaps" 392 | version = "2.1.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 395 | dependencies = [ 396 | "typenum", 397 | ] 398 | 399 | [[package]] 400 | name = "blake3" 401 | version = "1.5.1" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" 404 | dependencies = [ 405 | "arrayref", 406 | "arrayvec", 407 | "cc", 408 | "cfg-if", 409 | "constant_time_eq", 410 | "digest 0.10.7", 411 | ] 412 | 413 | [[package]] 414 | name = "block-buffer" 415 | version = "0.9.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 418 | dependencies = [ 419 | "generic-array", 420 | ] 421 | 422 | [[package]] 423 | name = "block-buffer" 424 | version = "0.10.4" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 427 | dependencies = [ 428 | "generic-array", 429 | ] 430 | 431 | [[package]] 432 | name = "bolt-attribute-bolt-arguments" 433 | version = "0.1.10" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "d08d09a4b4d5dc77a0949d64b206e9af94cfb3106d1dfd9e97716c04268298e4" 436 | dependencies = [ 437 | "proc-macro2", 438 | "quote", 439 | "syn 1.0.109", 440 | ] 441 | 442 | [[package]] 443 | name = "bolt-attribute-bolt-component" 444 | version = "0.1.10" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "813f9007e64bc2d57dcb100c13bfd7192c3e7f983a99f8b2f0cb5cb8df298d2f" 447 | dependencies = [ 448 | "bolt-utils", 449 | "proc-macro2", 450 | "quote", 451 | "syn 1.0.109", 452 | ] 453 | 454 | [[package]] 455 | name = "bolt-attribute-bolt-component-deserialize" 456 | version = "0.1.10" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "cde0ce2e092effd25e4b9da42203a4045f690bd5512b496a6b83dab216d14d75" 459 | dependencies = [ 460 | "bolt-utils", 461 | "proc-macro2", 462 | "quote", 463 | "syn 1.0.109", 464 | ] 465 | 466 | [[package]] 467 | name = "bolt-attribute-bolt-component-id" 468 | version = "0.1.10" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "6742f9435959058ebdf6b17bb8d21d20145ec24be2e2e77714fe152a06ba96bc" 471 | dependencies = [ 472 | "proc-macro2", 473 | "quote", 474 | "syn 1.0.109", 475 | ] 476 | 477 | [[package]] 478 | name = "bolt-attribute-bolt-delegate" 479 | version = "0.1.10" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "83afc910ded978177a13327c527d51e52723548af257a4468c650215b3d448be" 482 | dependencies = [ 483 | "proc-macro2", 484 | "quote", 485 | "syn 1.0.109", 486 | ] 487 | 488 | [[package]] 489 | name = "bolt-attribute-bolt-extra-accounts" 490 | version = "0.1.10" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "614e2a5c1a442244bc1a77395966eb1fc8264bee751bf548e20cb86674e7ad85" 493 | dependencies = [ 494 | "proc-macro2", 495 | "quote", 496 | "syn 1.0.109", 497 | ] 498 | 499 | [[package]] 500 | name = "bolt-attribute-bolt-program" 501 | version = "0.1.10" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "f49753dc6d1636ab3138d8a26e2923d847ade2f6957e4928cd7d99ad4ab0044b" 504 | dependencies = [ 505 | "proc-macro2", 506 | "quote", 507 | "syn 1.0.109", 508 | ] 509 | 510 | [[package]] 511 | name = "bolt-attribute-bolt-system" 512 | version = "0.1.10" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "2a2eb40bd76a7c580a45c0bcebbeed93c74372705ccecc610426048072fe8fc0" 515 | dependencies = [ 516 | "proc-macro2", 517 | "quote", 518 | "syn 1.0.109", 519 | ] 520 | 521 | [[package]] 522 | name = "bolt-attribute-bolt-system-input" 523 | version = "0.1.10" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "13b4c0d92d53b9a2676857eb197d8b8b46802667093dbb1ca5e64ecdae3894a8" 526 | dependencies = [ 527 | "proc-macro2", 528 | "quote", 529 | "syn 1.0.109", 530 | ] 531 | 532 | [[package]] 533 | name = "bolt-component" 534 | version = "0.1.10" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "006ba475d978950843e40918932c7b6b3209707066c9fdebbbdb368d855c2dc6" 537 | dependencies = [ 538 | "anchor-lang", 539 | "bolt-system", 540 | ] 541 | 542 | [[package]] 543 | name = "bolt-helpers-system-template" 544 | version = "0.1.10" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "aade36978d7dde0d3a6e7fc83babbd3d3efd9ee99e7e15e6f7fffccd68a6051d" 547 | dependencies = [ 548 | "proc-macro2", 549 | "quote", 550 | "syn 1.0.109", 551 | ] 552 | 553 | [[package]] 554 | name = "bolt-helpers-world-apply" 555 | version = "0.1.10" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "716c126888affc2bf771709d7a7147ec2b0c07d3c46586496f20add03853c3f6" 558 | dependencies = [ 559 | "proc-macro2", 560 | "quote", 561 | "syn 1.0.109", 562 | ] 563 | 564 | [[package]] 565 | name = "bolt-lang" 566 | version = "0.1.10" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "aede7e8121d87063bf95b52fec38c9d0bc9d5e20ff00282191b78e33a90670b5" 569 | dependencies = [ 570 | "ahash 0.8.11", 571 | "anchor-lang", 572 | "bolt-attribute-bolt-arguments", 573 | "bolt-attribute-bolt-component", 574 | "bolt-attribute-bolt-component-deserialize", 575 | "bolt-attribute-bolt-component-id", 576 | "bolt-attribute-bolt-delegate", 577 | "bolt-attribute-bolt-extra-accounts", 578 | "bolt-attribute-bolt-program", 579 | "bolt-attribute-bolt-system", 580 | "bolt-attribute-bolt-system-input", 581 | "bolt-system", 582 | "ephemeral-rollups-sdk", 583 | "serde", 584 | "serde_json", 585 | "world", 586 | ] 587 | 588 | [[package]] 589 | name = "bolt-system" 590 | version = "0.1.10" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "fe61db6cab57fddf9791eeaa20e8891f23886e63c5c3a828ad3b373244de2cfa" 593 | dependencies = [ 594 | "anchor-lang", 595 | "bolt-helpers-system-template", 596 | ] 597 | 598 | [[package]] 599 | name = "bolt-utils" 600 | version = "0.1.10" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "e1bf2f74f053143b0f83ab22cf53619c4967d4f1cf94c859d9766162b75b9b3e" 603 | dependencies = [ 604 | "proc-macro2", 605 | "quote", 606 | "syn 1.0.109", 607 | ] 608 | 609 | [[package]] 610 | name = "borsh" 611 | version = "0.9.3" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" 614 | dependencies = [ 615 | "borsh-derive 0.9.3", 616 | "hashbrown 0.11.2", 617 | ] 618 | 619 | [[package]] 620 | name = "borsh" 621 | version = "0.10.4" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" 624 | dependencies = [ 625 | "borsh-derive 0.10.4", 626 | "hashbrown 0.13.2", 627 | ] 628 | 629 | [[package]] 630 | name = "borsh" 631 | version = "1.5.2" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "f5327f6c99920069d1fe374aa743be1af0031dea9f250852cdf1ae6a0861ee24" 634 | dependencies = [ 635 | "borsh-derive 1.5.2", 636 | "cfg_aliases", 637 | ] 638 | 639 | [[package]] 640 | name = "borsh-derive" 641 | version = "0.9.3" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" 644 | dependencies = [ 645 | "borsh-derive-internal 0.9.3", 646 | "borsh-schema-derive-internal 0.9.3", 647 | "proc-macro-crate 0.1.5", 648 | "proc-macro2", 649 | "syn 1.0.109", 650 | ] 651 | 652 | [[package]] 653 | name = "borsh-derive" 654 | version = "0.10.4" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" 657 | dependencies = [ 658 | "borsh-derive-internal 0.10.4", 659 | "borsh-schema-derive-internal 0.10.4", 660 | "proc-macro-crate 0.1.5", 661 | "proc-macro2", 662 | "syn 1.0.109", 663 | ] 664 | 665 | [[package]] 666 | name = "borsh-derive" 667 | version = "1.5.2" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "10aedd8f1a81a8aafbfde924b0e3061cd6fedd6f6bbcfc6a76e6fd426d7bfe26" 670 | dependencies = [ 671 | "once_cell", 672 | "proc-macro-crate 3.2.0", 673 | "proc-macro2", 674 | "quote", 675 | "syn 2.0.87", 676 | ] 677 | 678 | [[package]] 679 | name = "borsh-derive-internal" 680 | version = "0.9.3" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" 683 | dependencies = [ 684 | "proc-macro2", 685 | "quote", 686 | "syn 1.0.109", 687 | ] 688 | 689 | [[package]] 690 | name = "borsh-derive-internal" 691 | version = "0.10.4" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" 694 | dependencies = [ 695 | "proc-macro2", 696 | "quote", 697 | "syn 1.0.109", 698 | ] 699 | 700 | [[package]] 701 | name = "borsh-schema-derive-internal" 702 | version = "0.9.3" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" 705 | dependencies = [ 706 | "proc-macro2", 707 | "quote", 708 | "syn 1.0.109", 709 | ] 710 | 711 | [[package]] 712 | name = "borsh-schema-derive-internal" 713 | version = "0.10.4" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" 716 | dependencies = [ 717 | "proc-macro2", 718 | "quote", 719 | "syn 1.0.109", 720 | ] 721 | 722 | [[package]] 723 | name = "bs58" 724 | version = "0.4.0" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" 727 | 728 | [[package]] 729 | name = "bs58" 730 | version = "0.5.1" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" 733 | dependencies = [ 734 | "tinyvec", 735 | ] 736 | 737 | [[package]] 738 | name = "bumpalo" 739 | version = "3.16.0" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 742 | 743 | [[package]] 744 | name = "bv" 745 | version = "0.11.1" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" 748 | dependencies = [ 749 | "feature-probe", 750 | "serde", 751 | ] 752 | 753 | [[package]] 754 | name = "bytemuck" 755 | version = "1.19.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" 758 | dependencies = [ 759 | "bytemuck_derive", 760 | ] 761 | 762 | [[package]] 763 | name = "bytemuck_derive" 764 | version = "1.8.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" 767 | dependencies = [ 768 | "proc-macro2", 769 | "quote", 770 | "syn 2.0.87", 771 | ] 772 | 773 | [[package]] 774 | name = "byteorder" 775 | version = "1.5.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 778 | 779 | [[package]] 780 | name = "cargo_toml" 781 | version = "0.19.2" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" 784 | dependencies = [ 785 | "serde", 786 | "toml 0.8.19", 787 | ] 788 | 789 | [[package]] 790 | name = "cc" 791 | version = "1.1.36" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" 794 | dependencies = [ 795 | "jobserver", 796 | "libc", 797 | "shlex", 798 | ] 799 | 800 | [[package]] 801 | name = "cfg-if" 802 | version = "1.0.0" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 805 | 806 | [[package]] 807 | name = "cfg_aliases" 808 | version = "0.2.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 811 | 812 | [[package]] 813 | name = "command" 814 | version = "0.1.6" 815 | dependencies = [ 816 | "anchor-lang", 817 | "bolt-lang", 818 | "game", 819 | "serde", 820 | ] 821 | 822 | [[package]] 823 | name = "console_error_panic_hook" 824 | version = "0.1.7" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 827 | dependencies = [ 828 | "cfg-if", 829 | "wasm-bindgen", 830 | ] 831 | 832 | [[package]] 833 | name = "console_log" 834 | version = "0.2.2" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" 837 | dependencies = [ 838 | "log", 839 | "web-sys", 840 | ] 841 | 842 | [[package]] 843 | name = "constant_time_eq" 844 | version = "0.3.1" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" 847 | 848 | [[package]] 849 | name = "cpufeatures" 850 | version = "0.2.14" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" 853 | dependencies = [ 854 | "libc", 855 | ] 856 | 857 | [[package]] 858 | name = "crossbeam-deque" 859 | version = "0.8.5" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 862 | dependencies = [ 863 | "crossbeam-epoch", 864 | "crossbeam-utils", 865 | ] 866 | 867 | [[package]] 868 | name = "crossbeam-epoch" 869 | version = "0.9.18" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 872 | dependencies = [ 873 | "crossbeam-utils", 874 | ] 875 | 876 | [[package]] 877 | name = "crossbeam-utils" 878 | version = "0.8.20" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 881 | 882 | [[package]] 883 | name = "crunchy" 884 | version = "0.2.2" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 887 | 888 | [[package]] 889 | name = "crypto-common" 890 | version = "0.1.6" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 893 | dependencies = [ 894 | "generic-array", 895 | "typenum", 896 | ] 897 | 898 | [[package]] 899 | name = "crypto-mac" 900 | version = "0.8.0" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" 903 | dependencies = [ 904 | "generic-array", 905 | "subtle", 906 | ] 907 | 908 | [[package]] 909 | name = "curve25519-dalek" 910 | version = "3.2.1" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" 913 | dependencies = [ 914 | "byteorder", 915 | "digest 0.9.0", 916 | "rand_core 0.5.1", 917 | "serde", 918 | "subtle", 919 | "zeroize", 920 | ] 921 | 922 | [[package]] 923 | name = "derivative" 924 | version = "2.2.0" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 927 | dependencies = [ 928 | "proc-macro2", 929 | "quote", 930 | "syn 1.0.109", 931 | ] 932 | 933 | [[package]] 934 | name = "digest" 935 | version = "0.9.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 938 | dependencies = [ 939 | "generic-array", 940 | ] 941 | 942 | [[package]] 943 | name = "digest" 944 | version = "0.10.7" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 947 | dependencies = [ 948 | "block-buffer 0.10.4", 949 | "crypto-common", 950 | "subtle", 951 | ] 952 | 953 | [[package]] 954 | name = "either" 955 | version = "1.13.0" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 958 | 959 | [[package]] 960 | name = "ephemeral-rollups-sdk" 961 | version = "0.0.6" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "78d083d793a467350cb76312a1281e9bc6a0083823102dee8f99e1117b668fb6" 964 | dependencies = [ 965 | "anchor-lang", 966 | "borsh 0.10.4", 967 | "ephemeral-rollups-sdk-attribute-commit", 968 | "ephemeral-rollups-sdk-attribute-delegate", 969 | "paste", 970 | "solana-program", 971 | ] 972 | 973 | [[package]] 974 | name = "ephemeral-rollups-sdk-attribute-commit" 975 | version = "0.0.6" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "82c0c885becc3dba2ffacd1d121b1f54bdb061a62549f4fe1f2982d4535b0a66" 978 | dependencies = [ 979 | "proc-macro2", 980 | "quote", 981 | "syn 1.0.109", 982 | ] 983 | 984 | [[package]] 985 | name = "ephemeral-rollups-sdk-attribute-delegate" 986 | version = "0.0.6" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "52e08894bd8218c86414fa637dad53c8cccddfab0f5ee81f3d70b79745e084cb" 989 | dependencies = [ 990 | "proc-macro2", 991 | "quote", 992 | "syn 1.0.109", 993 | ] 994 | 995 | [[package]] 996 | name = "equivalent" 997 | version = "1.0.1" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 1000 | 1001 | [[package]] 1002 | name = "feature-probe" 1003 | version = "0.1.1" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" 1006 | 1007 | [[package]] 1008 | name = "finish" 1009 | version = "0.1.6" 1010 | dependencies = [ 1011 | "anchor-lang", 1012 | "bolt-lang", 1013 | "game", 1014 | "serde", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "game" 1019 | version = "0.1.6" 1020 | dependencies = [ 1021 | "anchor-lang", 1022 | "bolt-lang", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "generate" 1027 | version = "0.1.6" 1028 | dependencies = [ 1029 | "anchor-lang", 1030 | "bolt-lang", 1031 | "game", 1032 | "serde", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "generic-array" 1037 | version = "0.14.7" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 1040 | dependencies = [ 1041 | "serde", 1042 | "typenum", 1043 | "version_check", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "getrandom" 1048 | version = "0.1.16" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 1051 | dependencies = [ 1052 | "cfg-if", 1053 | "js-sys", 1054 | "libc", 1055 | "wasi 0.9.0+wasi-snapshot-preview1", 1056 | "wasm-bindgen", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "getrandom" 1061 | version = "0.2.15" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 1064 | dependencies = [ 1065 | "cfg-if", 1066 | "js-sys", 1067 | "libc", 1068 | "wasi 0.11.0+wasi-snapshot-preview1", 1069 | "wasm-bindgen", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "hashbrown" 1074 | version = "0.11.2" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 1077 | dependencies = [ 1078 | "ahash 0.7.8", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "hashbrown" 1083 | version = "0.13.2" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 1086 | dependencies = [ 1087 | "ahash 0.8.11", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "hashbrown" 1092 | version = "0.15.1" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" 1095 | 1096 | [[package]] 1097 | name = "heck" 1098 | version = "0.3.3" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 1101 | dependencies = [ 1102 | "unicode-segmentation", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "hmac" 1107 | version = "0.8.1" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" 1110 | dependencies = [ 1111 | "crypto-mac", 1112 | "digest 0.9.0", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "hmac-drbg" 1117 | version = "0.3.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" 1120 | dependencies = [ 1121 | "digest 0.9.0", 1122 | "generic-array", 1123 | "hmac", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "im" 1128 | version = "15.1.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" 1131 | dependencies = [ 1132 | "bitmaps", 1133 | "rand_core 0.6.4", 1134 | "rand_xoshiro", 1135 | "rayon", 1136 | "serde", 1137 | "sized-chunks", 1138 | "typenum", 1139 | "version_check", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "indexmap" 1144 | version = "2.6.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 1147 | dependencies = [ 1148 | "equivalent", 1149 | "hashbrown 0.15.1", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "itertools" 1154 | version = "0.10.5" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 1157 | dependencies = [ 1158 | "either", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "itoa" 1163 | version = "1.0.11" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1166 | 1167 | [[package]] 1168 | name = "jobserver" 1169 | version = "0.1.32" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 1172 | dependencies = [ 1173 | "libc", 1174 | ] 1175 | 1176 | [[package]] 1177 | name = "join" 1178 | version = "0.1.6" 1179 | dependencies = [ 1180 | "anchor-lang", 1181 | "bolt-lang", 1182 | "game", 1183 | "serde", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "js-sys" 1188 | version = "0.3.72" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 1191 | dependencies = [ 1192 | "wasm-bindgen", 1193 | ] 1194 | 1195 | [[package]] 1196 | name = "keccak" 1197 | version = "0.1.5" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" 1200 | dependencies = [ 1201 | "cpufeatures", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "lazy_static" 1206 | version = "1.5.0" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1209 | 1210 | [[package]] 1211 | name = "libc" 1212 | version = "0.2.162" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" 1215 | 1216 | [[package]] 1217 | name = "libsecp256k1" 1218 | version = "0.6.0" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" 1221 | dependencies = [ 1222 | "arrayref", 1223 | "base64 0.12.3", 1224 | "digest 0.9.0", 1225 | "hmac-drbg", 1226 | "libsecp256k1-core", 1227 | "libsecp256k1-gen-ecmult", 1228 | "libsecp256k1-gen-genmult", 1229 | "rand 0.7.3", 1230 | "serde", 1231 | "sha2 0.9.9", 1232 | "typenum", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "libsecp256k1-core" 1237 | version = "0.2.2" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" 1240 | dependencies = [ 1241 | "crunchy", 1242 | "digest 0.9.0", 1243 | "subtle", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "libsecp256k1-gen-ecmult" 1248 | version = "0.2.1" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" 1251 | dependencies = [ 1252 | "libsecp256k1-core", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "libsecp256k1-gen-genmult" 1257 | version = "0.2.1" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" 1260 | dependencies = [ 1261 | "libsecp256k1-core", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "light-poseidon" 1266 | version = "0.2.0" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" 1269 | dependencies = [ 1270 | "ark-bn254", 1271 | "ark-ff", 1272 | "num-bigint", 1273 | "thiserror", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "lock_api" 1278 | version = "0.4.12" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1281 | dependencies = [ 1282 | "autocfg", 1283 | "scopeguard", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "log" 1288 | version = "0.4.22" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1291 | 1292 | [[package]] 1293 | name = "memchr" 1294 | version = "2.7.4" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1297 | 1298 | [[package]] 1299 | name = "memmap2" 1300 | version = "0.5.10" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" 1303 | dependencies = [ 1304 | "libc", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "memoffset" 1309 | version = "0.9.1" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 1312 | dependencies = [ 1313 | "autocfg", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "num-bigint" 1318 | version = "0.4.6" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1321 | dependencies = [ 1322 | "num-integer", 1323 | "num-traits", 1324 | ] 1325 | 1326 | [[package]] 1327 | name = "num-derive" 1328 | version = "0.4.2" 1329 | source = "registry+https://github.com/rust-lang/crates.io-index" 1330 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 1331 | dependencies = [ 1332 | "proc-macro2", 1333 | "quote", 1334 | "syn 2.0.87", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "num-integer" 1339 | version = "0.1.46" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1342 | dependencies = [ 1343 | "num-traits", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "num-traits" 1348 | version = "0.2.19" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1351 | dependencies = [ 1352 | "autocfg", 1353 | ] 1354 | 1355 | [[package]] 1356 | name = "once_cell" 1357 | version = "1.20.2" 1358 | source = "registry+https://github.com/rust-lang/crates.io-index" 1359 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 1360 | 1361 | [[package]] 1362 | name = "opaque-debug" 1363 | version = "0.3.1" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 1366 | 1367 | [[package]] 1368 | name = "parking_lot" 1369 | version = "0.12.3" 1370 | source = "registry+https://github.com/rust-lang/crates.io-index" 1371 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1372 | dependencies = [ 1373 | "lock_api", 1374 | "parking_lot_core", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "parking_lot_core" 1379 | version = "0.9.10" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1382 | dependencies = [ 1383 | "cfg-if", 1384 | "libc", 1385 | "redox_syscall", 1386 | "smallvec", 1387 | "windows-targets", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "paste" 1392 | version = "1.0.15" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1395 | 1396 | [[package]] 1397 | name = "pbkdf2" 1398 | version = "0.4.0" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" 1401 | dependencies = [ 1402 | "crypto-mac", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "ppv-lite86" 1407 | version = "0.2.20" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1410 | dependencies = [ 1411 | "zerocopy", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "proc-macro-crate" 1416 | version = "0.1.5" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 1419 | dependencies = [ 1420 | "toml 0.5.11", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "proc-macro-crate" 1425 | version = "3.2.0" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 1428 | dependencies = [ 1429 | "toml_edit", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "proc-macro2" 1434 | version = "1.0.89" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 1437 | dependencies = [ 1438 | "unicode-ident", 1439 | ] 1440 | 1441 | [[package]] 1442 | name = "quote" 1443 | version = "1.0.37" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1446 | dependencies = [ 1447 | "proc-macro2", 1448 | ] 1449 | 1450 | [[package]] 1451 | name = "rand" 1452 | version = "0.7.3" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1455 | dependencies = [ 1456 | "getrandom 0.1.16", 1457 | "libc", 1458 | "rand_chacha 0.2.2", 1459 | "rand_core 0.5.1", 1460 | "rand_hc", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "rand" 1465 | version = "0.8.5" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1468 | dependencies = [ 1469 | "libc", 1470 | "rand_chacha 0.3.1", 1471 | "rand_core 0.6.4", 1472 | ] 1473 | 1474 | [[package]] 1475 | name = "rand_chacha" 1476 | version = "0.2.2" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1479 | dependencies = [ 1480 | "ppv-lite86", 1481 | "rand_core 0.5.1", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "rand_chacha" 1486 | version = "0.3.1" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1489 | dependencies = [ 1490 | "ppv-lite86", 1491 | "rand_core 0.6.4", 1492 | ] 1493 | 1494 | [[package]] 1495 | name = "rand_core" 1496 | version = "0.5.1" 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" 1498 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1499 | dependencies = [ 1500 | "getrandom 0.1.16", 1501 | ] 1502 | 1503 | [[package]] 1504 | name = "rand_core" 1505 | version = "0.6.4" 1506 | source = "registry+https://github.com/rust-lang/crates.io-index" 1507 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1508 | dependencies = [ 1509 | "getrandom 0.2.15", 1510 | ] 1511 | 1512 | [[package]] 1513 | name = "rand_hc" 1514 | version = "0.2.0" 1515 | source = "registry+https://github.com/rust-lang/crates.io-index" 1516 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1517 | dependencies = [ 1518 | "rand_core 0.5.1", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "rand_xoshiro" 1523 | version = "0.6.0" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 1526 | dependencies = [ 1527 | "rand_core 0.6.4", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "rayon" 1532 | version = "1.10.0" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1535 | dependencies = [ 1536 | "either", 1537 | "rayon-core", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "rayon-core" 1542 | version = "1.12.1" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1545 | dependencies = [ 1546 | "crossbeam-deque", 1547 | "crossbeam-utils", 1548 | ] 1549 | 1550 | [[package]] 1551 | name = "redox_syscall" 1552 | version = "0.5.7" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1555 | dependencies = [ 1556 | "bitflags", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "regex" 1561 | version = "1.11.1" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1564 | dependencies = [ 1565 | "aho-corasick", 1566 | "memchr", 1567 | "regex-automata", 1568 | "regex-syntax", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "regex-automata" 1573 | version = "0.4.8" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 1576 | dependencies = [ 1577 | "aho-corasick", 1578 | "memchr", 1579 | "regex-syntax", 1580 | ] 1581 | 1582 | [[package]] 1583 | name = "regex-syntax" 1584 | version = "0.8.5" 1585 | source = "registry+https://github.com/rust-lang/crates.io-index" 1586 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1587 | 1588 | [[package]] 1589 | name = "rustc-hash" 1590 | version = "1.1.0" 1591 | source = "registry+https://github.com/rust-lang/crates.io-index" 1592 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1593 | 1594 | [[package]] 1595 | name = "rustc_version" 1596 | version = "0.4.1" 1597 | source = "registry+https://github.com/rust-lang/crates.io-index" 1598 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1599 | dependencies = [ 1600 | "semver", 1601 | ] 1602 | 1603 | [[package]] 1604 | name = "rustversion" 1605 | version = "1.0.18" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" 1608 | 1609 | [[package]] 1610 | name = "ryu" 1611 | version = "1.0.18" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1614 | 1615 | [[package]] 1616 | name = "scopeguard" 1617 | version = "1.2.0" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1620 | 1621 | [[package]] 1622 | name = "semver" 1623 | version = "1.0.23" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1626 | 1627 | [[package]] 1628 | name = "serde" 1629 | version = "1.0.214" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" 1632 | dependencies = [ 1633 | "serde_derive", 1634 | ] 1635 | 1636 | [[package]] 1637 | name = "serde_bytes" 1638 | version = "0.11.15" 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" 1640 | checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" 1641 | dependencies = [ 1642 | "serde", 1643 | ] 1644 | 1645 | [[package]] 1646 | name = "serde_derive" 1647 | version = "1.0.214" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" 1650 | dependencies = [ 1651 | "proc-macro2", 1652 | "quote", 1653 | "syn 2.0.87", 1654 | ] 1655 | 1656 | [[package]] 1657 | name = "serde_json" 1658 | version = "1.0.132" 1659 | source = "registry+https://github.com/rust-lang/crates.io-index" 1660 | checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" 1661 | dependencies = [ 1662 | "itoa", 1663 | "memchr", 1664 | "ryu", 1665 | "serde", 1666 | ] 1667 | 1668 | [[package]] 1669 | name = "serde_spanned" 1670 | version = "0.6.8" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 1673 | dependencies = [ 1674 | "serde", 1675 | ] 1676 | 1677 | [[package]] 1678 | name = "sha2" 1679 | version = "0.9.9" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" 1682 | dependencies = [ 1683 | "block-buffer 0.9.0", 1684 | "cfg-if", 1685 | "cpufeatures", 1686 | "digest 0.9.0", 1687 | "opaque-debug", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "sha2" 1692 | version = "0.10.8" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1695 | dependencies = [ 1696 | "cfg-if", 1697 | "cpufeatures", 1698 | "digest 0.10.7", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "sha3" 1703 | version = "0.10.8" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" 1706 | dependencies = [ 1707 | "digest 0.10.7", 1708 | "keccak", 1709 | ] 1710 | 1711 | [[package]] 1712 | name = "shlex" 1713 | version = "1.3.0" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1716 | 1717 | [[package]] 1718 | name = "sized-chunks" 1719 | version = "0.6.5" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 1722 | dependencies = [ 1723 | "bitmaps", 1724 | "typenum", 1725 | ] 1726 | 1727 | [[package]] 1728 | name = "smallvec" 1729 | version = "1.13.2" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1732 | 1733 | [[package]] 1734 | name = "solana-frozen-abi" 1735 | version = "1.18.26" 1736 | source = "registry+https://github.com/rust-lang/crates.io-index" 1737 | checksum = "03ab2c30c15311b511c0d1151e4ab6bc9a3e080a37e7c6e7c2d96f5784cf9434" 1738 | dependencies = [ 1739 | "block-buffer 0.10.4", 1740 | "bs58 0.4.0", 1741 | "bv", 1742 | "either", 1743 | "generic-array", 1744 | "im", 1745 | "lazy_static", 1746 | "log", 1747 | "memmap2", 1748 | "rustc_version", 1749 | "serde", 1750 | "serde_bytes", 1751 | "serde_derive", 1752 | "sha2 0.10.8", 1753 | "solana-frozen-abi-macro", 1754 | "subtle", 1755 | "thiserror", 1756 | ] 1757 | 1758 | [[package]] 1759 | name = "solana-frozen-abi-macro" 1760 | version = "1.18.26" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "c142f779c3633ac83c84d04ff06c70e1f558c876f13358bed77ba629c7417932" 1763 | dependencies = [ 1764 | "proc-macro2", 1765 | "quote", 1766 | "rustc_version", 1767 | "syn 2.0.87", 1768 | ] 1769 | 1770 | [[package]] 1771 | name = "solana-program" 1772 | version = "1.18.26" 1773 | source = "registry+https://github.com/rust-lang/crates.io-index" 1774 | checksum = "c10f4588cefd716b24a1a40dd32c278e43a560ab8ce4de6b5805c9d113afdfa1" 1775 | dependencies = [ 1776 | "ark-bn254", 1777 | "ark-ec", 1778 | "ark-ff", 1779 | "ark-serialize", 1780 | "base64 0.21.7", 1781 | "bincode", 1782 | "bitflags", 1783 | "blake3", 1784 | "borsh 0.10.4", 1785 | "borsh 0.9.3", 1786 | "borsh 1.5.2", 1787 | "bs58 0.4.0", 1788 | "bv", 1789 | "bytemuck", 1790 | "cc", 1791 | "console_error_panic_hook", 1792 | "console_log", 1793 | "curve25519-dalek", 1794 | "getrandom 0.2.15", 1795 | "itertools", 1796 | "js-sys", 1797 | "lazy_static", 1798 | "libc", 1799 | "libsecp256k1", 1800 | "light-poseidon", 1801 | "log", 1802 | "memoffset", 1803 | "num-bigint", 1804 | "num-derive", 1805 | "num-traits", 1806 | "parking_lot", 1807 | "rand 0.8.5", 1808 | "rustc_version", 1809 | "rustversion", 1810 | "serde", 1811 | "serde_bytes", 1812 | "serde_derive", 1813 | "serde_json", 1814 | "sha2 0.10.8", 1815 | "sha3", 1816 | "solana-frozen-abi", 1817 | "solana-frozen-abi-macro", 1818 | "solana-sdk-macro", 1819 | "thiserror", 1820 | "tiny-bip39", 1821 | "wasm-bindgen", 1822 | "zeroize", 1823 | ] 1824 | 1825 | [[package]] 1826 | name = "solana-sdk-macro" 1827 | version = "1.18.26" 1828 | source = "registry+https://github.com/rust-lang/crates.io-index" 1829 | checksum = "1b75d0f193a27719257af19144fdaebec0415d1c9e9226ae4bd29b791be5e9bd" 1830 | dependencies = [ 1831 | "bs58 0.4.0", 1832 | "proc-macro2", 1833 | "quote", 1834 | "rustversion", 1835 | "syn 2.0.87", 1836 | ] 1837 | 1838 | [[package]] 1839 | name = "solana-security-txt" 1840 | version = "1.1.1" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" 1843 | 1844 | [[package]] 1845 | name = "start" 1846 | version = "0.1.6" 1847 | dependencies = [ 1848 | "anchor-lang", 1849 | "bolt-lang", 1850 | "game", 1851 | "serde", 1852 | ] 1853 | 1854 | [[package]] 1855 | name = "subtle" 1856 | version = "2.6.1" 1857 | source = "registry+https://github.com/rust-lang/crates.io-index" 1858 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1859 | 1860 | [[package]] 1861 | name = "syn" 1862 | version = "1.0.109" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1865 | dependencies = [ 1866 | "proc-macro2", 1867 | "quote", 1868 | "unicode-ident", 1869 | ] 1870 | 1871 | [[package]] 1872 | name = "syn" 1873 | version = "2.0.87" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 1876 | dependencies = [ 1877 | "proc-macro2", 1878 | "quote", 1879 | "unicode-ident", 1880 | ] 1881 | 1882 | [[package]] 1883 | name = "thiserror" 1884 | version = "1.0.68" 1885 | source = "registry+https://github.com/rust-lang/crates.io-index" 1886 | checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" 1887 | dependencies = [ 1888 | "thiserror-impl", 1889 | ] 1890 | 1891 | [[package]] 1892 | name = "thiserror-impl" 1893 | version = "1.0.68" 1894 | source = "registry+https://github.com/rust-lang/crates.io-index" 1895 | checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" 1896 | dependencies = [ 1897 | "proc-macro2", 1898 | "quote", 1899 | "syn 2.0.87", 1900 | ] 1901 | 1902 | [[package]] 1903 | name = "tick" 1904 | version = "0.1.6" 1905 | dependencies = [ 1906 | "anchor-lang", 1907 | "bolt-lang", 1908 | "game", 1909 | "serde", 1910 | ] 1911 | 1912 | [[package]] 1913 | name = "tiny-bip39" 1914 | version = "0.8.2" 1915 | source = "registry+https://github.com/rust-lang/crates.io-index" 1916 | checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" 1917 | dependencies = [ 1918 | "anyhow", 1919 | "hmac", 1920 | "once_cell", 1921 | "pbkdf2", 1922 | "rand 0.7.3", 1923 | "rustc-hash", 1924 | "sha2 0.9.9", 1925 | "thiserror", 1926 | "unicode-normalization", 1927 | "wasm-bindgen", 1928 | "zeroize", 1929 | ] 1930 | 1931 | [[package]] 1932 | name = "tinyvec" 1933 | version = "1.8.0" 1934 | source = "registry+https://github.com/rust-lang/crates.io-index" 1935 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1936 | dependencies = [ 1937 | "tinyvec_macros", 1938 | ] 1939 | 1940 | [[package]] 1941 | name = "tinyvec_macros" 1942 | version = "0.1.1" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1945 | 1946 | [[package]] 1947 | name = "toml" 1948 | version = "0.5.11" 1949 | source = "registry+https://github.com/rust-lang/crates.io-index" 1950 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" 1951 | dependencies = [ 1952 | "serde", 1953 | ] 1954 | 1955 | [[package]] 1956 | name = "toml" 1957 | version = "0.8.19" 1958 | source = "registry+https://github.com/rust-lang/crates.io-index" 1959 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 1960 | dependencies = [ 1961 | "serde", 1962 | "serde_spanned", 1963 | "toml_datetime", 1964 | "toml_edit", 1965 | ] 1966 | 1967 | [[package]] 1968 | name = "toml_datetime" 1969 | version = "0.6.8" 1970 | source = "registry+https://github.com/rust-lang/crates.io-index" 1971 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1972 | dependencies = [ 1973 | "serde", 1974 | ] 1975 | 1976 | [[package]] 1977 | name = "toml_edit" 1978 | version = "0.22.22" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 1981 | dependencies = [ 1982 | "indexmap", 1983 | "serde", 1984 | "serde_spanned", 1985 | "toml_datetime", 1986 | "winnow", 1987 | ] 1988 | 1989 | [[package]] 1990 | name = "tuple-conv" 1991 | version = "1.0.1" 1992 | source = "registry+https://github.com/rust-lang/crates.io-index" 1993 | checksum = "1fd6314683350324c1bb3d71a65fbeacb4e870cb4faf30b28d8a7787fb8116dd" 1994 | 1995 | [[package]] 1996 | name = "typenum" 1997 | version = "1.17.0" 1998 | source = "registry+https://github.com/rust-lang/crates.io-index" 1999 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2000 | 2001 | [[package]] 2002 | name = "unicode-ident" 2003 | version = "1.0.13" 2004 | source = "registry+https://github.com/rust-lang/crates.io-index" 2005 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 2006 | 2007 | [[package]] 2008 | name = "unicode-normalization" 2009 | version = "0.1.24" 2010 | source = "registry+https://github.com/rust-lang/crates.io-index" 2011 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 2012 | dependencies = [ 2013 | "tinyvec", 2014 | ] 2015 | 2016 | [[package]] 2017 | name = "unicode-segmentation" 2018 | version = "1.12.0" 2019 | source = "registry+https://github.com/rust-lang/crates.io-index" 2020 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 2021 | 2022 | [[package]] 2023 | name = "version_check" 2024 | version = "0.9.5" 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" 2026 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2027 | 2028 | [[package]] 2029 | name = "wasi" 2030 | version = "0.9.0+wasi-snapshot-preview1" 2031 | source = "registry+https://github.com/rust-lang/crates.io-index" 2032 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 2033 | 2034 | [[package]] 2035 | name = "wasi" 2036 | version = "0.11.0+wasi-snapshot-preview1" 2037 | source = "registry+https://github.com/rust-lang/crates.io-index" 2038 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2039 | 2040 | [[package]] 2041 | name = "wasm-bindgen" 2042 | version = "0.2.95" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 2045 | dependencies = [ 2046 | "cfg-if", 2047 | "once_cell", 2048 | "wasm-bindgen-macro", 2049 | ] 2050 | 2051 | [[package]] 2052 | name = "wasm-bindgen-backend" 2053 | version = "0.2.95" 2054 | source = "registry+https://github.com/rust-lang/crates.io-index" 2055 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 2056 | dependencies = [ 2057 | "bumpalo", 2058 | "log", 2059 | "once_cell", 2060 | "proc-macro2", 2061 | "quote", 2062 | "syn 2.0.87", 2063 | "wasm-bindgen-shared", 2064 | ] 2065 | 2066 | [[package]] 2067 | name = "wasm-bindgen-macro" 2068 | version = "0.2.95" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 2071 | dependencies = [ 2072 | "quote", 2073 | "wasm-bindgen-macro-support", 2074 | ] 2075 | 2076 | [[package]] 2077 | name = "wasm-bindgen-macro-support" 2078 | version = "0.2.95" 2079 | source = "registry+https://github.com/rust-lang/crates.io-index" 2080 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 2081 | dependencies = [ 2082 | "proc-macro2", 2083 | "quote", 2084 | "syn 2.0.87", 2085 | "wasm-bindgen-backend", 2086 | "wasm-bindgen-shared", 2087 | ] 2088 | 2089 | [[package]] 2090 | name = "wasm-bindgen-shared" 2091 | version = "0.2.95" 2092 | source = "registry+https://github.com/rust-lang/crates.io-index" 2093 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 2094 | 2095 | [[package]] 2096 | name = "web-sys" 2097 | version = "0.3.72" 2098 | source = "registry+https://github.com/rust-lang/crates.io-index" 2099 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 2100 | dependencies = [ 2101 | "js-sys", 2102 | "wasm-bindgen", 2103 | ] 2104 | 2105 | [[package]] 2106 | name = "windows-targets" 2107 | version = "0.52.6" 2108 | source = "registry+https://github.com/rust-lang/crates.io-index" 2109 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2110 | dependencies = [ 2111 | "windows_aarch64_gnullvm", 2112 | "windows_aarch64_msvc", 2113 | "windows_i686_gnu", 2114 | "windows_i686_gnullvm", 2115 | "windows_i686_msvc", 2116 | "windows_x86_64_gnu", 2117 | "windows_x86_64_gnullvm", 2118 | "windows_x86_64_msvc", 2119 | ] 2120 | 2121 | [[package]] 2122 | name = "windows_aarch64_gnullvm" 2123 | version = "0.52.6" 2124 | source = "registry+https://github.com/rust-lang/crates.io-index" 2125 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2126 | 2127 | [[package]] 2128 | name = "windows_aarch64_msvc" 2129 | version = "0.52.6" 2130 | source = "registry+https://github.com/rust-lang/crates.io-index" 2131 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2132 | 2133 | [[package]] 2134 | name = "windows_i686_gnu" 2135 | version = "0.52.6" 2136 | source = "registry+https://github.com/rust-lang/crates.io-index" 2137 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2138 | 2139 | [[package]] 2140 | name = "windows_i686_gnullvm" 2141 | version = "0.52.6" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2144 | 2145 | [[package]] 2146 | name = "windows_i686_msvc" 2147 | version = "0.52.6" 2148 | source = "registry+https://github.com/rust-lang/crates.io-index" 2149 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2150 | 2151 | [[package]] 2152 | name = "windows_x86_64_gnu" 2153 | version = "0.52.6" 2154 | source = "registry+https://github.com/rust-lang/crates.io-index" 2155 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2156 | 2157 | [[package]] 2158 | name = "windows_x86_64_gnullvm" 2159 | version = "0.52.6" 2160 | source = "registry+https://github.com/rust-lang/crates.io-index" 2161 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2162 | 2163 | [[package]] 2164 | name = "windows_x86_64_msvc" 2165 | version = "0.52.6" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2168 | 2169 | [[package]] 2170 | name = "winnow" 2171 | version = "0.6.20" 2172 | source = "registry+https://github.com/rust-lang/crates.io-index" 2173 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 2174 | dependencies = [ 2175 | "memchr", 2176 | ] 2177 | 2178 | [[package]] 2179 | name = "world" 2180 | version = "0.1.10" 2181 | source = "registry+https://github.com/rust-lang/crates.io-index" 2182 | checksum = "eb805007132c53759325df54cc67c2398cc2e97aa4149dff79aed594971c47b8" 2183 | dependencies = [ 2184 | "anchor-lang", 2185 | "bolt-component", 2186 | "bolt-helpers-world-apply", 2187 | "bolt-system", 2188 | "solana-security-txt", 2189 | "tuple-conv", 2190 | ] 2191 | 2192 | [[package]] 2193 | name = "zerocopy" 2194 | version = "0.7.35" 2195 | source = "registry+https://github.com/rust-lang/crates.io-index" 2196 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2197 | dependencies = [ 2198 | "byteorder", 2199 | "zerocopy-derive", 2200 | ] 2201 | 2202 | [[package]] 2203 | name = "zerocopy-derive" 2204 | version = "0.7.35" 2205 | source = "registry+https://github.com/rust-lang/crates.io-index" 2206 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2207 | dependencies = [ 2208 | "proc-macro2", 2209 | "quote", 2210 | "syn 2.0.87", 2211 | ] 2212 | 2213 | [[package]] 2214 | name = "zeroize" 2215 | version = "1.3.0" 2216 | source = "registry+https://github.com/rust-lang/crates.io-index" 2217 | checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" 2218 | dependencies = [ 2219 | "zeroize_derive", 2220 | ] 2221 | 2222 | [[package]] 2223 | name = "zeroize_derive" 2224 | version = "1.4.2" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 2227 | dependencies = [ 2228 | "proc-macro2", 2229 | "quote", 2230 | "syn 2.0.87", 2231 | ] 2232 | -------------------------------------------------------------------------------- /backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["programs-ecs/components/*", "programs-ecs/systems/*"] 3 | resolver = "2" 4 | 5 | [profile.release] 6 | overflow-checks = true 7 | lto = "fat" 8 | codegen-units = 1 9 | [profile.release.build-override] 10 | opt-level = 3 11 | incremental = false 12 | codegen-units = 1 13 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solana-generals-backed", 3 | "license": "MIT", 4 | "scripts": { 5 | "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", 6 | "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" 7 | }, 8 | "dependencies": { 9 | "@coral-xyz/anchor": "0.30.1" 10 | }, 11 | "devDependencies": { 12 | "@magicblock-labs/bolt-sdk": "latest", 13 | "@metaplex-foundation/beet": "^0.7.1", 14 | "@metaplex-foundation/beet-solana": "^0.4.0", 15 | "@types/bn.js": "^5.1.0", 16 | "@types/chai": "^4.3.0", 17 | "@types/mocha": "^9.0.0", 18 | "chai": "^4.3.4", 19 | "mocha": "^9.0.3", 20 | "prettier": "^2.6.2", 21 | "ts-mocha": "^10.0.0", 22 | "typescript": "^4.3.5" 23 | }, 24 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 25 | } 26 | -------------------------------------------------------------------------------- /backend/programs-ecs/components/game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "game" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "game" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | -------------------------------------------------------------------------------- /backend/programs-ecs/components/game/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/components/game/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | 3 | declare_id!("C5iL81s4Fu6SnkQEfixFZpKPRQ32fqVizpotoLVTxA2n"); 4 | 5 | #[component(delegate)] 6 | pub struct Game { 7 | pub status: GameStatus, 8 | pub size_x: u8, 9 | pub size_y: u8, 10 | pub players: [GamePlayer; 2], 11 | pub cells: [GameCell; 128], // max grid size is 16x8=128 12 | pub tick_next_slot: u64, // TODO(vbrunet) - use more precise clock 13 | } 14 | 15 | #[component_deserialize] 16 | #[derive(PartialEq)] 17 | pub enum GameStatus { 18 | Generate, 19 | Lobby, 20 | Playing, 21 | Finished, 22 | } 23 | 24 | #[component_deserialize] 25 | #[derive(PartialEq, Default)] 26 | pub struct GamePlayer { 27 | pub ready: bool, 28 | pub authority: Pubkey, 29 | pub last_action_slot: u64, 30 | } 31 | 32 | #[component_deserialize] 33 | #[derive(PartialEq)] 34 | pub struct GameCell { 35 | pub kind: GameCellKind, 36 | pub owner: GameCellOwner, 37 | pub strength: u8, 38 | } 39 | 40 | #[component_deserialize] 41 | #[derive(PartialEq)] 42 | pub enum GameCellKind { 43 | Field, 44 | City, 45 | Capital, 46 | Mountain, 47 | Forest, 48 | } 49 | 50 | #[component_deserialize] 51 | #[derive(PartialEq)] 52 | pub enum GameCellOwner { 53 | Player(u8), 54 | Nobody, 55 | } 56 | 57 | /** 58 | * The initial state of the component when initialized 59 | */ 60 | impl Default for Game { 61 | fn default() -> Self { 62 | Self::new(GameInit { 63 | status: GameStatus::Generate, 64 | size_x: 16, 65 | size_y: 8, 66 | players: [GamePlayer::default(); 2], 67 | cells: [GameCell::field(); 128], 68 | tick_next_slot: 0, 69 | }) 70 | } 71 | } 72 | 73 | /** 74 | * Utility functions to manipulate the game's cells 75 | */ 76 | impl Game { 77 | pub fn compute_index(&self, x: u8, y: u8) -> Result { 78 | if x >= self.size_x || y >= self.size_y { 79 | return Err(GameError::CellIsOutOfBounds.into()); 80 | } 81 | Ok(usize::from(y) * usize::from(self.size_x) + usize::from(x)) 82 | } 83 | pub fn get_cell(&self, x: u8, y: u8) -> Result<&GameCell> { 84 | Ok(&self.cells[self.compute_index(x, y)?]) 85 | } 86 | pub fn set_cell(&mut self, x: u8, y: u8, cell: GameCell) -> Result<()> { 87 | self.cells[self.compute_index(x, y)?] = cell; 88 | Ok(()) 89 | } 90 | } 91 | 92 | /** 93 | * Utility functions for standard cell types 94 | */ 95 | impl GameCell { 96 | pub fn field() -> GameCell { 97 | GameCell { 98 | kind: GameCellKind::Field, 99 | owner: GameCellOwner::Nobody, 100 | strength: 0, 101 | } 102 | } 103 | pub fn city() -> GameCell { 104 | GameCell { 105 | kind: GameCellKind::City, 106 | owner: GameCellOwner::Nobody, 107 | strength: 40, 108 | } 109 | } 110 | pub fn capital(player_slot: u8) -> GameCell { 111 | GameCell { 112 | kind: GameCellKind::Capital, 113 | owner: GameCellOwner::Player(player_slot), 114 | strength: 20, 115 | } 116 | } 117 | pub fn mountain() -> GameCell { 118 | GameCell { 119 | kind: GameCellKind::Mountain, 120 | owner: GameCellOwner::Nobody, 121 | strength: 0, 122 | } 123 | } 124 | pub fn forest() -> GameCell { 125 | GameCell { 126 | kind: GameCellKind::Forest, 127 | owner: GameCellOwner::Nobody, 128 | strength: 0, 129 | } 130 | } 131 | } 132 | 133 | #[error_code] 134 | pub enum GameError { 135 | #[msg("The game status is not currently set to Generate.")] 136 | StatusIsNotGenerate, 137 | #[msg("The game status is not currently set to Lobby.")] 138 | StatusIsNotLobby, 139 | #[msg("The game status is not currently set to Playing.")] 140 | StatusIsNotPlaying, 141 | #[msg("A player already joined in this slot.")] 142 | PlayerAlreadyJoined, 143 | #[msg("The player in this slot doesn't match the payer")] 144 | PlayerIsNotPayer, 145 | #[msg("The player in this slot is not ready to start")] 146 | PlayerIsNotReady, 147 | #[msg("The cell's position is out of bounds")] 148 | CellIsOutOfBounds, 149 | #[msg("The cells specified are not adjacent")] 150 | CellsAreNotAdjacent, 151 | #[msg("The cell's strength is insufficient")] 152 | CellStrengthIsInsufficient, 153 | #[msg("The cell is not owned by the player")] 154 | CellIsNotOwnedByPlayer, 155 | #[msg("The cell cannot be interacted with")] 156 | CellIsNotWalkable, 157 | } 158 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/command/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "command" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "command" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/command/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/command/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameCellKind; 4 | use game::GameCellOwner; 5 | use game::GameError; 6 | use game::GameStatus; 7 | 8 | declare_id!("YGKbhp7S1cCvvheyQ8rcuECKUR1SVpKpHjnqCqdP1cm"); 9 | 10 | #[system] 11 | pub mod command { 12 | pub fn execute(ctx: Context, args: Args) -> Result { 13 | let game = &mut ctx.accounts.game; 14 | 15 | // Can only trigger commands when the game is playing 16 | if game.status != GameStatus::Playing { 17 | return Err(GameError::StatusIsNotPlaying.into()); 18 | } 19 | 20 | let payer = ctx.accounts.authority.key(); 21 | let player = &mut game.players[usize::from(args.player_index)]; 22 | 23 | // Check that the user has authority to play 24 | if player.authority != payer { 25 | return Err(GameError::PlayerIsNotPayer.into()); 26 | } 27 | 28 | // Save player action 29 | player.last_action_slot = Clock::get()?.slot; 30 | 31 | // Read the cells involved in the transaction 32 | let source_cell_before = game.get_cell(args.source_x, args.source_y)?; 33 | let target_cell_before = game.get_cell(args.target_x, args.target_y)?; 34 | 35 | // Make sure those cells are exactly adjacent on the grid 36 | let distance_x = (i32::from(args.source_x) - i32::from(args.target_x)).abs(); 37 | let distance_y = (i32::from(args.source_y) - i32::from(args.target_y)).abs(); 38 | if distance_x + distance_y != 1 { 39 | return Err(GameError::CellsAreNotAdjacent.into()); 40 | } 41 | 42 | // Make sure the player owns the source cell 43 | if source_cell_before.owner != GameCellOwner::Player(args.player_index) { 44 | return Err(GameError::CellIsNotOwnedByPlayer.into()); 45 | } 46 | 47 | // Make sure the target cell can be interacted with 48 | if target_cell_before.kind == GameCellKind::Mountain { 49 | return Err(GameError::CellIsNotWalkable.into()); 50 | } 51 | 52 | // Make sure the source cell has enough moveable strength (each cell has minimum 1) 53 | if source_cell_before.strength <= 1 { 54 | return Err(GameError::CellStrengthIsInsufficient.into()); 55 | } 56 | 57 | // Compute how much has been requested to move 58 | let moved_strength = u8::try_from( 59 | u32::from(source_cell_before.strength - 1) * u32::from(args.strength_percent) / 100u32, 60 | ) 61 | .unwrap(); 62 | 63 | let mut source_cell_after = source_cell_before.clone(); 64 | let mut target_cell_after = target_cell_before.clone(); 65 | 66 | // Transfer strength if target cell is owned by same player 67 | if target_cell_before.owner == GameCellOwner::Player(args.player_index) { 68 | target_cell_after.strength = target_cell_before.strength.saturating_add(moved_strength); 69 | source_cell_after.strength = source_cell_before.strength 70 | - (target_cell_after.strength - target_cell_before.strength); 71 | } 72 | // If the target cell is not the same player, invade it 73 | else { 74 | // Move the strength out of the source cell 75 | source_cell_after.strength = source_cell_before.strength - moved_strength; 76 | // Some cells are harder to attack 77 | let damage_strength = match target_cell_before.kind { 78 | GameCellKind::Forest => moved_strength / 2, 79 | _ => moved_strength, 80 | }; 81 | // If the target cell resists the attack 82 | if damage_strength < target_cell_before.strength { 83 | target_cell_after.strength = target_cell_before.strength - damage_strength; 84 | } 85 | // If the target cell dies with the attack 86 | else if damage_strength == target_cell_before.strength { 87 | target_cell_after.owner = GameCellOwner::Nobody; 88 | target_cell_after.strength = 0; 89 | } 90 | // If the target cell gets conquered 91 | else { 92 | target_cell_after.owner = source_cell_after.owner.clone(); 93 | target_cell_after.strength = damage_strength - target_cell_before.strength; 94 | } 95 | } 96 | 97 | // Apply changes on the impacted cells 98 | game.set_cell(args.source_x, args.source_y, source_cell_after)?; 99 | game.set_cell(args.target_x, args.target_y, target_cell_after)?; 100 | 101 | Ok(ctx.accounts) 102 | } 103 | 104 | #[system_input] 105 | pub struct Components { 106 | pub game: Game, 107 | } 108 | 109 | #[arguments] 110 | struct Args { 111 | player_index: u8, 112 | 113 | source_x: u8, 114 | source_y: u8, 115 | 116 | target_x: u8, 117 | target_y: u8, 118 | 119 | strength_percent: u8, 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/finish/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "finish" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "finish" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/finish/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/finish/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameCellOwner; 4 | use game::GameError; 5 | use game::GameStatus; 6 | 7 | declare_id!("HBdGPJycpHjjJ149T3RQGtQWjSC39MVpcKYF6JJvaF6e"); 8 | 9 | #[system] 10 | pub mod finish { 11 | 12 | pub fn execute(ctx: Context, args: Args) -> Result { 13 | let game = &mut ctx.accounts.game; 14 | 15 | // Can only finish the game when it's currently running 16 | if game.status != GameStatus::Playing { 17 | return Err(GameError::StatusIsNotPlaying.into()); 18 | } 19 | 20 | // The standard win condition is when a user is the last one standing 21 | let mut finished = true; 22 | for x in 0..game.size_x { 23 | for y in 0..game.size_y { 24 | let cell = game.get_cell(x, y)?; 25 | let anyone_else = match cell.owner { 26 | GameCellOwner::Player(player_index) => player_index != args.player_index, 27 | GameCellOwner::Nobody => false, 28 | }; 29 | if anyone_else { 30 | finished = false; 31 | } 32 | } 33 | } 34 | 35 | // Mark game finished only if didnt find anything against it 36 | if finished { 37 | game.status = GameStatus::Finished; 38 | } 39 | 40 | Ok(ctx.accounts) 41 | } 42 | 43 | #[system_input] 44 | pub struct Components { 45 | pub game: Game, 46 | } 47 | 48 | #[arguments] 49 | struct Args { 50 | player_index: u8, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/generate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generate" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "generate" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/generate/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/generate/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameCell; 4 | use game::GameError; 5 | use game::GameStatus; 6 | 7 | declare_id!("3KFBmeDYJgrB9SB8jfsrXQN5wNvKinneJgNnpa2KgRw7"); 8 | 9 | #[system] 10 | pub mod generate { 11 | 12 | pub fn execute(ctx: Context, _args: Args) -> Result { 13 | let game = &mut ctx.accounts.game; 14 | 15 | // Can only generate the game when it has just been initialized 16 | if game.status != GameStatus::Generate { 17 | return Err(GameError::StatusIsNotGenerate.into()); 18 | } 19 | 20 | // Generate a dummy map, could have multiple generation options later 21 | game.size_x = 16; 22 | game.size_y = 8; 23 | 24 | // Whole map is just fields for now 25 | for x in 0..game.size_x { 26 | for y in 0..game.size_y { 27 | game.set_cell(x, y, GameCell::field())? 28 | } 29 | } 30 | 31 | // Add cities in the corners 32 | game.set_cell(2, 5, GameCell::city())?; 33 | game.set_cell(13, 2, GameCell::city())?; 34 | 35 | // Add cities in the center 36 | game.set_cell(7, 3, GameCell::city())?; 37 | game.set_cell(8, 4, GameCell::city())?; 38 | 39 | // Players capitals in the corners 40 | game.set_cell(1, 1, GameCell::capital(0))?; 41 | game.set_cell(14, 6, GameCell::capital(1))?; 42 | 43 | /* 44 | // Add some mountains in the middle 45 | game.set_cell(7, 5, GameCell::mountain())?; 46 | game.set_cell(8, 2, GameCell::mountain())?; 47 | 48 | // Add some forest in the the middle 49 | game.set_cell(7, 1, GameCell::forest())?; 50 | game.set_cell(8, 6, GameCell::forest())?; 51 | */ 52 | 53 | // When done, mark the game as ready to receive players 54 | ctx.accounts.game.status = GameStatus::Lobby; 55 | 56 | Ok(ctx.accounts) 57 | } 58 | 59 | #[system_input] 60 | pub struct Components { 61 | pub game: Game, 62 | } 63 | 64 | #[arguments] 65 | struct Args {} 66 | } 67 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/join/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "join" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "join" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/join/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/join/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameError; 4 | use game::GameStatus; 5 | 6 | declare_id!("3zMXokc8DYYAairrtAKZKPJZKHmWKRdj6G8bm8ZZVi9g"); 7 | 8 | #[system] 9 | pub mod join { 10 | 11 | pub fn execute(ctx: Context, args: Args) -> Result { 12 | let game = &mut ctx.accounts.game; 13 | 14 | // Can only join the game when the game is accepting new players 15 | if game.status != GameStatus::Lobby { 16 | return Err(GameError::StatusIsNotLobby.into()); 17 | } 18 | 19 | let payer = ctx.accounts.authority.key(); 20 | let player = &mut game.players[usize::from(args.player_index)]; 21 | 22 | // If user want to join the room 23 | if args.join { 24 | if player.ready { 25 | return Err(GameError::PlayerAlreadyJoined.into()); 26 | } else { 27 | player.authority = payer; 28 | player.ready = true; 29 | } 30 | } 31 | // If user want to leave the room 32 | else { 33 | if player.authority != payer { 34 | return Err(GameError::PlayerIsNotPayer.into()); 35 | } else { 36 | player.authority = Pubkey::default(); 37 | player.ready = false; 38 | } 39 | } 40 | 41 | Ok(ctx.accounts) 42 | } 43 | 44 | #[system_input] 45 | pub struct Components { 46 | pub game: Game, 47 | } 48 | 49 | #[arguments] 50 | struct Args { 51 | player_index: u8, 52 | join: bool, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/start/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "start" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "start" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/start/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/start/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameError; 4 | use game::GameStatus; 5 | 6 | declare_id!("Cu8JkUA9a5msGWNChAuhBJ9PTE6FdevwHNgPyxbABkUL"); 7 | 8 | #[system] 9 | pub mod start { 10 | 11 | pub fn execute(ctx: Context, _args_p: Vec) -> Result { 12 | let game = &mut ctx.accounts.game; 13 | 14 | // Can only start the game when the game is not yet started 15 | if game.status != GameStatus::Lobby { 16 | return Err(GameError::StatusIsNotLobby.into()); 17 | } 18 | 19 | // Check if all players are ready 20 | for player in &game.players { 21 | if !player.ready { 22 | return Err(GameError::PlayerIsNotPayer.into()); 23 | } 24 | } 25 | 26 | // Mark the game as ready to tick 27 | game.tick_next_slot = Clock::get()?.slot; 28 | 29 | // Mark the game as started 30 | ctx.accounts.game.status = GameStatus::Playing; 31 | 32 | Ok(ctx.accounts) 33 | } 34 | 35 | #[system_input] 36 | pub struct Components { 37 | pub game: Game, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/tick/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tick" 3 | version = "0.1.6" 4 | description = "Created with Bolt" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "tick" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | idl-build = ["anchor-lang/idl-build"] 18 | 19 | [dependencies] 20 | bolt-lang = "0.1.9" 21 | anchor-lang = "0.30.1" 22 | serde = { version = "1.0", features = ["derive"] } 23 | game = { path = "../../components/game", features = ["cpi"] } 24 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/tick/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /backend/programs-ecs/systems/tick/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bolt_lang::*; 2 | use game::Game; 3 | use game::GameCellKind; 4 | use game::GameCellOwner; 5 | use game::GameError; 6 | use game::GameStatus; 7 | 8 | declare_id!("8tKAapRKPrNkxXwcArbSAnBHieYnX6M2WoTxukbCQtTa"); 9 | 10 | const TICKS_PER_SECOND: u64 = 20; 11 | 12 | #[system] 13 | pub mod tick { 14 | pub fn execute(ctx: Context, _args_p: Vec) -> Result { 15 | let game = &mut ctx.accounts.game; 16 | 17 | // Can only trigger commands when the game is playing 18 | if game.status != GameStatus::Playing { 19 | return Err(GameError::StatusIsNotPlaying.into()); 20 | } 21 | 22 | // Check that the game tick cooldown has elapsed 23 | let mut incremented_times = 0; 24 | while Clock::get()?.slot >= game.tick_next_slot { 25 | game.tick_next_slot = game.tick_next_slot + 1; 26 | 27 | // Everything happens on ticks mutliples of 1 second, we can ignore everything else 28 | if game.tick_next_slot % TICKS_PER_SECOND != 0 { 29 | continue; 30 | } 31 | 32 | // Loop over all cell and increment its strength if someone is occupying 33 | for x in 0..game.size_x { 34 | for y in 0..game.size_y { 35 | let mut cell = game.get_cell(x, y)?.clone(); 36 | if cell.owner != GameCellOwner::Nobody { 37 | // Capital gains 1 unit every 5 seconds 38 | if game.tick_next_slot % (TICKS_PER_SECOND * 5) == 0 39 | && cell.kind == GameCellKind::Capital 40 | { 41 | cell.strength = cell.strength.saturating_add(1); 42 | } 43 | // City gains 1 unit every 10 seconds 44 | if game.tick_next_slot % (TICKS_PER_SECOND * 10) == 0 45 | && cell.kind == GameCellKind::City 46 | { 47 | cell.strength = cell.strength.saturating_add(1); 48 | } 49 | // Fields gains 1 unit every 60 seconds 50 | if game.tick_next_slot % (TICKS_PER_SECOND * 60) == 0 51 | && cell.kind == GameCellKind::Field 52 | { 53 | cell.strength = cell.strength.saturating_add(1); 54 | } 55 | } 56 | game.set_cell(x, y, cell)?; 57 | } 58 | } 59 | 60 | // We can only process a few ticks per transaction (we are CU limited) 61 | incremented_times += 1; 62 | if incremented_times >= 5 { 63 | break; 64 | } 65 | } 66 | 67 | Ok(ctx.accounts) 68 | } 69 | 70 | #[system_input] 71 | pub struct Components { 72 | pub game: Game, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /backend/tests/backend.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@coral-xyz/anchor"; 2 | import { Program } from "@coral-xyz/anchor"; 3 | import { PublicKey } from "@solana/web3.js"; 4 | import { Game } from "../target/types/game"; 5 | import { Generate } from "../target/types/generate"; 6 | import { Join } from "../target/types/join"; 7 | import { Start } from "../target/types/start"; 8 | import { Tick } from "../target/types/tick"; 9 | import { Finish } from "../target/types/finish"; 10 | import { Command } from "../target/types/command"; 11 | import { 12 | InitializeNewWorld, 13 | AddEntity, 14 | InitializeComponent, 15 | ApplySystem, 16 | } from "@magicblock-labs/bolt-sdk"; 17 | import { expect } from "chai"; 18 | 19 | describe("Backend", () => { 20 | const provider = anchor.AnchorProvider.env(); 21 | anchor.setProvider(provider); 22 | 23 | let worldPda: PublicKey; 24 | let entityPda: PublicKey; 25 | let gamePda: PublicKey; 26 | 27 | const gameComponent = anchor.workspace.Game as Program; 28 | 29 | const systemGenerate = anchor.workspace.Generate as Program; 30 | const systemJoin = anchor.workspace.Join as Program; 31 | const systemStart = anchor.workspace.Start as Program; 32 | const systemTick = anchor.workspace.Tick as Program; 33 | const systemCommand = anchor.workspace.Command as Program; 34 | const systemFinish = anchor.workspace.Finish as Program; 35 | 36 | it("InitializeNewWorld", async () => { 37 | const initializeNewWorld = await InitializeNewWorld({ 38 | payer: provider.wallet.publicKey, 39 | connection: provider.connection, 40 | }); 41 | const signature = await provider.sendAndConfirm( 42 | initializeNewWorld.transaction 43 | ); 44 | worldPda = initializeNewWorld.worldPda; 45 | console.log( 46 | `Initialized a new world (ID=${worldPda}). Initialization signature: ${signature}` 47 | ); 48 | }); 49 | 50 | it("AddEntity", async () => { 51 | const addEntity = await AddEntity({ 52 | payer: provider.wallet.publicKey, 53 | world: worldPda, 54 | connection: provider.connection, 55 | }); 56 | const signature = await provider.sendAndConfirm(addEntity.transaction); 57 | entityPda = addEntity.entityPda; 58 | console.log( 59 | `Initialized a new Entity (ID=${addEntity.entityId}). Initialization signature: ${signature}` 60 | ); 61 | }); 62 | 63 | it("Initialize Game", async () => { 64 | const initializeComponent = await InitializeComponent({ 65 | payer: provider.wallet.publicKey, 66 | entity: entityPda, 67 | componentId: gameComponent.programId, 68 | }); 69 | const signature = await provider.sendAndConfirm( 70 | initializeComponent.transaction 71 | ); 72 | gamePda = initializeComponent.componentPda; 73 | console.log( 74 | `Initialized the grid component. Initialization signature: ${signature}` 75 | ); 76 | await logGameInfos(); 77 | await logGameCells(); 78 | }); 79 | 80 | it("Generate the map", async () => { 81 | await tryApplySystemOnGame({ 82 | systemId: systemGenerate.programId, 83 | args: { 84 | map: 0, 85 | }, 86 | }); 87 | await logGameInfos(); 88 | await logGameCells(); 89 | }); 90 | 91 | it("Player 1 joins slot 0", async () => { 92 | await tryApplySystemOnGame({ 93 | systemId: systemJoin.programId, 94 | args: { 95 | player_index: 0, 96 | join: true, 97 | }, 98 | }); 99 | await logGameInfos(); 100 | }); 101 | 102 | it("Player 2 joins slot 1", async () => { 103 | await tryApplySystemOnGame({ 104 | systemId: systemJoin.programId, 105 | args: { 106 | player_index: 1, 107 | join: true, 108 | }, 109 | }); 110 | await logGameInfos(); 111 | }); 112 | 113 | it("Start the game", async () => { 114 | await tryApplySystemOnGame({ systemId: systemStart.programId, args: {} }); 115 | await logGameInfos(); 116 | }); 117 | 118 | it("Tick the game (#1)", async () => { 119 | await tryApplySystemOnGame({ systemId: systemTick.programId, args: {} }); 120 | await logGameInfos(); 121 | await logGameCells(); 122 | }); 123 | 124 | it("Player 1 makes a move", async () => { 125 | await tryApplySystemOnGame({ 126 | systemId: systemCommand.programId, 127 | args: { 128 | player_index: 0, 129 | source_x: 1, 130 | source_y: 1, 131 | target_x: 1, 132 | target_y: 2, 133 | strength_percent: 100, 134 | }, 135 | }); 136 | await logGameInfos(); 137 | await logGameCells(); 138 | }); 139 | 140 | it("Player 2 tries to claim victory, it should have no effect", async () => { 141 | await tryApplySystemOnGame({ 142 | systemId: systemFinish.programId, 143 | args: { 144 | player_index: 1, 145 | }, 146 | }); 147 | await logGameInfos(); 148 | }); 149 | 150 | it("Player 2 makes a move", async () => { 151 | await tryApplySystemOnGame({ 152 | systemId: systemCommand.programId, 153 | args: { 154 | player_index: 1, 155 | source_x: 14, 156 | source_y: 6, 157 | target_x: 13, 158 | target_y: 6, 159 | strength_percent: 100, 160 | }, 161 | }); 162 | await logGameInfos(); 163 | await logGameCells(); 164 | }); 165 | 166 | async function tryApplySystemOnGame({ 167 | systemId, 168 | args, 169 | expectedError, 170 | }: { 171 | systemId: PublicKey; 172 | args: {}; 173 | expectedError?: string; 174 | }) { 175 | const applySystem = await ApplySystem({ 176 | authority: provider.wallet.publicKey, 177 | system: systemId, 178 | entity: entityPda, 179 | components: [gameComponent.programId], 180 | args: args, 181 | }); 182 | let success = true; 183 | try { 184 | const signature = await provider.sendAndConfirm(applySystem.transaction); 185 | console.log(`Applied a system. Signature: ${signature}`); 186 | } catch (error) { 187 | success = false; 188 | if (expectedError) { 189 | expect(error.toString()).to.include(expectedError); 190 | } else { 191 | console.log("unexpected error:", error); 192 | } 193 | } 194 | expect(success).to.equal(!expectedError); 195 | } 196 | 197 | async function logGameInfos() { 198 | const gameData = await gameComponent.account.game.fetch(gamePda); 199 | 200 | console.log("Game Status:"); 201 | let status = "??"; 202 | if (gameData.status.finished !== undefined) { 203 | status = "FINISHED"; 204 | } else if (gameData.status.generate !== undefined) { 205 | status = "GENERATE"; 206 | } else if (gameData.status.lobby !== undefined) { 207 | status = "LOBBY"; 208 | } else if (gameData.status.playing !== undefined) { 209 | status = "PLAYING"; 210 | } 211 | console.log(">", status); 212 | 213 | console.log("Game Players:"); 214 | for (let i = 0; i < gameData.players.length; i++) { 215 | const player = gameData.players[i]; 216 | console.log( 217 | ">", 218 | player.ready ? "[READY]" : "[ AFK ]", 219 | player.authority.toBase58() 220 | ); 221 | } 222 | } 223 | 224 | async function logGameCells() { 225 | const gameData = await gameComponent.account.game.fetch(gamePda); 226 | 227 | console.log("Game Cells:"); 228 | for (let y = 0; y < gameData.sizeY; y++) { 229 | const parts = []; 230 | for (let x = 0; x < gameData.sizeX; x++) { 231 | const cell = gameData.cells[y * gameData.sizeX + x]; 232 | parts.push("|"); 233 | 234 | if (cell.owner.nobody !== undefined) { 235 | parts.push(" "); 236 | } else if (cell.owner.player !== undefined) { 237 | if (cell.owner.player[0] == 0) { 238 | parts.push("A"); 239 | } else if (cell.owner.player[0] == 1) { 240 | parts.push("B"); 241 | } else { 242 | parts.push("?"); 243 | } 244 | } else { 245 | parts.push("?"); 246 | } 247 | 248 | if (cell.kind.capital !== undefined) { 249 | parts.push("$"); 250 | } else if (cell.kind.city !== undefined) { 251 | parts.push("X"); 252 | } else if (cell.kind.field !== undefined) { 253 | parts.push(" "); 254 | } else if (cell.kind.mountain !== undefined) { 255 | parts.push("M"); 256 | } else if (cell.kind.forest !== undefined) { 257 | parts.push("F"); 258 | } else { 259 | parts.push("?"); 260 | } 261 | 262 | if (cell.strength == 0) { 263 | parts.push(" "); 264 | } else if (cell.strength < 10) { 265 | parts.push(cell.strength.toString()); 266 | } else { 267 | parts.push("+"); 268 | } 269 | } 270 | parts.push("|"); 271 | console.log(parts.join("")); 272 | } 273 | } 274 | }); 275 | -------------------------------------------------------------------------------- /backend/tests/fixtures/registry.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "pubkey": "EHLkWwAT9oebVv9ht3mtqrvHhRVMKrt54tF3MfHTey2K", 4 | "account": { 5 | "lamports": 1002240, 6 | "data": [ 7 | "L65u9ri2/NoCAAAAAAAAAA==", 8 | "base64" 9 | ], 10 | "owner": "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n", 11 | "executable": false, 12 | "rentEpoch": 18446744073709551615, 13 | "space": 16 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/tests/fixtures/world.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magicblock-labs/solana-generals/d741b664205c51833d9d859840b25a41539a80dc/backend/tests/fixtures/world.so -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["mocha", "chai"], 4 | "typeRoots": ["./node_modules/@types"], 5 | "lib": ["es2015"], 6 | "module": "commonjs", 7 | "target": "es6", 8 | "esModuleInterop": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # MagicBlock Boilerplate 2 | 3 | ## Introduction 4 | 5 | This folder countains an example of a fully functioning on-chain game using MagicBlock's engine. 6 | 7 | ## Using the code 8 | 9 | The `src` folder countains a basic example app to get started. 10 | 11 | * Run `npm install` to install dependencies. 12 | * Run one of the following commands: 13 | 1. `npm run build-dev` to build the app in `development` mode. 14 | 2. `npm run build-prod` to build in `production` mode. 15 | 3. `npm run dev` to start the dev-server. 16 | * Make whatever additional changes you need for your project. 17 | -------------------------------------------------------------------------------- /frontend/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png"; 2 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solana-generals-frontend", 3 | "license": "MIT", 4 | "main": "index.js", 5 | "scripts": { 6 | "build-dev": "webpack --mode=development", 7 | "build-prod": "webpack --mode=production", 8 | "dev": "webpack serve --hot --port 8080", 9 | "test": "", 10 | "lint:fix": "prettier \"*/**/*{.ts,.tsx,.json}\" -w", 11 | "lint": "prettier \"*/**/*{.ts,.tsx,.json}\" --check" 12 | }, 13 | "devDependencies": { 14 | "@types/react": "18.2.21", 15 | "@types/react-dom": "18.2.7", 16 | "@types/react-router-dom": "5.3.3", 17 | "clean-webpack-plugin": "^4.0.0", 18 | "css-loader": "6.8.1", 19 | "file-loader": "6.2.0", 20 | "html-webpack-plugin": "5.5.3", 21 | "prettier": "^3.3.2", 22 | "sass": "1.67.0", 23 | "sass-loader": "13.3.2", 24 | "style-loader": "3.3.3", 25 | "ts-loader": "9.4.4", 26 | "typescript": "5.2.2", 27 | "url-loader": "4.1.1", 28 | "webpack": "5.88.2", 29 | "webpack-cli": "5.1.4", 30 | "webpack-dev-server": "4.15.1" 31 | }, 32 | "dependencies": { 33 | "@coral-xyz/anchor": "^0.30.1", 34 | "@magicblock-labs/bolt-sdk": "0.1.10", 35 | "@solana/wallet-adapter-base": "^0.9.23", 36 | "@solana/wallet-adapter-react": "^0.15.35", 37 | "@solana/web3.js": "^1.93.0", 38 | "process": "^0.11.10", 39 | "react": "18.2.0", 40 | "react-dom": "18.2.0", 41 | "react-router-dom": "6.16.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/components/game/GameError.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | import { Text } from "../util/Text"; 6 | import { Button } from "../util/Button"; 7 | 8 | export function GameError({ message }: { message: string }) { 9 | const navigate = useNavigate(); 10 | return ( 11 | <> 12 | 13 | 14 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/components/menu/MenuSession.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { useMagicBlockEngine } from "../../engine/MagicBlockEngineProvider"; 4 | 5 | import { Text } from "../util/Text"; 6 | import { Button } from "../util/Button"; 7 | 8 | import { MenuBalance } from "./MenuBalance"; 9 | 10 | export function MenuSession() { 11 | const engine = useMagicBlockEngine(); 12 | 13 | const sessionPayer = engine.getSessionPayer(); 14 | 15 | const [sessionLamports, setSessionLamports] = React.useState(undefined); 16 | React.useEffect(() => { 17 | return engine.subscribeToChainAccountInfo(sessionPayer, (accountInfo) => { 18 | setSessionLamports(accountInfo?.lamports); 19 | }); 20 | }, [engine]); 21 | 22 | const needsFunding = 23 | sessionLamports !== undefined 24 | ? sessionLamports < engine.getSessionMinLamports() 25 | : true; 26 | 27 | const extras = []; 28 | if (engine.getWalletConnected() && needsFunding) { 29 | const onFund = async () => { 30 | await engine.fundSessionFromWallet(); 31 | console.log("funded"); 32 | }; 33 | extras.push( 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/components/util/ForEach.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { If } from "./If"; 4 | 5 | export function ForEach({ 6 | values, 7 | before, 8 | renderer, 9 | placeholder, 10 | }: { 11 | values?: Value[]; 12 | renderer: (value: Value, index: number) => React.ReactElement; 13 | before?: () => React.ReactElement; 14 | placeholder?: () => React.ReactElement; 15 | }) { 16 | if (!values || values.length <= 0) { 17 | return ; 18 | } 19 | return ( 20 | <> 21 | 22 | {values.map(renderer)} 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/components/util/If.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export function If({ 4 | value, 5 | renderer, 6 | placeholder, 7 | }: { 8 | value: Value; 9 | renderer: (value: Value) => React.ReactElement; 10 | placeholder?: () => React.ReactElement; 11 | }) { 12 | if (!value) { 13 | if (placeholder) { 14 | return placeholder(); 15 | } else { 16 | return <>; 17 | } 18 | } 19 | return renderer(value); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/components/util/Text.scss: -------------------------------------------------------------------------------- 1 | .Text { 2 | padding: 4px 8px; 3 | text-align: center; 4 | 5 | &.Title { 6 | font-weight: bold; 7 | } 8 | &.Warning { 9 | color: rgb(255, 150, 150); 10 | } 11 | &.Fading { 12 | opacity: 0.5; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/util/Text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import "./Text.scss"; 4 | 5 | export function Text({ 6 | value, 7 | isTitle, 8 | isWarning, 9 | isFading, 10 | }: { 11 | value: string; 12 | isTitle?: boolean; 13 | isWarning?: boolean; 14 | isFading?: boolean; 15 | }) { 16 | const classNames = ["Text"]; 17 | if (isTitle) { 18 | classNames.push("Title"); 19 | } 20 | if (isWarning) { 21 | classNames.push("Warning"); 22 | } 23 | if (isFading) { 24 | classNames.push("Fading"); 25 | } 26 | return
{value}
; 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/engine/MagicBlockEngine.ts: -------------------------------------------------------------------------------- 1 | import { Idl, Program } from "@coral-xyz/anchor"; 2 | import { WalletContextState } from "@solana/wallet-adapter-react"; 3 | import { 4 | AccountInfo, 5 | Commitment, 6 | Connection, 7 | Keypair, 8 | PublicKey, 9 | SystemProgram, 10 | Transaction, 11 | } from "@solana/web3.js"; 12 | import { WalletName } from "@solana/wallet-adapter-base"; 13 | 14 | const ENDPOINT_CHAIN_RPC = "https://api.devnet.solana.com"; 15 | const ENDPOINT_CHAIN_WS = "wss://api.devnet.solana.com"; 16 | 17 | const _ENDPOINT_CHAIN_RPC = "http://127.0.0.1:7899"; 18 | const _ENDPOINT_CHAIN_WS = "ws://127.0.0.1:7900"; 19 | 20 | const ENDPOINT_EPHEM_RPC = "https://devnet.magicblock.app"; 21 | const ENDPOINT_EPHEM_WS = "wss://devnet.magicblock.app:8900"; 22 | 23 | const _ENDPOINT_EPHEM_RPC = "http://localhost:8899"; 24 | const _ENDPOINT_EPHEM_WS = "ws://localhost:8900"; 25 | 26 | const TRANSACTION_COST_LAMPORTS = 5000; 27 | 28 | const connectionChain = new Connection(ENDPOINT_CHAIN_RPC, { 29 | wsEndpoint: ENDPOINT_CHAIN_WS, 30 | }); 31 | const connectionEphem = new Connection(ENDPOINT_EPHEM_RPC, { 32 | wsEndpoint: ENDPOINT_EPHEM_WS, 33 | }); 34 | 35 | interface SessionConfig { 36 | minLamports: number; 37 | maxLamports: number; 38 | } 39 | 40 | interface WalletAdapter { 41 | name: string; 42 | icon: string; 43 | } 44 | 45 | export class MagicBlockEngine { 46 | private walletContext: WalletContextState; 47 | private sessionKey: Keypair; 48 | private sessionConfig: SessionConfig; 49 | 50 | constructor( 51 | walletContext: WalletContextState, 52 | sessionKey: Keypair, 53 | sessionConfig: SessionConfig 54 | ) { 55 | this.walletContext = walletContext; 56 | this.sessionKey = sessionKey; 57 | this.sessionConfig = sessionConfig; 58 | } 59 | 60 | getProgramOnChain(idl: {}): Program { 61 | return new Program(idl as T, { connection: connectionChain }); 62 | } 63 | getProgramOnEphem(idl: {}): Program { 64 | return new Program(idl as T, { connection: connectionEphem }); 65 | } 66 | 67 | getConnectionChain(): Connection { 68 | return connectionChain; 69 | } 70 | getConnectionEphem(): Connection { 71 | return connectionEphem; 72 | } 73 | 74 | getWalletConnected() { 75 | return this.walletContext.connected; 76 | } 77 | getWalletConnecting() { 78 | return this.walletContext.connecting; 79 | } 80 | 81 | getWalletPayer(): PublicKey { 82 | return this.walletContext.publicKey; 83 | } 84 | 85 | getSessionPayer(): PublicKey { 86 | return this.sessionKey.publicKey; 87 | } 88 | 89 | async processWalletTransaction( 90 | name: string, 91 | transaction: Transaction 92 | ): Promise { 93 | console.log(name, "sending"); 94 | const signature = await this.walletContext.sendTransaction( 95 | transaction, 96 | connectionChain 97 | ); 98 | await this.waitSignatureConfirmation( 99 | name, 100 | signature, 101 | connectionChain, 102 | "confirmed" 103 | ); 104 | return signature; 105 | } 106 | 107 | async processSessionChainTransaction( 108 | name: string, 109 | transaction: Transaction 110 | ): Promise { 111 | console.log(name, "sending"); 112 | const signature = await connectionChain.sendTransaction( 113 | transaction, 114 | [this.sessionKey], 115 | { skipPreflight: true } 116 | ); 117 | await this.waitSignatureConfirmation( 118 | name, 119 | signature, 120 | connectionChain, 121 | "confirmed" 122 | ); 123 | return signature; 124 | } 125 | 126 | async processSessionEphemTransaction( 127 | name: string, 128 | transaction: Transaction 129 | ): Promise { 130 | console.log(name, "sending"); 131 | transaction.compileMessage; 132 | const signature = await connectionEphem.sendTransaction( 133 | transaction, 134 | [this.sessionKey], 135 | { skipPreflight: true } 136 | ); 137 | await this.waitSignatureConfirmation( 138 | name, 139 | signature, 140 | connectionEphem, 141 | "finalized" 142 | ); 143 | return signature; 144 | } 145 | 146 | async waitSignatureConfirmation( 147 | name: string, 148 | signature: string, 149 | connection: Connection, 150 | commitment: Commitment 151 | ): Promise { 152 | console.log(name, "sent", signature); 153 | return new Promise((resolve, reject) => { 154 | connection.onSignature( 155 | signature, 156 | (result) => { 157 | console.log(name, commitment, signature, result.err); 158 | if (result.err) { 159 | this.debugError(name, signature, connection); 160 | reject(result.err); 161 | } else { 162 | resolve(); 163 | } 164 | }, 165 | commitment 166 | ); 167 | }); 168 | } 169 | 170 | async debugError(name: string, signature: string, connection: Connection) { 171 | const transaction = await connection.getParsedTransaction(signature); 172 | console.log("debugError", name, signature, transaction); 173 | } 174 | 175 | async getSessionFundingMissingLamports() { 176 | const accountInfo = await connectionChain.getAccountInfo( 177 | this.getSessionPayer() 178 | ); 179 | const currentLamports = accountInfo?.lamports ?? 0; 180 | if (currentLamports < this.sessionConfig.minLamports) { 181 | return this.sessionConfig.maxLamports - currentLamports; 182 | } 183 | return 0; 184 | } 185 | 186 | async fundSessionFromAirdrop() { 187 | const missingLamports = await this.getSessionFundingMissingLamports(); 188 | if (missingLamports > 0) { 189 | await connectionChain.requestAirdrop( 190 | this.sessionKey.publicKey, 191 | missingLamports 192 | ); 193 | } 194 | } 195 | 196 | async fundSessionFromWallet() { 197 | const missingLamports = await this.getSessionFundingMissingLamports(); 198 | if (missingLamports > 0) { 199 | await this.processWalletTransaction( 200 | "FundSessionFromWallet", 201 | new Transaction().add( 202 | SystemProgram.transfer({ 203 | fromPubkey: this.getWalletPayer(), 204 | toPubkey: this.getSessionPayer(), 205 | lamports: missingLamports, 206 | }) 207 | ) 208 | ); 209 | } 210 | } 211 | 212 | async defundSessionBackToWallet() { 213 | const accountInfo = await connectionChain.getAccountInfo( 214 | this.getSessionPayer() 215 | ); 216 | if (accountInfo && accountInfo.lamports > 0) { 217 | const transferableLamports = 218 | accountInfo.lamports - TRANSACTION_COST_LAMPORTS; 219 | await this.processSessionChainTransaction( 220 | "DefundSessionBackToWallet", 221 | new Transaction().add( 222 | SystemProgram.transfer({ 223 | fromPubkey: this.getSessionPayer(), 224 | toPubkey: this.getWalletPayer(), 225 | lamports: transferableLamports, 226 | }) 227 | ) 228 | ); 229 | } 230 | } 231 | 232 | getChainAccountInfo(address: PublicKey) { 233 | return connectionChain.getAccountInfo(address); 234 | } 235 | 236 | getEphemAccountInfo(address: PublicKey) { 237 | return connectionEphem.getAccountInfo(address); 238 | } 239 | 240 | subscribeToChainAccountInfo( 241 | address: PublicKey, 242 | onAccountChange: (accountInfo?: AccountInfo) => void 243 | ) { 244 | return this.subscribeToAccountInfo( 245 | connectionChain, 246 | address, 247 | onAccountChange 248 | ); 249 | } 250 | 251 | subscribeToEphemAccountInfo( 252 | address: PublicKey, 253 | onAccountChange: (accountInfo?: AccountInfo) => void 254 | ) { 255 | return this.subscribeToAccountInfo( 256 | connectionEphem, 257 | address, 258 | onAccountChange 259 | ); 260 | } 261 | 262 | subscribeToAccountInfo( 263 | connection: Connection, 264 | address: PublicKey, 265 | onAccountChange: (accountInfo?: AccountInfo) => void 266 | ) { 267 | let ignoreFetch = false; 268 | connection.getAccountInfo(address).then( 269 | (accountInfo) => { 270 | if (ignoreFetch) { 271 | return; 272 | } 273 | onAccountChange(accountInfo); 274 | }, 275 | (error) => { 276 | console.log("Error fetching accountInfo", error); 277 | onAccountChange(undefined); 278 | } 279 | ); 280 | const subscription = connection.onAccountChange(address, (accountInfo) => { 281 | ignoreFetch = true; 282 | onAccountChange(accountInfo); 283 | }); 284 | return () => { 285 | ignoreFetch = true; 286 | connection.removeAccountChangeListener(subscription); 287 | }; 288 | } 289 | 290 | listWalletAdapters(): WalletAdapter[] { 291 | return this.walletContext.wallets.map((wallet) => { 292 | return { 293 | name: wallet.adapter.name, 294 | icon: wallet.adapter.icon, 295 | }; 296 | }); 297 | } 298 | 299 | selectWalletAdapter(wallet: WalletAdapter | null) { 300 | if (wallet) { 301 | return this.walletContext.select(wallet.name as WalletName); 302 | } else { 303 | return this.walletContext.disconnect(); 304 | } 305 | } 306 | 307 | getSessionMinLamports(): number { 308 | return this.sessionConfig.minLamports; 309 | } 310 | 311 | getSessionMaximalLamports(): number { 312 | return this.sessionConfig.maxLamports; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /frontend/src/engine/MagicBlockEngineProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { WalletProvider, useWallet } from "@solana/wallet-adapter-react"; 3 | import { Keypair } from "@solana/web3.js"; 4 | import { MagicBlockEngine } from "./MagicBlockEngine"; 5 | 6 | const SESSION_LOCAL_STORAGE = "magicblock-session-key"; 7 | const SESSION_MIN_LAMPORTS = 0.02 * 1_000_000_000; 8 | const SESSION_MAX_LAMPORTS = 0.05 * 1_000_000_000; 9 | 10 | const MagicBlockEngineContext = React.createContext( 11 | {} as MagicBlockEngine 12 | ); 13 | 14 | export function useMagicBlockEngine(): MagicBlockEngine { 15 | return React.useContext(MagicBlockEngineContext); 16 | } 17 | 18 | export function MagicBlockEngineProvider({ 19 | children, 20 | }: { 21 | children: React.ReactNode; 22 | }) { 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | } 29 | 30 | function MagicBlockEngineProviderInner({ 31 | children, 32 | }: { 33 | children: React.ReactNode; 34 | }) { 35 | const walletContext = useWallet(); 36 | 37 | const engine = React.useMemo(() => { 38 | let sessionKey; 39 | 40 | const sessionKeyString = localStorage.getItem(SESSION_LOCAL_STORAGE); 41 | if (sessionKeyString) { 42 | sessionKey = Keypair.fromSecretKey( 43 | Uint8Array.from(JSON.parse(sessionKeyString)) 44 | ); 45 | } else { 46 | sessionKey = Keypair.generate(); 47 | localStorage.setItem( 48 | SESSION_LOCAL_STORAGE, 49 | JSON.stringify(Array.from(sessionKey.secretKey)) 50 | ); 51 | } 52 | 53 | return new MagicBlockEngine(walletContext, sessionKey, { 54 | minLamports: SESSION_MIN_LAMPORTS, 55 | maxLamports: SESSION_MAX_LAMPORTS, 56 | }); 57 | }, [walletContext]); 58 | 59 | return ( 60 | 61 | {children} 62 | 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/engine/MagicBlockQueue.ts: -------------------------------------------------------------------------------- 1 | import { Transaction } from "@solana/web3.js"; 2 | 3 | import { MagicBlockEngine } from "./MagicBlockEngine"; 4 | 5 | export class MagicBlockQueue { 6 | private engine: MagicBlockEngine; 7 | private last?: Promise; 8 | 9 | constructor(engine: MagicBlockEngine) { 10 | this.engine = engine; 11 | this.last = undefined; 12 | } 13 | 14 | getSessionPayer() { 15 | return this.engine.getSessionPayer(); 16 | } 17 | 18 | async processSessionEphemTransaction( 19 | name: string, 20 | transaction: Transaction 21 | ): Promise { 22 | const engine = this.engine; 23 | const last = this.last; 24 | const next = (async function () { 25 | try { 26 | if (last !== undefined) { 27 | await last; 28 | } 29 | } catch (error) { 30 | // The error should be handled by another awaiter (from the return) 31 | } 32 | const expiration = new Promise((resolve) => 33 | setTimeout(() => resolve(""), 1000) 34 | ); 35 | const execution = engine.processSessionEphemTransaction( 36 | name, 37 | transaction 38 | ); 39 | return await Promise.race([expiration, execution]); 40 | })(); 41 | 42 | this.last = next; 43 | return next; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MagicBlock Labs 6 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/src/index.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | border: 0; 4 | margin: 0; 5 | padding: 0; 6 | 7 | display: flex; 8 | flex-shrink: 1; 9 | flex-grow: 0; 10 | flex-wrap: wrap; 11 | 12 | flex-direction: column; 13 | justify-content: center; 14 | align-items: stretch; 15 | 16 | line-height: 1.5; 17 | font-size: 16px; 18 | font-family: "Courier New", monospace; 19 | 20 | text-align: start; 21 | word-break: break-word; 22 | } 23 | 24 | html, 25 | body, 26 | #app { 27 | width: 100%; 28 | height: 100%; 29 | flex-wrap: nowrap; 30 | } 31 | head { 32 | display: none; 33 | } 34 | #app { 35 | background-color: black; 36 | color: white; 37 | } 38 | .Content { 39 | align-items: center; 40 | flex-grow: 1; 41 | } 42 | 43 | .Container, 44 | .ContainerOuter { 45 | padding: 4px; 46 | } 47 | .Container, 48 | .ContainerInner { 49 | > * { 50 | margin: 4px; 51 | } 52 | } 53 | .Centered { 54 | align-items: center; 55 | } 56 | .Horizontal { 57 | flex-direction: row; 58 | } 59 | -------------------------------------------------------------------------------- /frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { Route, Routes, HashRouter } from "react-router-dom"; 4 | 5 | import { MagicBlockEngineProvider } from "./engine/MagicBlockEngineProvider"; 6 | 7 | import { MenuBar } from "./components/menu/MenuBar"; 8 | 9 | import { PageHome } from "./components/page/PageHome"; 10 | import { PageCreate } from "./components/page/PageCreate"; 11 | import { PagePlay } from "./components/page/PagePlay"; 12 | 13 | import "./index.scss"; 14 | 15 | function App() { 16 | return ( 17 | 18 | 19 | 20 |
21 |
22 | 23 | } /> 24 | } /> 25 | } /> 26 | 27 |
28 |
29 |
30 |
31 | ); 32 | } 33 | 34 | createRoot(document.getElementById("app")).render(); 35 | -------------------------------------------------------------------------------- /frontend/src/states/gameCreate.ts: -------------------------------------------------------------------------------- 1 | import { Transaction } from "@solana/web3.js"; 2 | import { 3 | AddEntity, 4 | InitializeComponent, 5 | createAddEntityInstruction, 6 | createDelegateInstruction, 7 | } from "@magicblock-labs/bolt-sdk"; 8 | 9 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 10 | 11 | import { COMPONENT_GAME_PROGRAM_ID } from "./gamePrograms"; 12 | import { gameSystemGenerate } from "./gameSystemGenerate"; 13 | import { gameWorldGetOrCreate } from "./gameWorld"; 14 | 15 | export async function gameCreate( 16 | engine: MagicBlockEngine, 17 | onLog: (log: string) => void 18 | ) { 19 | // Choose the world we're using 20 | const worldPda = await gameWorldGetOrCreate(engine); 21 | // Create a new Entity 22 | onLog("Creating a new entity"); 23 | const addEntity = await AddEntity({ 24 | connection: engine.getConnectionChain(), 25 | payer: engine.getSessionPayer(), 26 | world: worldPda, 27 | }); 28 | // Initialize the game component 29 | onLog("Initializing a new component"); 30 | const initializeComponent = await InitializeComponent({ 31 | payer: engine.getSessionPayer(), 32 | entity: addEntity.entityPda, 33 | componentId: COMPONENT_GAME_PROGRAM_ID, 34 | }); 35 | // Delegate the game component 36 | onLog("Delegating to Ephem rollups"); 37 | const delegateComponentInstruction = createDelegateInstruction( 38 | { 39 | entity: addEntity.entityPda, 40 | account: initializeComponent.componentPda, 41 | ownerProgram: COMPONENT_GAME_PROGRAM_ID, 42 | payer: engine.getSessionPayer(), 43 | }, 44 | undefined, 45 | 1_000_000_000 // We don't want to auto-commit the state of the game 46 | ); 47 | // Execute all instructions at once 48 | onLog("Processing creation"); 49 | await engine.processSessionChainTransaction( 50 | "DelegateComponent", 51 | new Transaction() 52 | .add(addEntity.instruction) 53 | .add(initializeComponent.instruction) 54 | .add(delegateComponentInstruction) 55 | ); 56 | // Generate the game 57 | onLog("Generate the game"); 58 | await gameSystemGenerate(engine, addEntity.entityPda); 59 | // Entity PDA for later use 60 | onLog("Game is ready!"); 61 | return addEntity.entityPda; 62 | } 63 | -------------------------------------------------------------------------------- /frontend/src/states/gameFetch.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 3 | import { getComponentGameOnEphem } from "./gamePrograms"; 4 | 5 | export async function gameFetch(engine: MagicBlockEngine, gamePda: PublicKey) { 6 | const componentGame = getComponentGameOnEphem(engine); 7 | return componentGame.account.game.fetchNullable(gamePda); 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/states/gameList.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FindComponentPda, 3 | FindEntityPda, 4 | World, 5 | } from "@magicblock-labs/bolt-sdk"; 6 | 7 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 8 | 9 | import { getComponentGameOnChain } from "./gamePrograms"; 10 | import { gameWorldGetOrCreate } from "./gameWorld"; 11 | 12 | export async function gameList(engine: MagicBlockEngine, count: number) { 13 | const componentGame = getComponentGameOnChain(engine); 14 | const worldPda = await gameWorldGetOrCreate(engine); 15 | 16 | const world = await World.fromAccountAddress( 17 | engine.getConnectionChain(), 18 | worldPda 19 | ); 20 | 21 | let entityId = world.entities; 22 | 23 | const found: any[] = []; 24 | while (!entityId.isNeg() && found.length < count) { 25 | // Create a batch of accounts PDAs to read 26 | const batch: any[] = []; 27 | while (!entityId.isNeg() && batch.length <= 100) { 28 | const entityPda = FindEntityPda({ 29 | worldId: world.id, 30 | entityId: entityId, 31 | }); 32 | const gamePda = FindComponentPda({ 33 | componentId: componentGame.programId, 34 | entity: entityPda, 35 | }); 36 | batch.push({ 37 | entityId: entityId.toString(), 38 | entityPda: entityPda, 39 | gamePda: gamePda, 40 | }); 41 | entityId = entityId.subn(1); 42 | } 43 | // Fetch multiple games at the same time 44 | const games = await componentGame.account.game.fetchMultiple( 45 | batch.map((entry) => entry.gamePda) 46 | ); 47 | games.forEach((game, index) => { 48 | const entry = batch[index]; 49 | const entityId = entry.entityId; 50 | const entityPda = entry.entityPda; 51 | const gamePda = entry.gamePda; 52 | console.log("Check game", entityId, game); 53 | if (game && found.length < count) { 54 | found.push({ 55 | entityPda, 56 | entityId, 57 | gamePda, 58 | game, 59 | }); 60 | } 61 | }); 62 | } 63 | return found; 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/states/gameListen.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 3 | import { getComponentGameOnEphem } from "./gamePrograms"; 4 | import { gameSystemGenerate } from "./gameSystemGenerate"; 5 | import { gameLog } from "./gameLog"; 6 | 7 | export function gameListen( 8 | engine: MagicBlockEngine, 9 | entityPda: PublicKey, 10 | gamePda: PublicKey, 11 | setGame: (game: any) => void 12 | ) { 13 | const onGameValue = (gameData: any) => { 14 | // Log game info 15 | gameLog(gamePda, gameData); 16 | // Update state 17 | setGame(gameData); 18 | }; 19 | return engine.subscribeToEphemAccountInfo(gamePda, (accountInfo) => { 20 | // If the game doesn't exist in the ephemeral 21 | if (!accountInfo) { 22 | // try to nudge it to be downloaded into the ephemeral validator if it exists on chain 23 | gameSystemGenerate(engine, entityPda).then( 24 | (value) => console.log("nudge generate success", value), 25 | (reason) => console.log("nudge generate fail", reason.toString()) 26 | ); 27 | // Display an error for now 28 | return onGameValue(null); 29 | } 30 | // If we found the game, decode its state 31 | const coder = getComponentGameOnEphem(engine).coder; 32 | return onGameValue(coder.accounts.decode("game", accountInfo.data)); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/states/gameLog.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | export function gameLog(gamePda: PublicKey, gameData: any) { 4 | if (!gameData) { 5 | return; 6 | } 7 | 8 | const lines = []; 9 | 10 | const header = []; 11 | header.push("gameLog"); 12 | let status = "??"; 13 | if (gameData.status.finished !== undefined) { 14 | status = "(FINISHED)"; 15 | } else if (gameData.status.generate !== undefined) { 16 | status = "(GENERATE)"; 17 | } else if (gameData.status.lobby !== undefined) { 18 | status = "(LOBBY)"; 19 | } else if (gameData.status.playing !== undefined) { 20 | status = "(PLAYING)"; 21 | } 22 | header.push(status); 23 | header.push("<" + gameData.tickNextSlot.toString() + ">"); 24 | header.push(gamePda.toBase58()); 25 | lines.push(header.join(" ")); 26 | 27 | lines.push("--"); 28 | 29 | for (let i = 0; i < gameData.players.length; i++) { 30 | const player = gameData.players[i]; 31 | const parts = []; 32 | if (player.ready) { 33 | parts.push("[ READY ]"); 34 | } else { 35 | parts.push("[WAITING]"); 36 | } 37 | parts.push(player.authority.toBase58()); 38 | lines.push(parts.join(" ")); 39 | } 40 | 41 | lines.push("--"); 42 | 43 | for (let y = 0; y < gameData.sizeY; y++) { 44 | const parts = []; 45 | for (let x = 0; x < gameData.sizeX; x++) { 46 | const cell = gameData.cells[y * gameData.sizeX + x]; 47 | parts.push("|"); 48 | 49 | if (cell.owner.nobody !== undefined) { 50 | parts.push(" "); 51 | } else if (cell.owner.player !== undefined) { 52 | if (cell.owner.player[0] == 0) { 53 | parts.push("A"); 54 | } else if (cell.owner.player[0] == 1) { 55 | parts.push("B"); 56 | } else { 57 | parts.push("?"); 58 | } 59 | } else { 60 | parts.push("?"); 61 | } 62 | 63 | if (cell.kind.capital !== undefined) { 64 | parts.push("$"); 65 | } else if (cell.kind.city !== undefined) { 66 | parts.push("X"); 67 | } else if (cell.kind.field !== undefined) { 68 | parts.push(" "); 69 | } else if (cell.kind.mountain !== undefined) { 70 | parts.push("M"); 71 | } else if (cell.kind.forest !== undefined) { 72 | parts.push("F"); 73 | } else { 74 | parts.push("?"); 75 | } 76 | 77 | if (cell.strength == 0) { 78 | parts.push(" "); 79 | } else if (cell.strength < 10) { 80 | parts.push(cell.strength.toString()); 81 | } else { 82 | parts.push("+"); 83 | } 84 | } 85 | parts.push("|"); 86 | lines.push(parts.join("")); 87 | } 88 | console.log(lines.join("\n")); 89 | } 90 | -------------------------------------------------------------------------------- /frontend/src/states/gamePrograms.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 4 | 5 | import { Game } from "../../../backend/target/types/game"; 6 | import { Generate } from "../../../backend/target/types/generate"; 7 | import { Join } from "../../../backend/target/types/join"; 8 | import { Start } from "../../../backend/target/types/start"; 9 | import { Command } from "../../../backend/target/types/command"; 10 | import { Tick } from "../../../backend/target/types/tick"; 11 | import { Finish } from "../../../backend/target/types/finish"; 12 | 13 | import * as GameIdl from "../../../backend/target/idl/game.json"; 14 | import * as GenerateIdl from "../../../backend/target/idl/generate.json"; 15 | import * as JoinIdl from "../../../backend/target/idl/join.json"; 16 | import * as StartIdl from "../../../backend/target/idl/start.json"; 17 | import * as CommandIdl from "../../../backend/target/idl/command.json"; 18 | import * as TickIdl from "../../../backend/target/idl/tick.json"; 19 | import * as FinishIdl from "../../../backend/target/idl/finish.json"; 20 | 21 | const componentGame = GameIdl as Game; 22 | const systemGenerate = GenerateIdl as Generate; 23 | const systemJoin = JoinIdl as Join; 24 | const systemStart = StartIdl as Start; 25 | const systemCommand = CommandIdl as Command; 26 | const systemTick = TickIdl as Tick; 27 | const systemFinish = FinishIdl as Finish; 28 | 29 | export const COMPONENT_GAME_PROGRAM_ID = new PublicKey(componentGame.address); 30 | export const SYSTEM_GENERATE_PROGRAM_ID = new PublicKey(systemGenerate.address); 31 | export const SYSTEM_JOIN_PROGRAM_ID = new PublicKey(systemJoin.address); 32 | export const SYSTEM_START_PROGRAM_ID = new PublicKey(systemStart.address); 33 | export const SYSTEM_COMMAND_PROGRAM_ID = new PublicKey(systemCommand.address); 34 | export const SYSTEM_TICK_PROGRAM_ID = new PublicKey(systemTick.address); 35 | export const SYSTEM_FINISH_PROGRAM_ID = new PublicKey(systemFinish.address); 36 | 37 | export function getComponentGameOnChain(engine: MagicBlockEngine) { 38 | return engine.getProgramOnChain(componentGame); 39 | } 40 | 41 | export function getComponentGameOnEphem(engine: MagicBlockEngine) { 42 | return engine.getProgramOnEphem(componentGame); 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemCommand.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { 5 | SYSTEM_COMMAND_PROGRAM_ID, 6 | COMPONENT_GAME_PROGRAM_ID, 7 | } from "./gamePrograms"; 8 | 9 | import { MagicBlockQueue } from "../engine/MagicBlockQueue"; 10 | import { gameWorldGet } from "./gameWorld"; 11 | 12 | export async function gameSystemCommand( 13 | queue: MagicBlockQueue, 14 | entityPda: PublicKey, 15 | playerIndex: number, 16 | sourceX: number, 17 | sourceY: number, 18 | targetX: number, 19 | targetY: number, 20 | strengthPercent: number 21 | ) { 22 | const worldPda = gameWorldGet(); 23 | const applySystem = await ApplySystem({ 24 | authority: queue.getSessionPayer(), 25 | systemId: SYSTEM_COMMAND_PROGRAM_ID, 26 | world: worldPda, 27 | entities: [ 28 | { 29 | entity: entityPda, 30 | components: [ 31 | { 32 | componentId: COMPONENT_GAME_PROGRAM_ID, 33 | }, 34 | ], 35 | }, 36 | ], 37 | args: { 38 | player_index: playerIndex, 39 | source_x: sourceX, 40 | source_y: sourceY, 41 | target_x: targetX, 42 | target_y: targetY, 43 | strength_percent: strengthPercent, 44 | }, 45 | }); 46 | 47 | console.log( 48 | "gameSystemCommand.queued:", 49 | sourceX + "x" + sourceY, 50 | "->", 51 | targetX + "x" + targetY 52 | ); 53 | 54 | const dudu = await queue.processSessionEphemTransaction( 55 | "SystemCommand:" + 56 | playerIndex + 57 | " (" + 58 | sourceX + 59 | "x" + 60 | sourceY + 61 | "->" + 62 | targetX + 63 | "x" + 64 | targetY + 65 | ")", 66 | applySystem.transaction 67 | ); 68 | 69 | console.log( 70 | "gameSystemCommand.awaited:", 71 | sourceX + "x" + sourceY, 72 | "->", 73 | targetX + "x" + targetY 74 | ); 75 | 76 | return dudu; 77 | } 78 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemFinish.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 5 | import { 6 | SYSTEM_FINISH_PROGRAM_ID, 7 | COMPONENT_GAME_PROGRAM_ID, 8 | } from "./gamePrograms"; 9 | import { gameWorldGet } from "./gameWorld"; 10 | 11 | export async function gameSystemFinish( 12 | engine: MagicBlockEngine, 13 | entityPda: PublicKey, 14 | playerIndex: number 15 | ) { 16 | const worldPda = gameWorldGet(); 17 | const applySystem = await ApplySystem({ 18 | authority: engine.getSessionPayer(), 19 | systemId: SYSTEM_FINISH_PROGRAM_ID, 20 | world: worldPda, 21 | entities: [ 22 | { 23 | entity: entityPda, 24 | components: [ 25 | { 26 | componentId: COMPONENT_GAME_PROGRAM_ID, 27 | }, 28 | ], 29 | }, 30 | ], 31 | args: { 32 | player_index: playerIndex, 33 | }, 34 | }); 35 | return await engine.processSessionEphemTransaction( 36 | "SystemFinish:" + playerIndex, 37 | applySystem.transaction 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemGenerate.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 5 | import { 6 | COMPONENT_GAME_PROGRAM_ID, 7 | SYSTEM_GENERATE_PROGRAM_ID, 8 | } from "./gamePrograms"; 9 | import { gameWorldGet } from "./gameWorld"; 10 | 11 | export async function gameSystemGenerate( 12 | engine: MagicBlockEngine, 13 | entityPda: PublicKey 14 | ) { 15 | const worldPda = gameWorldGet(); 16 | const applySystem = await ApplySystem({ 17 | authority: engine.getSessionPayer(), 18 | systemId: SYSTEM_GENERATE_PROGRAM_ID, 19 | world: worldPda, 20 | entities: [ 21 | { 22 | entity: entityPda, 23 | components: [ 24 | { 25 | componentId: COMPONENT_GAME_PROGRAM_ID, 26 | }, 27 | ], 28 | }, 29 | ], 30 | }); 31 | return await engine.processSessionEphemTransaction( 32 | "SystemGenerate", 33 | applySystem.transaction 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemJoin.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 5 | import { 6 | COMPONENT_GAME_PROGRAM_ID, 7 | SYSTEM_JOIN_PROGRAM_ID, 8 | } from "./gamePrograms"; 9 | import { gameWorldGet } from "./gameWorld"; 10 | 11 | export async function gameSystemJoin( 12 | engine: MagicBlockEngine, 13 | entityPda: PublicKey, 14 | playerIndex: number, 15 | join: boolean 16 | ) { 17 | const worldPda = gameWorldGet(); 18 | const applySystem = await ApplySystem({ 19 | authority: engine.getSessionPayer(), 20 | systemId: SYSTEM_JOIN_PROGRAM_ID, 21 | world: worldPda, 22 | entities: [ 23 | { 24 | entity: entityPda, 25 | components: [ 26 | { 27 | componentId: COMPONENT_GAME_PROGRAM_ID, 28 | }, 29 | ], 30 | }, 31 | ], 32 | args: { 33 | player_index: playerIndex, 34 | join, 35 | }, 36 | }); 37 | return await engine.processSessionEphemTransaction( 38 | "SystemJoin:" + playerIndex, 39 | applySystem.transaction 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemStart.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 5 | import { 6 | COMPONENT_GAME_PROGRAM_ID, 7 | SYSTEM_START_PROGRAM_ID, 8 | } from "./gamePrograms"; 9 | import { gameWorldGet } from "./gameWorld"; 10 | 11 | export async function gameSystemStart( 12 | engine: MagicBlockEngine, 13 | entityPda: PublicKey 14 | ) { 15 | const worldPda = gameWorldGet(); 16 | const applySystem = await ApplySystem({ 17 | authority: engine.getSessionPayer(), 18 | systemId: SYSTEM_START_PROGRAM_ID, 19 | world: worldPda, 20 | entities: [ 21 | { 22 | entity: entityPda, 23 | components: [ 24 | { 25 | componentId: COMPONENT_GAME_PROGRAM_ID, 26 | }, 27 | ], 28 | }, 29 | ], 30 | }); 31 | return await engine.processSessionEphemTransaction( 32 | "SystemStart", 33 | applySystem.transaction 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/states/gameSystemTick.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApplySystem } from "@magicblock-labs/bolt-sdk"; 3 | 4 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 5 | import { 6 | COMPONENT_GAME_PROGRAM_ID, 7 | SYSTEM_TICK_PROGRAM_ID, 8 | } from "./gamePrograms"; 9 | import { gameWorldGet } from "./gameWorld"; 10 | 11 | export async function gameSystemTick( 12 | engine: MagicBlockEngine, 13 | entityPda: PublicKey 14 | ) { 15 | const worldPda = gameWorldGet(); 16 | const applySystem = await ApplySystem({ 17 | authority: engine.getSessionPayer(), 18 | systemId: SYSTEM_TICK_PROGRAM_ID, 19 | world: worldPda, 20 | entities: [ 21 | { 22 | entity: entityPda, 23 | components: [ 24 | { 25 | componentId: COMPONENT_GAME_PROGRAM_ID, 26 | }, 27 | ], 28 | }, 29 | ], 30 | }); 31 | return await engine.processSessionEphemTransaction( 32 | "SystemTick", 33 | applySystem.transaction 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/states/gameWorld.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createInitializeRegistryInstruction, 3 | FindRegistryPda, 4 | InitializeNewWorld, 5 | } from "@magicblock-labs/bolt-sdk"; 6 | import { MagicBlockEngine } from "../engine/MagicBlockEngine"; 7 | import { PublicKey, Transaction } from "@solana/web3.js"; 8 | 9 | const EXPECTED_WORLD_PDA = new PublicKey( 10 | "JBupPMmv4zaXa5c8EdubsCPvoHZwCK7mwnDfmfs8dC5Y" 11 | ); 12 | 13 | export function gameWorldGet(): PublicKey { 14 | return EXPECTED_WORLD_PDA; 15 | } 16 | 17 | export async function gameWorldGetOrCreate( 18 | engine: MagicBlockEngine 19 | ): Promise { 20 | // If possible, try to get an airdrop for when we are working with devnet/testnet/localnet 21 | try { 22 | await engine.fundSessionFromAirdrop(); 23 | } catch (error) { 24 | console.log("Could not airdrop to fund the session key"); 25 | } 26 | // Check if the registry exists, or try to create it 27 | const registryPda = FindRegistryPda({}); 28 | const registryAccountInfo = await engine.getChainAccountInfo(registryPda); 29 | if (registryAccountInfo === null) { 30 | const initializeRegistryIx = createInitializeRegistryInstruction({ 31 | registry: registryPda, 32 | payer: engine.getSessionPayer(), 33 | }); 34 | await engine.processSessionChainTransaction( 35 | "InitializeRegistry", 36 | new Transaction().add(initializeRegistryIx) 37 | ); 38 | console.log("Initialized Registry"); 39 | } 40 | // Check if the world exists, or try to create it 41 | const worldPda = EXPECTED_WORLD_PDA; 42 | const worldAccountInfo = await engine.getChainAccountInfo(worldPda); 43 | if (worldAccountInfo === null) { 44 | const initializeNewWorld = await InitializeNewWorld({ 45 | connection: engine.getConnectionChain(), 46 | payer: engine.getSessionPayer(), 47 | }); 48 | console.log("InitializeNewWorld", initializeNewWorld); 49 | await engine.processSessionChainTransaction( 50 | "InitializeNewWorld", 51 | initializeNewWorld.transaction 52 | ); 53 | } 54 | // By now, we hopefully have a valid world we can use as expected 55 | return worldPda; 56 | } 57 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "commonjs", 6 | "target": "es6", 7 | "jsx": "react", 8 | "resolveJsonModule": true 9 | }, 10 | "include": ["src", "index.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const HTMLWebpackPlugin = require("html-webpack-plugin"); 4 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 5 | 6 | module.exports = (env, argv) => { 7 | let isDevelopment = (process.env.NODE_ENV = argv["mode"]) !== "production"; 8 | 9 | return { 10 | entry: "./src/index.tsx", 11 | 12 | mode: isDevelopment ? "development" : "production", 13 | 14 | output: { 15 | path: path.resolve(__dirname, "dist"), 16 | filename: "[contenthash].[name].js", 17 | }, 18 | 19 | resolve: { 20 | modules: ["node_modules"], 21 | // Add '.ts' and '.tsx' as resolvable extensions. 22 | extensions: [".json", ".js", ".ts", ".tsx", ".js", ".css", ".scss"], 23 | }, 24 | 25 | devServer: { 26 | static: { 27 | directory: path.join(__dirname, "dist/"), 28 | }, 29 | }, 30 | 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.ts(x?)$/, 35 | exclude: /node_modules/, 36 | use: [{ loader: "ts-loader" }], 37 | }, 38 | { 39 | test: /\.scss$/, 40 | use: ["style-loader", "css-loader", "sass-loader"], 41 | }, 42 | { 43 | test: /\.(png|jpe?g|gif|svg)$/, 44 | use: ["file-loader"], 45 | }, 46 | ], 47 | }, 48 | 49 | plugins: [ 50 | new CleanWebpackPlugin(), 51 | new HTMLWebpackPlugin({ 52 | template: path.join(__dirname, "./src/index.html"), 53 | }), 54 | // Shims necessary 55 | new webpack.ProvidePlugin({ 56 | Buffer: ["buffer", "Buffer"], 57 | process: "process/browser", 58 | }), 59 | ], 60 | }; 61 | }; 62 | --------------------------------------------------------------------------------