├── .gitignore ├── .gitmodules ├── .prettierrc ├── README.md ├── builders_guide.md ├── dapp.md ├── img ├── custom_upgrade_final.png ├── custom_upgrade_init.png ├── enter_game.png ├── hardhat_prod_deploy.png ├── link.png ├── log_in.png ├── netlify_deploy.png ├── netlify_import.png ├── netlify_prod_deploy.png ├── new_key.png ├── pass.png ├── site_id.png ├── stop_auto_publishing.png └── test.png ├── netlify.toml ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Local Netlify folder 4 | .netlify 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "client"] 2 | path = client 3 | url = https://github.com/darkforest-eth/client 4 | [submodule "eth"] 5 | path = eth 6 | url = https://github.com/darkforest-eth/eth 7 | [submodule "circuits"] 8 | path = circuits 9 | url = https://github.com/darkforest-eth/circuits.git 10 | [submodule "packages"] 11 | path = packages 12 | url = https://github.com/darkforest-eth/packages 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "jsxSingleQuote": true, 4 | "trailingComma": "es5", 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true, 8 | "printWidth": 100, 9 | "overrides": [ 10 | { 11 | "files": "*.sol", 12 | "options": { 13 | "tabWidth": 4, 14 | "useTabs": false, 15 | "singleQuote": false, 16 | "bracketSpacing": false, 17 | "explicitTypes": "always" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # darkforest-local 2 | 3 | The Dark Forest client deploy process is set up for players to easily fork the client and connect to the mainnet game, but that design decision currently makes it difficult to run a playable version of the game locally. This repository provides a setup for running a local game with just a few steps. 4 | 5 | ## Builder's Guide 6 | 7 | For a comprehensive tutorial on using this repository to make custom local games and deploy them, check out the [Builder's Guide](builders_guide.md). 8 | 9 | ## How `darkforest-local` works 10 | 11 | This repo uses [submodules](.gitmodules) to pair the [Dark Forest Ethereum backend](https://github.com/darkforest-eth/eth) with the [Dark Forest TypeScript frontend](https://github.com/darkforest-eth/client) so you can launch a local game. 12 | 13 | It also uses Yarn, a package manager (like `npm`) that allows for multiple [workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces) to exist within a project. 14 | 15 | The workspaces allows each [submodule](.gitmodules) to have their own packages and configuration. 16 | Yarn places all of the packages for each submodule in the top level `node_modules/` folder. 17 | 18 | ## Requirements 19 | * Install `node >= 14` (You can consider using [nvm](https://github.com/nvm-sh/nvm)) 20 | * Install [Yarn](https://classic.yarnpkg.com/en/docs/install) 21 | 22 | ### Quickstart for running a local game 23 | 1. Fork [darkforest-local](https://github.com/projectsophon/darkforest-local) to your GitHub account 24 | 2. `git clone --recurse-submodules https://github.com//darkforest-local.git` 25 | 3. If you didn't clone with `--recurse-submodules` or already have a cloned version: `git submodule update --init --recursive --remote --merge` 26 | 4. `yarn` 27 | 5. `yarn start` 28 | 29 | ### If you plan to make changes to `darkforest-local` 30 | 1. Fork [darkforest-local](https://github.com/projectsophon/darkforest-local) to your GitHub account 31 | 2. Fork [darkforest-eth/eth](https://github.com/darkforest-eth/eth) to your GitHub account 32 | 3. Fork [darkforest-eth/client](https://github.com/darkforest-eth/client) to your GitHub account 33 | 4. Fork [darkforest-eth/circuits](https://github.com/darkforest-eth/circuits) to your GitHub account 34 | 5. Fork [darkforest-eth/packages](https://github.com/darkforest-eth/packages) to your GitHub account 35 | 4. Clone your darkforest-local repo: `git clone https://github.com//darkforest-local.git` 36 | 5. Update the `.gitmodules` file to point to your new forks of `eth`, `client`, `circuits`, and `packages` 37 | ex: 38 | * `url = https://github.com/darkforest-eth/eth` => `url = https://github.com/cha0sg0d/eth` 39 | * `url = https://github.com/darkforest-eth/client` => `url = https://github.com/cha0sg0d/client` 40 | * `url = https://github.com/darkforest-eth/circuits` => `url = https://github.com/cha0sg0d/circuits` 41 | * `url = https://github.com/darkforest-eth/packages` => `url = https://github.com/cha0sg0d/packages` 42 | 6. Fetch the code from the submodules 43 | * `git submodule update --init --recursive` 44 | 7. Add new branches for developing: 45 | - The `darkforest-local` monorepo detaches the submodules from their current HEADs. If you want to save your changes (for example, if you're testing an new contract in `eth`), you'll need to make a new branch in these submodules. 46 | 1. `cd eth` 47 | 1. `git checkout -b ` 48 | 2. `cd client` 49 | 1. `git checkout -b ` 50 | 3. `cd circuits` 51 | 1. `git checkout -b ` 52 | 4. `cd packages` 53 | 1. `git checkout -b ` 54 | 7. Install packages and dependencies 55 | * `yarn` 56 | 8. Start a game 57 | * `yarn start` 58 | 59 | 60 | ## Run a local game 61 | 62 | - Running `yarn start`, will 1) start a local node, 2) deploy the contracts, and 3) run the local client in dev mode 63 | - When finished, the process should pop up your browser to the game client at http://localhost:8081/ 64 | 65 | - Here are 3 private keys with 100 ETH each to use in the game: 66 | 1. `0x044C7963E9A89D4F8B64AB23E02E97B2E00DD57FCB60F316AC69B77135003AEF` 67 | 2. `0x523170AAE57904F24FFE1F61B7E4FF9E9A0CE7557987C2FC034EACB1C267B4AE` 68 | 3. `0x67195c963ff445314e667112ab22f4a7404bad7f9746564eb409b9bb8c6aed32` 69 | 70 | 71 | ## Static deployment of Dark Forest (no webserver) 72 | 73 | If you want to deploy the contracts (with whatever modifications you want) to mainnet, and client 74 | code (with whatever modifications you want), you can follow these instructions. The reasons you 75 | might want to do this could be one of: 76 | 77 | - You want to run a community round of Dark Forest 78 | - You want to run a multiplayer round with some friends 79 | - etc. 80 | 81 | Deploying to production is quite similar to deploying a local version of the game, but I'm going to 82 | walk through the steps explicitly here. 83 | 84 | ### Step 1: Deploy contracts 85 | 86 | Unlike in development mode, in production mode you will need to create a `.env` file in both the 87 | `client/` and `eth/` submodules. You can find the set of environment variables you will need to 88 | populate those `.env` files with in their adjacent `.env.example` files. 89 | 90 | > **Danger!** The `.env` files ARE in the respective `.gitignore`s of the aforementioned submodules, 91 | > however you should also manually make sure that they don't end up checked into your repository. 92 | 93 | To randomly generate a new deployer private key and mnemonic for `eth/.env`, run 94 | ```bash 95 | yarn workspace eth hardhat wallet:new 96 | ``` 97 | You should see something like this: 98 | 99 | ![yarn workspace eth hardhat wallet:new](img/new_key.png) 100 | 101 | 102 | To deploy the contracts, you will need to run the following command: 103 | 104 | ```bash 105 | yarn workspace eth hardhat:prod deploy 106 | ``` 107 | 108 | You should see something like this: 109 | 110 | ![yarn workspace eth hardhat:prod deploy](img/hardhat_prod_deploy.png) 111 | 112 | To find out more information about the `contracts` submodule, you can look here: 113 | [https://github.com/darkforest-eth/eth](https://github.com/darkforest-eth/eth). 114 | 115 | ### Step 2: Deploy client website 116 | 117 | To deploy the website interface, you may either self-host, or use the same infra that we use - 118 | [Netlify](https://www.netlify.com/). This is a simple option, and free for up to some amount of 119 | gigabytes of bandwidth per month, which has often been enough for us. 120 | 121 | To use Netlify: 122 | 123 | - Make a new [Netlify account](https://app.netlify.com/signup) using your Github profile. 124 | - [Import](https://app.netlify.com/start) your forked **darkforest-local** repo as a new Netlify site. 125 | 126 | - During Step 3 of the import (Site settings, and deploy!): 127 | - Build command: `yarn workspace client build` 128 | - Publish directory: `client/dist` 129 | 130 | Your initial settings should look like this: 131 | 132 | ![netlify_settings](img/netlify_import.png) 133 | 134 | *Importing from Github will automatically trigger an initial build by Netlify, which will take ~5 min.* 135 | 136 | - Install the Netlify CLI 137 | - `npm install netlify-cli -g` 138 | - Login to your account 139 | - `netlify login` 140 | - Connect to your app 141 | - `netlify link` 142 | 143 | ![Netlify link](img/link.png) 144 | 145 | - Choose *Enter a site ID* 146 | 147 | ![Site ID](img/site_id.png) 148 | 149 | - Input the id that you find by clicking on your new site on Netlify. In this example, the id is `jolly-hermann-3947ca` 150 | - Now that your site is linked, you can deploy to Netlify from the CLI. 151 | - `yarn deploy:client:prod` 152 | - If successful, you will see something like this. 153 | 154 | ![deploy_success](img/netlify_deploy.png) 155 | - By default, all future changes to the branch you specified in the settings will result in an automatic deploy (in this case `master`). 156 | - You can still choose to deploy manually (via `yarn deploy:client:prod`). 157 | - Additionally, if you want to stop auto-publishing via git updates, see this image: 158 | ![stop_auto_publishing](img/stop_auto_publishing.png) 159 | 160 | ### Step 3: Allow player addresses 161 | 162 | The default implementation of Dark Forest ships with a [whitelist 163 | contract](https://github.com/darkforest-eth/eth/blob/master/contracts/Whitelist.sol). When we 164 | deployed the contracts in Step 1, amongst them was this whitelist contract. A 'static' deployment of 165 | Dark Forest (no web server) isn't capable of submitting a whitelisting transaction on behalf of the 166 | user (which lets a given burner address into the game and drips it a fraction of an xDAI). 167 | 168 | This means that if you want to let players into your game, you must first collect their 169 | addresses. 170 | 171 | If you want to generate a burner wallet on the CLI for testing, follow the same steps from [Step 1](#step-1-deploy-contracts): 172 | 173 | ```bash 174 | yarn workspace eth hardhat:dev wallet:new 175 | ``` 176 | 177 | 178 | Once you collect the addresses of the people you want to let into your world, you can register them 179 | individually via the following command 180 | 181 | ```bash 182 | yarn workspace eth hardhat:prod whitelist:register --address $BURNER_WALLET_ADDRESS 183 | # where $BURNER_WALLET_ADDRESS is the network address of the player you want to allow to play in 184 | # your deployment of the game. 185 | ``` 186 | 187 | If everything worked properly, you should be able to log in: 188 | 189 | ![log in](img/log_in.png) 190 | 191 | Then, once you've found a home planet, you should be able to enter the game! 192 | 193 | ![enter game](img/enter_game.png) 194 | 195 | ### Step 4: Play your game 196 | 197 | - Now go the URL given by Netlify → something like [https://jolly-hermann-3947ca.netlify.app/](https://jolly-hermann-3947ca.netlify.app/) and test out your game! Login with one of the private keys for the addresses that were whitelisted. Make some moves and test that everything works as expected. 198 | 199 | ## Update to latest Dark Forest code 200 | 201 | If theres been new Dark Forest updates released and we haven't yet updated this repo yet, it is possible you can get away with updating yourself by running `git submodule update --remote --merge` and remember to run `yarn` again. 202 | 203 | ## For maintainers of the repo updating to latest Dark Forest code 204 | 205 | - [Update the submodules](#update-to-latest-dark-forest-code). 206 | - Replace the root `yarn.lock` with the `yarn.lock` from `eth/` or `client/` 207 | - Run `yarn`. 208 | - Finally add all changes, commit, and PR. -------------------------------------------------------------------------------- /builders_guide.md: -------------------------------------------------------------------------------- 1 | # Community Round Builder’s Guide 2 | 3 | # Intro 4 | 5 | - This guide will teach Dark Forest enthusiasts how to build and deploy their own rounds. 6 | - It will also provide a high-level overview of the Dark Forest blockchain application. If are new to the Dark Forest codebase, please scan the [dapp breakdown document](dapp.md) first. 7 | 8 | ## Why should you run a community round? 9 | 10 | - You can play Dark Forest whenever you want. 11 | - You can customize the game with your own reward system, user interface, rulesets, and/or constraints. 12 | - It’s super fun. 13 | 14 | # Guide Structure 15 | 16 | ## Main Quests 17 | 18 | 0. [Download the correct code and install dependencies](#quest-0-installation-and-setup) 19 | 1. [Deploy a local game](#quest-1-local-game) 20 | 2. [Deploy a local game with a unique configuration via `darkforest.toml`](#quest-2-local-game-with-custom-configuration) 21 | 3. [Deploy a local game with a modified client and contracts](#quest-3-local-game-with-custom-client-and-contracts) 22 | 4. [Publish your local game to production on xDai and Netlify](#quest-4-deploy-contracts-and-client-to-production) 23 | 24 | ## Side Quests 25 | 26 | 1. [Deploy the subgraph](#side-quest-1-deploying-the-subgraph) 27 | 2. [Auto whitelisting with no server](#side-quest-2-auto-whitelisting-with-no-server) 28 | 3. [Publishing packages for your new contracts](#side-quest-3-publishing-your-new-contracts-as-an-npm-package) 29 | 30 | ## Knowledge Prerequisites 31 | 32 | - How to [clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) git repositories. 33 | - Good git [practices](#appendix-1-using-git-effectively) 34 | - For Main Quest 3: a small amount of Solidity and TypeScript knowledge. 35 | 36 | # Quest 0: Installation and Setup 37 | 38 | 1. Install [git](https://git-scm.com/downloads) to your local machine if you don’t have it already. 39 | 2. Make a GitHub [account](https://github.com/). 40 | 3. Clone the repo and install submodules and dependencies: 41 | 1. If you’re here to experiment with a local game and learn about the codebase, follow the [Quickstart for running a local game](README.md#quickstart-for-running-a-local-game) instructions. 42 | 2. If you’re here to deploy a community round to production, follow the [If you plan to make changes to darkforest-local](README.md#if-you-plan-to-make-changes-to-darkforest-local) instructions. 43 | 3. Don’t run `yarn start` just yet - we will practice testing the smart contracts before running a local game in Quest 1. 44 | 45 | # Quest 1: Local Game 46 | 47 | - We are going to learn how run a local game of Dark Forest with no changes to the current configuration. 48 | 49 | ## 1.0 Run Hardhat tests 50 | 51 | - Read a quick [overview](dapp.md#what-is-hardhat) of the Hardhat deployment environment. 52 | 53 | - This is not strictly necessary, but a good sanity check to make sure that the current smart contracts are passing the existing tests: 54 | `yarn workspace eth test` 55 | 56 | - You should see output like this: 57 | 58 | ![Hardhat Test](img/test.png) 59 | 60 | ![Hardhat Pass](img/pass.png) 61 | 62 | - If everything looks good, the Solidity backend is ready to go. 63 | 64 | 65 | ## 1.1 Run a local game 66 | 67 | - This is another sanity check to make sure the client and game have no obvious bugs. 68 | - Run `yarn start` 69 | - You should have already run `yarn` in Step 0.3. Run `yarn` if you didn't already. 70 | - In the game, test out the play. If you have any problems, check out the [Troubleshooting](#troubleshooting) section. Find a home planet, make a few moves, reveal a planet, etc. If everything looks good, you can be reasonably confident that everything will work in production. 71 | - To stop the local Hardhat network and kill the client, type `CTRL C` (^C) in your terminal. 72 | 73 | # Quest 2: Local game with custom configuration 74 | 75 | - For this quest, you will configure the universe to be a race to the center with one objective: *Be the first person to capture the Level 9 Planet at coordinate (0,0).* 76 | 77 | ## 2.0 Understand `darkforest.toml` 78 | 79 | - Now we are going to make some changes to the game via the settings file. This file is called `darkforest.toml` and lives in the [eth](https://github.com/darkforest-eth/eth/) repository. 80 | - The `darkforest.toml` is useful because it allows you to configure a large number game parameters without writing a single line of code. Understanding what each parameter means is also a great way to get acquainted with the codebase. 81 | - Read the explanation of each setting in this [gist](https://gist.github.com/cha0sg0d/2d4d9b95a5f90c1b0e285b601b850827). 82 | - *Note: Parameters that affect shrinking are not in the base codebase, so can't be used for this tutorial*. 83 | 84 | ## 2.1 Configure `darkforest.toml` 85 | 86 | - Duplicate the existing the `darkforest.toml` file. 87 | - Rename it `darkforest.localhost.toml` 88 | - *Note: The settings loader will [prefer](https://github.com/darkforest-eth/eth/blob/master/settings.ts#L260) a toml file that includes your network name over the one without.* 89 | - *If you want to deploy your .toml changes to production, put them in `darkforest.xdai.toml`.* 90 | 91 | - Add the following to `darkforest.localhost.toml`: 92 | 93 | ```toml 94 | # Add lvl9 admin planet at (0,0) 95 | [[planets]] 96 | x = 0 97 | y = 0 98 | level = 9 99 | planetType = 0 100 | requireValidLocationId = false 101 | revealLocation = true 102 | ``` 103 | 104 | ## 2.3 Run Hardhat tests 105 | 106 | - Same as in Step 1.0 107 | 108 | ## 2.4 Run a local game 109 | 110 | - Same as 1.2, but check to see that there is a level 9 planet at location (0,0) when you join the universe. 111 | - The existing deploy script should automatically create the level 9 planet because it calls the Hardhat `createPlanets` task. 112 | 113 | ## 2.5 Play your game 114 | 115 | - Same as 1.1, but now you’re in a custom universe that you designed! 116 | - Without writing even one line of code, you’ve just made a custom mode of Dark Forest. Next, we are going to learn how to modify the game even further. 117 | 118 | # Quest 3: Local game with custom client and contracts 119 | 120 | - For this quest, you will create a Dark Forest game with the following custom rule change: All planets can be upgraded, not just planets of type PLANET. 121 | - If you are new to Dark Forest, there are 5 planet types: Planet, Asteroid, Foundry, Spacetime Rip, and Quasar. Currently, only the Planet type can be upgraded to improve its defense, range, or speed. 122 | - We will add a new setting, `UPGRADEABLE_PLANETS` that is an array of 5 booleans. If an element is true, the corresponding planet type can be upgraded. If an element is false, the corresponding planet type cannot be upgraded. 123 | - Before you get started, review the [structure](dapp.md#client-overview) of the Dark Forest codebase. 124 | 125 | ## 3.0 Set up a new config variable 126 | 127 | - We want future users to be able to control planet upgrades from the `darkforest.toml` file. This allows for easy customization of the game for non-technical users. 128 | 129 | ### 3.0.1 Add a new `UPGRADEABLE_PLANETS` variable to the contracts 130 | 131 | - With these edits, we are simply adding a new constant to the codebase that will be set before the contracts are deployed in the `darkforest.localhost.toml`. To have a variable reflected across the smart contracts requires making changes in a few places: 132 | - In `darkforest.localhost.toml`: 133 | 134 | ```toml 135 | # Corresponds to [Planet, Asteroid, Foundry, Rip, Quasar] 136 | UPGRADEABLE_PLANETS = [true, true, false, false, false] 137 | ``` 138 | 139 | - In `contracts/DarkForestTypes.sol`: 140 | 141 | ```solidity 142 | struct GameConstants { 143 | ... 144 | bool[5] UPGRADEABLE_PLANETS; 145 | ... 146 | } 147 | ``` 148 | 149 | ```solidity 150 | struct DfInitArgs { 151 | ... 152 | bool[5] UPGRADEABLE_PLANETS; 153 | ... 154 | } 155 | ``` 156 | 157 | - In `contracts/DarkForestCore.sol`: 158 | 159 | ```solidity 160 | // ~ line 92 161 | s.gameConstants = DarkForestTypes.GameConstants({ 162 | ... 163 | UPGRADEABLE_PLANETS: initArgs.UPGRADEABLE_PLANETS 164 | }) 165 | 166 | ``` 167 | 168 | - In `settings.ts`: 169 | 170 | ```typescript 171 | // ~ line 151 172 | UPGRADEABLE_PLANETS: yup.array(yup.boolean()).length(5).default([true,false,false,false,false]) 173 | ``` 174 | 175 | - Run `yarn hardhat workspace eth compile` to make sure that the contract compiles successfully. 176 | - *See contract too big warning in [Troubleshooting](#troubleshooting) if needed.* 177 | 178 | ## 3.1 Write a Hardhat test to confirm existence of new config 179 | 180 | ### 3.1.1 Add a new fixture for testing an upgradeable planets universe 181 | 182 | - *Quickly Learn about [fixtures](https://ethereum-waffle.readthedocs.io/en/latest/fixtures.html).* 183 | - Add to `eth/test/utils/WorldConstants.ts`: 184 | 185 | ```typescript 186 | export const customUpgradeInitializers = { 187 | ...initializers, 188 | UPGRADEABLE_PLANETS: [true, true, false, false, false] 189 | // We're adding the ability to upgrade asteroids! 190 | } 191 | ``` 192 | 193 | - Add to `eth/test/utils/TestWorld.ts`: 194 | 195 | ```typescript 196 | import { customUpgradeInitializers } from './WorldConstants'; 197 | 198 | export function customUpgradeWorldFixture(): Promise { 199 | return initializeWorld({ 200 | initializers: customUpgradeInitializers, 201 | enableWhitelist: false, 202 | }); 203 | } 204 | ``` 205 | 206 | ### 3.1.2 Make a new file for testing 207 | 208 | - Duplicate `DFUpgrade.test.ts` and name it `DFCustomUpgrade.test.ts` 209 | - In `eth/test/DFCustomUpgrade.test.ts`: 210 | - Rename the test 211 | 212 | ```diff 213 | - describe('DarkForestUpgrade', function () { 214 | + describe('DarkForestCustomUpgrade', function () { 215 | ``` 216 | 217 | 218 | ### 3.1.3 Switch the world fixture in the new test file 219 | 220 | - In `eth/test/DFCustomUpgrade.test.ts`: 221 | 222 | ```diff 223 | import {..., customUpgradeWorldFixture } from './utils/TestWorld' 224 | 225 | beforeEach('load fixture', async function () { 226 | - world = await fixtureLoader(defaultWorldFixture) 227 | + world = await fixtureLoader(customUpgradeWorldFixture); 228 | }); 229 | ``` 230 | 231 | 232 | ### 3.2.3 Write a test to confirm a new config appears 233 | 234 | - In `eth/test/DFCustomUpgrade.test.ts`: 235 | 236 | ```typescript 237 | ... // near line 234 238 | it('should recognize upgradeable planets config', async function (){ 239 | const gameConstants = await world.contracts.core.gameConstants() 240 | const expected = [true,true,false,false,false]; 241 | expect(gameConstants.UPGRADEABLE_PLANETS).to.eql(expected); 242 | }); 243 | 244 | ``` 245 | 246 | - *Note: To learn more about .eql vs .equal and Chai, check out this [doc](https://ethereum-waffle.readthedocs.io/en/latest/matchers.html).* 247 | 248 | ### 3.2.4 Run that test 249 | 250 | - `yarn workspace eth hardhat test test/DFCustomUpgrade.test.ts` 251 | 252 | ![Initial Custom Upgrade Output](img/custom_upgrade_init.png) 253 | 254 | - A successful result should look like this ^. 255 | 256 | ## 3.2 Add the business logic to the `DarkForestPlanet.sol` 257 | 258 | - Now that we have a basic test and a new config variable, we actually are going to change the rules of Dark Forest! 259 | - In `contracts/DarkForestPlanet.sol` 260 | 261 | ```diff 262 | // ~line 323 263 | require( 264 | - planet.planetType == DarkForestTypes.PlanetType.PLANET, 265 | - "Can only upgrade regular planets" 266 | + s().gameConstants.UPGRADEABLE_PLANETS[uint256(planet.planetType)], 267 | + "Can only upgrade allowed planet types" 268 | ); 269 | ``` 270 | 271 | - We just changed which planets can be upgraded! 272 | 273 | ### 3.3 Write new Hardhat tests for the new logic 274 | 275 | ### 3.3.1 Modify the `should reject upgrade on silver mine, ...` test. 276 | 277 | - This test should now only reject upgrade on planets that are *marked false* in the `UPGRADEABLE_PLANETS` settings. 278 | - When you are done, rename the test `should reject upgrade on planets that are NOT upgradeable`. 279 | 280 | - Try this on your own before peeking! 281 |
282 | Solution 283 | 284 | ```typescript 285 | it('should reject upgrade on planets that are NOT upgradeable', async function () { 286 | await world.user1Core.initializePlayer(...makeInitArgs(SPAWN_PLANET_1)); 287 | 288 | // conquer the special planets 289 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, LVL1_ASTEROID_2); 290 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, LVL3_SPACETIME_1); 291 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, ARTIFACT_PLANET_1); 292 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, LVL1_QUASAR); 293 | 294 | // fill up the special planets with silver 295 | await feedSilverToCap(world, world.user1Core, LVL1_ASTEROID_2, LVL3_SPACETIME_1); 296 | await feedSilverToCap(world, world.user1Core, LVL1_ASTEROID_2, ARTIFACT_PLANET_1); 297 | await feedSilverToCap(world, world.user1Core, LVL1_ASTEROID_2, LVL1_QUASAR); 298 | await increaseBlockchainTime(); // fills up LVL1_ASTEROID_2 299 | 300 | const UPGRADEABLE_PLANETS = (await world.contracts.core.gameConstants()).UPGRADEABLE_PLANETS; 301 | 302 | enum PlanetType {PLANET, ASTEROID, FOUNDRY, RIP, QUASAR}; 303 | 304 | // Only expect revert on planets that are NOT upgradeable 305 | 306 | if(!UPGRADEABLE_PLANETS[PlanetType.PLANET]) { 307 | console.log("ERROR: PLANET type must be upgradeable"); 308 | } 309 | 310 | if(!UPGRADEABLE_PLANETS[PlanetType.ASTEROID]) { 311 | await expect(world.user1Core.upgradePlanet(LVL1_ASTEROID_2.id, 0)).to.be.revertedWith( 312 | 'Can only upgrade allowed planet types' 313 | ); 314 | } 315 | 316 | if(!UPGRADEABLE_PLANETS[PlanetType.FOUNDRY]) { 317 | await expect(world.user1Core.upgradePlanet(ARTIFACT_PLANET_1.id, 0)).to.be.revertedWith( 318 | 'Can only upgrade allowed planet types' 319 | ); 320 | } 321 | 322 | if(!UPGRADEABLE_PLANETS[PlanetType.RIP]) { 323 | await expect(world.user1Core.upgradePlanet(LVL3_SPACETIME_1.id, 0)).to.be.revertedWith( 324 | 'Can only upgrade allowed planet types' 325 | ); 326 | } 327 | 328 | if(!UPGRADEABLE_PLANETS[PlanetType.QUASAR]) { 329 | await expect(world.user1Core.upgradePlanet(LVL1_QUASAR.id, 0)).to.be.revertedWith( 330 | 'Can only upgrade allowed planet types' 331 | ); 332 | } 333 | }); 334 | ``` 335 |
336 | 337 | 338 | ### 3.3.2 Create a new test to confirm asteroid upgrades are successful 339 | 340 | - Name this test: `should upgrade asteroid if asteroid is upgradeable` 341 | 342 | - If you carefully examine the `should upgrade planet stats and emit event` test in `DFCustomUpgrade.test.ts`, you should be able to infer how to test the upgrade for the `LVL1_ASTEROID_2`. 343 | - Try this on your own before peeking! 344 |
345 | Solution 346 | 347 | ```typescript 348 | it('should upgrade asteroid if asteroid is upgradeable', async function (){ 349 | const ASTEROID = 1; 350 | const gameConstants = await world.contracts.core.gameConstants() 351 | expect(gameConstants.UPGRADEABLE_PLANETS[ASTEROID]).to.be.true; 352 | 353 | const upgradeablePlanetId = LVL1_PLANET_NEBULA.id; 354 | const silverMinePlanetId = LVL1_ASTEROID_2.id; 355 | 356 | await world.user1Core.initializePlayer(...makeInitArgs(SPAWN_PLANET_1)); 357 | 358 | // conquer silver mine and upgradeable planet 359 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, LVL1_PLANET_NEBULA); 360 | await conquerUnownedPlanet(world, world.user1Core, SPAWN_PLANET_1, LVL1_ASTEROID_2); 361 | 362 | await increaseBlockchainTime(); 363 | 364 | await world.user1Core.refreshPlanet(silverMinePlanetId); 365 | 366 | const planetBeforeUpgrade = await world.contracts.core.planets(silverMinePlanetId); 367 | 368 | const silverCap = planetBeforeUpgrade.silverCap.toNumber(); 369 | const initialSilver = planetBeforeUpgrade.silver.toNumber(); 370 | const initialPopulationCap = planetBeforeUpgrade.populationCap; 371 | const initialPopulationGrowth = planetBeforeUpgrade.populationGrowth; 372 | 373 | await expect(world.user1Core.upgradePlanet(silverMinePlanetId, 0)) 374 | .to.emit(world.contracts.core, 'PlanetUpgraded') 375 | .withArgs(world.user1.address, silverMinePlanetId, BN.from(0), BN.from(1)); 376 | 377 | const planetAfterUpgrade = await world.contracts.core.planets(silverMinePlanetId); 378 | const newPopulationCap = planetAfterUpgrade.populationCap; 379 | const newPopulationGrowth = planetAfterUpgrade.populationGrowth; 380 | const newSilver = planetAfterUpgrade.silver.toNumber(); 381 | 382 | expect(newSilver).to.equal(initialSilver - 0.2 * silverCap); 383 | expect(initialPopulationCap).to.be.below(newPopulationCap); 384 | expect(initialPopulationGrowth).to.be.below(newPopulationGrowth); 385 | }); 386 | ``` 387 |
388 | 389 | ## 3.4 Run the new Hardhat test 390 | 391 | - `yarn workspace eth hardhat test test/DFCustomUpgrade.test.ts` 392 | - You should see this output 393 | 394 | ![Custom upgrade test output](img/custom_upgrade_final.png) 395 | 396 | 397 | ## 3.5 Run all Hardhat tests 398 | 399 | - `yarn workspace eth test` 400 | - You will see an error in `DFUpgrade.test.ts`: 401 | 402 | > AssertionError: Expected transaction to be reverted with Can only upgrade regular planets, but other exception was thrown: Error: VM Exception while processing transaction: reverted with reason string 'Can only upgrade allowed planet types’ 403 | > 404 | - How would you fix this? 405 | - (Hint: see the Solution to 3.3.1) 406 | - Fix this error and run `yarn workspace eth test` again and all test should pass. 407 | 408 | The upgrade logic is now working in the tests, but we need to make these changes accessible to players using the game client as well. 409 | 410 | ## 3.6 Add `UPGRADEABLE_PLANETS` config to the client 411 | 412 | - This allows us to reference the value of `UPGRADEABLE_PLANETS` anywhere in the client. 413 | 414 | ### 3.6.1 Add `UPGRADEABLE_PLANETS` type 415 | 416 | - The Dark Forest client is a TypeScript project. That means that for every new variable we add, (like `UPGRADEABLE_PLANETS`), we need to add its type as well. 417 | - In `client/src/_types/darkforest/api/ContractsAPITypes.ts:` 418 | 419 | ```diff 420 | export interface ContractConstants { 421 | ... 422 | + UPGRADEABLE_PLANETS: boolean[]; 423 | } 424 | ``` 425 | 426 | 427 | ### 3.6.2 Add `UPGRADEABLE_PLANETS` value 428 | 429 | - In `client/src/Backend/GameLogic/ContractsAPI.ts` 430 | 431 | ```typescript 432 | // ~ Line 800 433 | { 434 | ... 435 | UPGRADEABLE_PLANETS 436 | } = await this.makeCall(this.coreContract.gameConstants); 437 | 438 | // ~ Line 880 439 | 440 | const constants: ContractConstants = { 441 | ... 442 | UPGRADEABLE_PLANETS 443 | } 444 | ``` 445 | 446 | 447 | ## 3.7 Show user which planets are upgradeable 448 | 449 | - In `client/src/Frontend/Panes/UpgradeDetailsPane.ts`: 450 | - Try to change this logic yourself to allow only the planets marked as true in `UPGRADEABLE_PLANETS` variable to be upgraded in the client. 451 | - Hint: This is very similar to the change we made to `DarkForestPlanet.sol` but in TypeScript. 452 | - Also, to get access to the `UPGRADEABLE_PLANETS` value, we need to access the GameManager object. The React [Frontend](https://github.com/darkforest-eth/client/tree/master/src/Frontend) doesn’t have direct access to the GameManager, but it does have access to the GameUiManager, which can fetch the GameManager. 453 | - So, getting the `UPGRADEABLE_PLANETS` value looks like this: 454 | 455 | ```jsx 456 | const UPGRADEABLE_PLANETS = uiManager.getGameManager().getContractConstants().UPGRADEABLE_PLANETS; 457 | ``` 458 | 459 |
460 | Solution 461 | 462 | ```typescript 463 | // ~ Line 88 464 | const UPGRADEABLE_PLANETS = uiManager.getGameManager().getContractConstants().UPGRADEABLE_PLANETS; 465 | 466 | if (planet && account) { 467 | if (planet.owner !== account) { 468 | } else if (!UPGRADEABLE_PLANETS[planet.planetType] || planet.silverCap === 0) { 469 | content = ( 470 | 471 | This Planet
is not Upgradeable 472 |
473 | ); 474 | } 475 | ``` 476 |
477 | 478 | 479 | ## 3.8 Run a local game 480 | 481 | - `yarn` to compile the client side changes 482 | - `yarn start` to launch the game. (Same as step 1.1) 483 | - *Note: For test purposes, you can set `DISABLE_ZK_CHECKS` to true for the local game.* 484 | - See if you can upgrade an asteroid field! 485 | - [It should look something like this](https://twitter.com/cha0sg0d_/status/1484620387866447873) 486 | 487 | A complete solution to Quest 3 can be found on the tutorial branch of [cha0sg0d/eth](https://github.com/cha0sg0d/eth/tree/tutorial) and [cha0sg0d/client](https://github.com/cha0sg0d/client/tree/tutorial). 488 | 489 | # Quest 4: Deploy contracts and client to production 490 | 491 | Follow the steps in the [README](README.md#static-deployment-of-dark-forest-no-webserver) to deploy the contracts and client. 492 | 493 | If your round includes custom planets, run the following command after deploying DarkForestCore: 494 | 495 | ### 4.1 Add custom planet (optional). 496 | 497 | - `yarn workspace eth hardhat:prod game:createPlanets` 498 | - This functionality is built-in to the local deploy process, but needs to happen manually for deploying to production. 499 | 500 | 501 | ## Side Quest 1: Deploying the subgraph 502 | 503 | - What is [The Graph](https://thegraph.com/) and why is it useful? 504 | - [The Graph](https://thegraph.com/) is an indexing protocol for querying networks like Ethereum and IPFS. Anyone can build and publish open APIs, called subgraphs, making data easily accessible. 505 | - The graph enables more complex queries like who owned this planet at time *t* and has a much faster performance than a vanilla JSON-RPC request. 506 | - [Jacob Rosenthal](https://twitter.com/jacobrosenthal) has created a DF subgraph that can be deployed via just a few commands. Follow the instructions [here](https://github.com/darkforest-eth/eth/tree/master/subgraph) to deploy the subgraph. 507 | - *Note: If you’re on a Mac M1, make sure you have the [correct Docker version](https://docs.docker.com/desktop/mac/apple-silicon/).* 508 | - If you are changing a fundamental data structure of the subgraph, you will need to update the schema and mappings. See [here](https://github.com/cha0sg0d/eth/commit/1b382624c986ed2b045b8465bde2df543dc5d3f0) for an example edit. 509 | 510 | ## Side Quest 2: Auto Whitelisting with no server 511 | 512 | - Get a csv file of all the addresses you want to add to the game. 513 | - Run the following command (`hardhat:prod` for production or `hardhat:dev` for local network) 514 | - Run: `yarn workspace eth hardhat:prod whitelist:registerAddress --address < cat address.csv` 515 | 516 | - This will generate a whitelist key for each address and add them to the game. 517 | 518 | - You can automate this process (if more people are joining your game) with a simple Hardhat script 519 | 520 | - Here is a sample [auto whitelist script](https://github.com/cha0sg0d/eth/blob/community/scripts/whitelist.js) that will give you a place to start. 521 | 522 | 523 | ## Side Quest 3: Publishing your new contracts as an npm package 524 | 525 | - Work in progress... 526 | 527 | 528 | ## Troubleshooting 529 | 530 | - If you see any ZK checks fail, like `failed init proof check`, you need to recompile the circuits with the following command: 531 | - `yarn workspace eth circom:dev` 532 | - Wait ~5 minutes for the circuits to recompile. This will result in a new `Verifier.sol` file. 533 | - If you see the following warning 534 | 535 | > Warning: Contract code size is 24622 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. 536 | > 537 | - You need to reduce the contract size. Try commenting out changeAdmin or buyHat functions in DarkForestCore. Note that if you do, you will also have to comment out or delete the tests that use these functions. (`eth/test/DFHat.test`) 538 | 539 | ## Appendix 1: Using Git effectively 540 | 541 | - For setting up your environment initially, see the [If you plan to make changes to darkforest-local](README.md#if-you-plan-to-make-changes-to-darkforest-local) steps. 542 | - Each time you want to add a new feature, make a new branch on that specific repository. 543 | - `git checkout -b ` 544 | - When the feature is ready to go, merge it with your main branch and delete the feature branch. 545 | - `git checkout main` 546 | - `git merge new-feature` 547 | - `git branch -d new-feature` 548 | - When making changes to the codebase, describe each change you make via `git add` and `git commit -m ` 549 | - It is crucial that each edit is understandable to someone who did not write this code. 550 | - See [cha0sg0d's](https://github.com/cha0sg0d) `darkforest/eth` [repo](https://github.com/cha0sg0d/eth/commits/community) for an example of documenting each commit. 551 | 552 | -------------------------------------------------------------------------------- /dapp.md: -------------------------------------------------------------------------------- 1 | # Dark Forest Dapp Overview 2 | 3 | This page will provide a high level overview of the components and structure of the Dark Forest codebase. 4 | 5 | *This page is not official in any way and is subject to change as the game evolves.* 6 | 7 | # Intro 8 | - Dark Forest has a lot of moving parts, but at its core, it is a blockchain-based application, commonly referred as a [dapp](https://ethereum.org/en/developers/docs/dapps/). 9 | 10 | > A dapp has its backend code running on a decentralized peer-to-peer network (blockchain). Contrast this with an app where the backend code is running on centralized servers. A dapp can have frontend code and user interfaces written in any language (just like an app) to make calls to its backend 11 | > 12 | - Dark Forest has a backend that contains the **[code](https://github.com/darkforest-eth/eth/)** that enforces the game constraints and rules. This code is divided into files called **[smart contracts](https://github.com/darkforest-eth/eth/tree/master/contracts),** and it runs on a blockchain. 13 | - Players interact with these contracts via a frontend called the **[client](https://github.com/darkforest-eth/client/)**. This **client** shows a visual representation of the state of the Dark Forest universe and serves as the connection between players and the smart contracts on the blockchain. 14 | - To create a round of Dark Forest, you need to do 3 things: 15 | 1. (optional) Customize the **smart contracts** and **client** to your liking. 16 | 2. Deploy the **smart contracts** to a blockchain. 17 | 3. Deploy the **client** to a frontend hosting service like Netlify. 18 | 19 | - Let's dive deeper into each component of the code base. 20 | 21 | # Client overview 22 | 23 | ## Backend 24 | 25 | ### GameManager 26 | 27 | ### GameUIManager 28 | 29 | ## Frontend 30 | 31 | # Eth overview 32 | 33 | ## What is Hardhat? 34 | - [https://hardhat.org/getting-started/](https://hardhat.org/getting-started/) 35 | - Hardhat is a development environment to compile, deploy, test, and debug your smart contracts. 36 | - Hardhat simulates a local blockchain and deploys your contracts to this chain for development purposes. 37 | - There are many cool features in Hardhat, including the ability to manually mine blocks, speed up blockchain time, and easily deploy contracts to public chains. 38 | - Hardhat also lets you define custom tasks that relate to deploying and interacting with smart contracts. 39 | > For example, the `deploy:contracts` task deploys the Dark Forest contracts and the `game:createPlanets` task adds any custom planets to the universe. 40 | - Additionally, Hardhat has a variety of additional plugins that add features such as [unit tests](https://hardhat.org/tutorial/testing-contracts.html#_5-testing-contracts) and [types](https://www.npmjs.com/package/@typechain/hardhat) for smart contracts. 41 | - When running tasks for the local network, use `hardhat:dev`. 42 | - When running tasks for xDai, use `hardhat:prod`. 43 | 44 | ## contracts 45 | 46 | ## test 47 | 48 | ## tasks 49 | 50 | # Circuits overview 51 | 52 | -------------------------------------------------------------------------------- /img/custom_upgrade_final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/custom_upgrade_final.png -------------------------------------------------------------------------------- /img/custom_upgrade_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/custom_upgrade_init.png -------------------------------------------------------------------------------- /img/enter_game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/enter_game.png -------------------------------------------------------------------------------- /img/hardhat_prod_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/hardhat_prod_deploy.png -------------------------------------------------------------------------------- /img/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/link.png -------------------------------------------------------------------------------- /img/log_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/log_in.png -------------------------------------------------------------------------------- /img/netlify_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/netlify_deploy.png -------------------------------------------------------------------------------- /img/netlify_import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/netlify_import.png -------------------------------------------------------------------------------- /img/netlify_prod_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/netlify_prod_deploy.png -------------------------------------------------------------------------------- /img/new_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/new_key.png -------------------------------------------------------------------------------- /img/pass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/pass.png -------------------------------------------------------------------------------- /img/site_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/site_id.png -------------------------------------------------------------------------------- /img/stop_auto_publishing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/stop_auto_publishing.png -------------------------------------------------------------------------------- /img/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectsophon/darkforest-local/325736de0a95da5113ed6db7fbac5903002e3653/img/test.png -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "yarn workspace client build" 3 | functions = "functions" 4 | publish = "client/dist" 5 | 6 | [build.environment] 7 | CONVERSATION_API_HOST = "" 8 | LEADERBOARD_API = "" 9 | WEBSERVER_URL = "" 10 | 11 | [[redirects]] 12 | from = "/*" 13 | to = "/index.html" 14 | status = 200 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "darkforest-local", 3 | "private": true, 4 | "engines": { 5 | "node": ">=14" 6 | }, 7 | "workspaces": [ 8 | "packages/types", 9 | "packages/events", 10 | "packages/hashing", 11 | "packages/snarks", 12 | "packages/settings", 13 | "eth", 14 | "packages/contracts", 15 | "packages/constants", 16 | "packages/hexgen", 17 | "packages/serde", 18 | "packages/network", 19 | "packages/ui", 20 | "packages/gamelogic", 21 | "packages/procedural", 22 | "packages/renderer", 23 | "circuits", 24 | "client" 25 | ], 26 | "scripts": { 27 | "prepare": "yarn workspaces run prepare", 28 | "test": "yarn workspaces run test", 29 | "lint": "yarn workspaces run lint", 30 | "format": "yarn workspaces run format", 31 | "deploy:contracts": "yarn workspace eth hardhat:dev deploy --whitelist false", 32 | "deploy:client": "netlify build && netlify deploy", 33 | "deploy:client:prod": "netlify build && netlify deploy --prod", 34 | "wait:node": "wait-on tcp:8545", 35 | "start:client": "yarn workspace client start", 36 | "start:node": "yarn workspace eth hardhat:node", 37 | "start:game": "run-s wait:node deploy:contracts add:planets start:client", 38 | "add:planets": "yarn workspace eth hardhat:dev game:createPlanets", 39 | "start": "run-p start:node start:game", 40 | "clean:workspaces": "yarn workspaces run clean", 41 | "clean:self": "del-cli node_modules/", 42 | "clean": "run-s clean:workspaces clean:self" 43 | }, 44 | "devDependencies": { 45 | "del-cli": "^4.0.1", 46 | "npm-run-all": "^4.1.5", 47 | "wait-on": "^6.0.0" 48 | }, 49 | "resolutions": { 50 | "ts-node": "9.1.1" 51 | } 52 | } 53 | --------------------------------------------------------------------------------