Git Product home page Git Product logo

ltfschoen / flappytips Goto Github PK

View Code? Open in Web Editor NEW
11.0 2.0 2.0 3.56 MB

FlappyTips 2. Polkadot ecosystem game

License: GNU General Public License v3.0

HTML 1.86% CSS 1.67% JavaScript 73.63% Rust 16.51% Procfile 0.01% DIGITAL Command Language 3.64% Shell 2.67%
p5js-game web3 polkadot-js react heroku kusama-network polkadot-network treasury ink-language smart-contracts websockets substrate zeitgeist linode nginx websocket

flappytips's Introduction

FlappyTips 2

Objective: Fly the DOT character between more gaps (of obstacles blocks) than all other opponents to win the game of that starting block on the Zeitgeist chain.

Economics Players may choose to play without using any funds. However, incentivises exist that require tokens, where users may create a Substrate-based account and deposit sufficient tokens (DOT tokens) to cover the transaction costs required to share their game results. If the user plays the game and shares their results, they may be eligible for a tip from the treasury.

Build Log

  • Add restricted gameplay endpoint of only Zeitgeist to use their block time for ease of integration with the Zeitgeist prediction markets. Future releases may restore choice of chain
  • Adds support for multiplayer instead of just single player games. If no other opponents connect in time. Once the user selects a Polkadot.js Extension account to play with and clicks "Play" it schedules a game at a future block. Other players may join if they also click "Play" a sufficient amount of blocks before the game starts, otherwise they are scheduled to play at a future block, where other players can join too.
  • Added support to show ghost icon of other players during gameplay
  • Adds support to tracks the start block and end block of the game after all players have hit an obstacle
  • Adds gameplay success factor where players may only Win in multiplayer. Single player games or draws are shown as losing
  • Updated all dependencies including Polkadot.js API, Polkadot Extension, Express API, P5 Gaming API, React
  • Updated from Node.js 10 to latest Node.js 19 and updated Yarn 1 to Yarn 3
  • Added Websockets support for multiplayer
  • Add IP address recording only incase necessary to block malicious users in production.
  • Retains FlappyTips 1 UI where player character is a dot icon
  • Retains FlappyTips 1 gameplay movement via pressing space bar (Desktop) or tapping screen (Mobile) to fly character between gaps of approaching obstacles. Each obstacle is labelled to represent a block of the connected blockchain (Zeitgeist)
  • Retains FlappyTips 1 obstacle speed increase after each obstacle bypassed
  • Retains FlappyTips 1 responsive support for mobile devices or desktop
  • Retains FlappyTips 1 legacy code where users may share their results on Twitter
  • Retains FlappyTips 1 legacy code (only Desktop support) to request a tip from the Polkadot treasury
  • Retains FlappyTips 1 support for deploying to Heroku for production
  • Temporarily removes FlappTips 1 support for requesting a tip on Mobile devices until QR code scanning is supported to avoid having tn enter private key
  • Removes FlappyTips 1 Namebase API and Handshake API domain deployment since Sia Skynet Skylink deprecated.

Play

Setup

Start Game

  • Click the "Play" button after the "Loading..." screen disappears to automatically schedule to play a game at an upcoming block
  • Watch the countdown to the scheduled starting block when gameplay starts
  • Press space bar multiple times (Desktop) to make your dot character fly and try to navigate and clear your way through gaps in the obstacles to score points.
  • Touch the screen multiple times to fly the DOT (Mobile devices)
  • Obstacles (blocks) appear each time a new block is authored on the connected chain (Zeitgeist).
  • After each block appears, the speed that it moves increases each time.
  • After about 10 blocks the gap may becomes larger but it still becomes more difficult as the blocks move faster

Share Results

  • After game ends optionally click the "Share" button to share your result or request a tip (Desktop only)
  • After winning a game you may wish to click the "Share & Request Tip?" button, along with an optional identifer (i.e. your Twitter handle). Share your result on Twitter for free. Alternatively deposit submit sufficient funds into the wallet to create an extrinsic to Polkadot chain (DOT tokens) that will report your awesomeness for clearing some blocks requesting a Tip, and should appear in the "Tip" section here https://polkadot.js.org/apps/#/treasury on a chain that supports treasury.reportAwesome (Polkadot).

Develop Environment

Clone the repository. Checkout the PR with FlappyTips 2 features.

git clone https://github.com/ltfschoen/flappytips
cd flappytips
git checkout master

If using Nginx, update /etc/nginx/sites-available/flappytips and ./server.js to use self-signed certificates instead of Let's Encrypt or PositiveSSL

  • Note: See example in ./production/nginx/clawbird.com
  • Note: Inject environment variables from .env.development using . ./scripts/env-dev.sh

Install Yarn 3.x and Node.js, and then run the following in terminal:

printf '\e[?2004l' &&
nvm use 19.6.0 &&
npm i -g yarn &&
corepack enable && corepack prepare yarn@stable --activate &&
yarn set version 3.4.1 &&
yarn &&
npm install -g nodemon &&
npm install -g concurrently &&
yarn add node-gyp &&
yarn add fs &&
. ./scripts/env_dev.sh &&
DEBUG=* yarn run dev

Note: yarn dev does not work in production, use yarn start or yarn prod

  • Follow the "Setup" in the "Play" section of this README file, but instead go to http://localhost:4000
  • Click the polkadot-js/extension browser icon and allow it to interact with FlappyTips 2
  • Press space bar to make your dot character fly and try to navigate through the obstacles.
  • Open other browser windows at http://localhost:4000 for other players to join
  • Access the API endpoints at http://localhost:5000/api

Debugging on Mobile

https://www.addictivetips.com/android/get-web-console-log-chrome-for-android/

Debugging Websockets

To enable websockets debugging in server logs:

DEBUG=* node yourfile.js // server

localStorage.debug = '*'; // browser

https://socket.io/docs/v4/logging-and-debugging/

https://socket.io/docs/v4/client-installation/

To debug Websockets frames in browser: https://stackoverflow.com/a/30770934/3208553 It may be necessary to drag down an expand the window to view each frame.

Zeitgeist

Testnet (Battery Station)

Maintenance

npm outdated
npm update --save
rm -rf node_modules
npm install

Websockets Socket.IO

Troubleshooting

  • Client & Server paths must match
    • Client

      const socketEndpoint = <ENDPOINT>;
      const socket = io(socketEndpoint, {
        transports: ["websocket"],
        addTrailingSlash: true, // trailing slash of path
        path: "/socket.io/", // explicit custom path (default)
      });
      
    • Server

      const io = require("socket.io")(httpServer, {
        transports: ["websocket"], // set to use websocket only
        path: "/socket.io/", // explicitely set custom path (default)
        ...
      

References

Nginx

Config

HTTPS / SSL

Option 1: Let's Encrypt / CertBot

Community

https://community.letsencrypt.org/

Setup
sudo apt update
sudo apt install snapd
sudo snap install core
sudo snap refresh core
sudo apt remove certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx \
  -d clawbird.com \
  -d www.clawbird.com
  • Specify email address and other details

  • Output

$ cat /etc/letsencrypt/live/README

`[cert name]/privkey.pem`  : the private key for your certificate.
`[cert name]/fullchain.pem`: the certificate file used in most server software.
`[cert name]/chain.pem`    : used for OCSP stapling in Nginx >=1.3.7.
`[cert name]/cert.pem`     : will break many server configurations, and should not be used without reading further documentation (see link below).

Note: None of the certificates or keys should be pushed to Git but I have included only self-signing ones for learning purposes.

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/www.clawbird.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/www.clawbird.com/privkey.pem
This certificate expires on 2023-05-20.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for www.clawbird.com to /etc/nginx/sites-enabled/flappytips
Successfully deployed certificate for clawbird.com to /etc/nginx/sites-enabled/flappytips
Congratulations! You have successfully enabled HTTPS on https://www.clawbird.com and https://clawbird.com

Option 2: PositiveSSL

PositiveSSL activation and add to server

Backup Certificates

  • Backup SSL files
     mkdir -p /root/certs/backup
     cp /etc/nginx/nginx.conf /root/certs/backup/nginx.conf.backup-pt1
     cp -r /etc/nginx/conf.d/ /root/certs/backup/conf.d-backup-pt1
     cp /etc/nginx/sites-available/flappytips /root/certs/backup/flappytips-backup-pt1
    

Content Security Policy (CSP)

https://content-security-policy.com/

  • Add to /etc/nginx/nginx.conf
add_header          Content-Security-Policy "default-src 'self' 'unsafe-eval'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' platform.twitter.com syndication.twitter.com; script-src-elem 'self' https://platform.twitter.com/widgets.js https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js https://clawbird.com:4000/static/js/main.1d765367.js https://clawbird.com/static/js/main.1d765367.js https://platform.twitter.com/js/button.e7f9415a2e000feaab02c86dd5802747.js; connect-src 'self' platform.twitter.com syndication.twitter.com wss://rpc.polkadot.io/ https://ipapi.co/json/ wss://clawbird.com:443/socket.io/ wss://clawbird.com:5000/socket.io/ wss://zeitgeist-rpc.dwellir.com/ https://clawbird.com:4000/assets/LemonMilkMedium.otf https://clawbird.com/assets/LemonMilkMedium.otf; img-src 'self' data: https://clawbird.com:4000/favicon.ico https://clawbird.com/favicon.ico https://clawbird.com:4000/logo192.png https://clawbird.com/logo192.png platform.twitter.com syndication.twitter.com; frame-src 'self' https://platform.twitter.com/";

Deploy to Linode (Production Environment)

Deploy React App with Linode https://www.youtube.com/watch?v=FTyby51m0hQ

ssh [email protected]
git clone [email protected]:ltfschoen/flappytips.git
git fetch origin master
git checkout master
  • Check that HTTPS and WWS example credentials that were setup using this guide are still valid guidehttps://medium.com/developer-rants/implementing-https-and-wss-support-in-express-with-typescript-of-course-f36006c77bab
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
openssl rsa -in key.pem -out key-rsa.pem
nvm use v19.6.0
apt-get update && apt-get upgrade
sudo apt install nginx

mv ./flappytips /var/www/flappytips
sudo vim /etc/nginx/nginx.conf
sudo vim /etc/nginx/sites-available/flappytips
sudo ln -s /etc/nginx/sites-available/flappytips /etc/nginx/sites-enabled
sudo nginx -t
nginx -s reload && sudo systemctl restart nginx
journalctl -xeu nginx.service
  • Show Nginx version installed

    apt-get update && apt-get upgrade
    apt install nginx
    
    nginx is already the newest version (1.18.0-6ubuntu14.3)
    
  • Update from Nginx 1.18 to Nginx 1.23.3. Note that at https://nginx.org/en/download.html it shows 1.23 (mainline version) and 1.18 (legacy), but installing using apt only installs 1.18.

    • Important: Backup /etc/nginx/nginx.conf first since it will be overwritten

    • Temporarily disable Nginx

      ps -aux | grep nginx
      sudo systemctl status nginx
      sudo systemctl stop nginx
      sudo systemctl disable nginx
      
    • Run the commands here https://nginx.org/en/linux_packages.html#Ubuntu

    • Restore functionality of /etc/nginx/nginx.conf by adding lines like the following that were removed linking to the site in /etc/nginx/sites-available/flappytips

      include /etc/nginx/modules-enabled/*.conf;
      ...
      add_header          Content-Security-Policy "default-src 'self'; upgrade-insecure-requests;";
      ...
      include /etc/nginx/sites-enabled/*;
      
    • Verify version

      nginx -V
      
      nginx version: nginx/1.23.3
      
    • Restart Nginx and reload config files

      sudo systemctl stop nginx
      sudo systemctl enable nginx
      sudo systemctl start nginx
      sudo systemctl reload nginx
      nginx -s reload
      sudo systemctl status nginx
      
      • Alternatives
        sudo systemctl restart nginx
        sudo service nginx reload
        sudo service nginx status
        
  • https://www.linode.com/docs/guides/getting-started-with-nginx-part-3-enable-tls-for-https/

/etc/nginx/sites-available/flappytips

server {
  # Self-signed certificate
  # ssl_certificate     /var/www/flappytips/cert.pem;
  # ssl_certificate_key /var/www/flappytips/key-rsa.pem;

  listen          443 ssl default_server;
  listen          [::]:443 ssl default_server;
  server_name     139.144.96.196;
  root            /var/www/flappytips/build;
  index           index.html;
  location / {
                  try_files $uri /index.html =404;
  }
}
nginx -s reload && sudo systemctl restart nginx
  • Enable Firewall
ufw enable
ufw status verbose
ufw allow 'Nginx Full'
ufw reload

Linode Troubleshooting

If error address in use EADDRINUSE when try to restart server

lsof -i tcp:5000

kill -9 <PID>

If the error says ENOSPC: System limit for number of file watchers reached:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Deploy to Heroku

Note: It is necessary to use either Eco or Basic plan on Heroku. Eco plan dyno that receives no web traffic in a 30-minute period sleeps and becomes active again upon receiving traffic. See https://www.heroku.com/pricing

Note: I tried to deploy and run the game on Heroku after removing console.log to reduce the required production memory to ~1Gb memory. I provided credit card info and chose 1 dyno resized to performance-m which has 2.5Gb memory instead of 0.5Gb memory of the lower plans that were giving R14 out of memory errors, however Heroku would not let me scale to performance-m and an gave an error Access to performance-m and performance-l dynos is limited to customers with an established payment history. But the only way to quickly get payment history is to buy their lame $5 Eco plan that only provides 0.5Gb of memory, which is not sufficient to run my game anyway, but they do not give the option of paying $25 upfront for the performance-m plan.

[heroku-exec] Starting
2023-02-17T09:08:44.912127+00:00 app[web.1]: Creating an optimized production build...
2023-02-17T09:09:00.375813+00:00 heroku[web.1]: Process running mem=789M(154.3%)
2023-02-17T09:09:00.377211+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2023-02-17T09:09:22.552936+00:00 heroku[web.1]: Process running mem=1194M(233.2%)
2023-02-17T09:09:22.555218+00:00 heroku[web.1]: Error R15 (Memory quota vastly exceeded)
2023-02-17T09:09:22.557134+00:00 heroku[web.1]: Stopping process with SIGKILL

So despite having used Heroku for many years with Ruby on Rails, based on this experience I think I will switch to deploying on Linode where I already have a cheap server with plenty of memory.

  • Install Heroku CLI for macOS
brew tap heroku/brew && brew install heroku
  • Start
heroku login
heroku apps:create flappytips
heroku git:remote -a flappytips
heroku config:set XYZ=abc --app flappytips
git push -f heroku yourbranch:master
git push -f heroku master
heroku local web
heroku ps:scale web=0:Eco
heroku ps
heroku open
heroku logs --tail
heroku restart
  • SSH
heroku ps:exec
  • Stop
heroku ps:stop web
  • Scale up dynos. If you get an error like code=H14 desc="No web processes running" in Heroku logs then scale your dynos
heroku ps:scale web=1:Basic
heroku ps:scale web=2:standard-2x
heroku ps:scale web=1:performance-m
  • Scale down dynos
heroku ps:scale web=0:Eco

Troubleshooting

  • If you get an unknown type error, then it may be necessary to update polkadot-js/api dependency in package.json, since it is constantly evolving.

  • To kill a frozen process

ps -ef | grep node
kill -9 <PROCESS_ID>
  • If you get 00~ in terminal commands then reset terminal to turn paste bracketing off again
    • printf '\e[?2004l'

Additional Notes

This project was bootstrapped with Create React App. Credit to this repo that was used to replicate a Flappy Bird like game https://codepen.io/renzo/pen/GXWbEq

Zeitgeist Prediction Markets Integration

Create Market

Note: Relevant Zeitgeist frontend UI code to create a market https://github.com/zeitgeistpm/ui/blob/staging/pages/create.tsx

  • User enters market name
  • User enters question
  • User adds logo for market
  • User choose category (i.e. e-sports)
  • User chooses market end date or block (when game ends)
    • Calculate time lapsed between current zeitgeist block and proposed market end block
    • Ensure it ends after get result from all competitors, than allow time for oracle result too
    • Zeitgeist app provides 4 days for oracle to submit final outcome (otherwise oracle forfeits oracle bond of 200 ZTG) (i.e. >= 4 days between market ending and oracle submitting result)
  • User creates crypto assets for each outcome, i.e. Y/N, Options, Range i.e. outcomes + ticker Player1 PLY1, Player2 PLY2 (ticker must be unique)
    • The more outcome tokens minted (i.e. amt column below... the better for the market, and the more efficient the market will be
  • User specifies Oracle wallet address i.e. use your own i.e. d_______ (Zeitgeist)
    • Must report the correct result before resolution time
  • User specifies market description
    • The end date
    • Location of source of finality
    • i.e. Prediction market to give insights into who will win the ___ race, the market will end right before the semi-final stage so to give more opportunity for predictions, the Oracle shall source the result from url www.___.com after the final completes, in the highlight unlikely even that it is a draw, then the outcome "OTHER" shall be the winning token, winners holding a winning asset get 1 ZTG per winning asset
  • User gets liquidity from zeitgeist or provides it themselves

balance weights % amt price (1 ZTG / 3) total value PLY1 200 33 33 100 0.33 33 PLY2 200 33 33 100 0.33 33 PLY3 200 33 33 100 0.33 33 ZTG 200 100 100 100 1 100

Note: markets denominated in ZTG tokens (to buy/sell outcome assets)

Prize pool = 100 ZTG Liquidity = 200 ZTG

  • User chooses Pool Fees (i.e. 0.1%, 1%, 10%, 3%) allowing liquidity providers to collect more from a given trade, but may reduce market participants

Cost breakdown Network fee - 0.053 ZTG Permissionless Bond - 1000 ZTG (if believe it is a fair market, returned if market not deleted by committee) Oracle Bond - 200 ZTG Liquidity - 200 ZTG (100 for counterpair, 10*10=100 to mint other outcome tokens)

  • Ends in x days

Oracle Smart Contract in ink!

Setup & Deploy

  • Install Rust
  • Check version
rustup update
rustup show
  • Install Cargo Contract to allow you to compile and interact with contracts (published on crates.io)
cargo install cargo-contract --version 2.0.0-rc.1
  • Error (see Github Issues)

  • Run Cargo Contract Node

substrate-contracts-node --dev
  • Install Contracts Node
cargo install contracts-node --git https://github.com/paritytech/substrate-contracts-node/ --version 0.24.0
  • Create rust project with template inside (i.e. Cargo.toml and lib.rs, which uses ink crate 4.0.0-rc)

    cargo contract new flappytips
    
  • Generate .contract, .wasm, metadata.json code. Note: Use ---release to deploy

    cargo contract build
     cargo contract build --release
    
  • Upload Contract (note: prefer to use contracts-ui to avoid exposing private key)

cargo contract upload --suri //Alice
  • Outputs the:
CodeStored event
code_hash: 0x......
  • Note: only one copy of the code is stored, but there can be many instance of one code blob, differs from other EVM chains where each node has a copy

Interact with ink! Contracts using Contracts Node

Cargo Contracts
  • Instantiate Contract
contract instantiate \
	--suri //Bob \
	--contructor new \
	--args 10
  • Wait for response
...
Event System => NewAccount
	account: 5G...
...
Event Contracts + Instantiated
	deployer: 5F...
	contract: 5G.... (new contract account address to interact with contract)
...
  • Check value was assigned correctly
  • Use dry-run because if execute as a transaction then we won't see the return value
cargo contract call \
	--suri //Charlie \
	--contract 5G... \
	--message get \
	--dry-run
  • Interact to increment by 5, not a dry run so no response but we get a gas limit response
cargo contract call \
	--suri //Charlie \
	--contract 5G... \
	--message inc \
	--args 5
  • Check it incremented
cargo contract call \
	--suri //Charlie \
	--contract 5G... \
	--message get \
	--dry-run
  • Note: only works in debug mode cargo build (not release)
ink::env::debug_println("inc by {}, new value {}", by, self.value);
  • Note: it should output on substrate-contracts-node too as tokio-runtime-worker runtime::contracts Exection finished with debug buffer...
  • Note: it should show in contracts-ui website too
  • Note: events are not emitted in a dry-run (why wouldn't we want this in debugging mode?)

Interact with Contract using Polkadot.js API

Documentation for ink!

https://paritytech.github.io/ink/

References (Blockchain)

Events

flappytips's People

Contributors

ltfschoen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

todokku

flappytips's Issues

Refresh multiple times if does not immediate detect extension sometimes

This may be associated with browser cache.

After playing a game if you choose to "Play Again" it refreshes the page but sometimes it doesn't detect the Polkadot.js Extension immediately and it's necessary to right click the browser refresh button and choose "Empty Cache and Hard Reload" a few times before it recognises the extension and bypasses that modal that says "FlappyTips 2 on Desktop: Install and enable Polkadot.js Extension Desktop users"

Can't play in two incognito windows because one of them doesn't allow you to choose "Empty Cache and Hard Reload"

But can play using IP address in one window i.e. https://139.144.96.196 and with domain in the other https://clawbird.com

Mobile user can still connect and win and beat Desktop users. Twitter message doesn't reflect game result

Mobile user can still win and beat Desktop users. It shows that they lost in Twitter message if they share the result even though they won (they could easily edit it) and shows they won in the UI and wanted to shared on twitter. Since it's not a native game it requires them to sign in to Twitter to share it.

We weren't going to let Mobile users win since they can't sign in with QR code, so users that aren't even establishing that they have a Substrate address loaded can play

Need to change logic from chainAccountResult === chainAccount ... to chainAccountResult === chainAccount && chainAccount !== 'DEMO-MOBILE' ...

Prevent user from sharing awesomeness when balance insufficient to pay for submission

At the moment a user can choose an account thats injected from Polkadot.js Extension (Desktop) or enter a Mnemonic Seed (Mobile), but that account could be an inactive account or a dust account and the extrinsic could fail. So it would be better UX to detect and warn the user that they have insufficient balance before they even try to submit

Inactive browser tab vs Active browser tab both lose

If you start one player in a Brave browser window tab, then start another player in a new incognito window of Brave, then if the first player changes to a different tab in the browser whilst the other player plays and clears a block, then when you open the second players window only then does their block obstacle appear, and if they don't clear any blocks you'd expect the second player to win since they cleared a block, but the result is that both lose for some reason...

Maybe need to check if the current browser tab isn't active at all during the game then the player automatically loses or game is invalidated... and prevent this behaviour repeating somehow

HTTP to HTTPS redirection only works on latest Firefox by not the latest Chrome or Brave

https://stackoverflow.com/questions/21106998/nginx-redirect-http-to-https-and-non-www-to-ww/75499730#75499730

I couldn't figure out why HTTP http://example.com wouldn't redirect to HTTPS https://example.com. i had configured it the following way like others have and according to https://nginx.org/en/docs/http/configuring_https_servers.html, even after restarting the server with nginx -s reload && sudo systemctl restart nginx

/etc/nginx/sites-available/example.com

server {
    listen 80;
    server_name www.example.com example.com;
    return 301 https://$server_name$request_uri;
}

server {
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 1m;

    listen                  443 ssl http2 default_server;
    listen                  [::]:443 ssl http2 default_server;
    server_name             www.example.com example.com;
    root                    /var/www/build;
    index                   index.html;
    location / {
                            try_files $uri /index.html =404;
    }
}

But even though i'm using the latest browsers, the redirection from HTTP to HTTPS only works with the latest in incognito window:

  • Firefox: 110.0 (64-bit) (both on Desktop and Mobile)

But not with the latest in incognito window even after clear cache and hard reload:

  • Brave: Version 1.48.164 Chromium: 110.0.5481.100 (Official Build) (x86_64)
  • Chrome: Version 110.0.5481.100 (Official Build) (x86_64)

With Brave and Chrome it just loads the default HTTP page "Welcome to nginx!" from /var/www/html/
index.nginx-debian.html

Prevent users from submitting a fake game result

Users could slow their CPU speed down to make it easier to win.
Possibly record the timestamp when the user starts playing and when they finish, and how many obstacles they cleared, and compare that with the actual time between the start and end blocks they played.
Send block hash of the to the server and verify its valid

could restrict each account to play once per day (approx x blocks) but we don't want to force people to have an account with an identity or require a payment to play

only allow a win if winning score was higher than y, otherwise a lose (prevent spam)

Application error

If you go to flappytips.herokuapp.com it gives an 'Application error'

Retrieve wallet info returns 403

Previously I able to use the 'hs-client' library to retrieve Wallet Info with

  // Get Wallet Info
  const wallet = walletClient.wallet('primary');
  const result = await wallet.getInfo();
  console.log('Wallet Info: ', result);

And the output was:

Wallet Info:  {
  network: 'main',
  wid: 0,
  id: 'primary',
  watchOnly: false,
  accountDepth: 1,
  token: '...',
  tokenDepth: 0,
  master: { encrypted: false },
  balance: {
    account: -1,
    tx: 0,
    coin: 0,
    unconfirmed: 0,
    confirmed: 0,
    lockedUnconfirmed: 0,
    lockedConfirmed: 0
  }
}

But now when i run the same query, the response is:

Error: Status code: 403.

Multiplayer game winner will lose if all other players leave the game or press Play Again before they finish

If you play two player on mobile, and if player 1 hits the first obstacle but the other player 2 doesn't hit the first obstacle, then player 1 result will be "waiting for other results", and on player 2's screen it will still show player 1 in the opponent list shown in blue, but if player 1 leaves the game (closes the browser tab, or even presses "Play Again") then they won't appear on the opponent list for player 2 anymore, and if that happens before player 2 hits a subsequent obstacle, then player 2 will lose because the game isn't correctly keeping the context of all the players that actually started anymore.

so need a fix where even if player 1 leaves the game before player 2 also hits a subsequent obstacle, player 2 will win...
and we need to communicate what's going on to the players

but also don't want a situation where someone with multiple browser devices and multiple browser tabs open tries to play them all at the same time... possible but hard.... and since some openings are at the bottom of the screen they might clear some obstacles not even pressing anything and possibly win... and don't want them spamming games

so we need the following changes:

  • first obstacle must prevent players clearing it if they don't press anything (i.e. the gap can't extend all the way to the bottom)
  • only allow a winning result if at least two players joined the game at a starting block (that's already the case) and if at least two players clear the a high number like the x-th block (proof of actually playing lol), to make it harder to win, and to have less asking for tips, and to make it harder to predict the result on prediction markets
    • note: we don't penalise them for not clearing the first block because players also need to learn to play, and we don't want to discourage them by throttling how often they can play just because they're learning keep hitting the first block (not spam)
  • add a "Practice" button instead of just "Play" so players can just learn to clear blocks, and allow them to play immediately single player (also for those who are addicted to the game and too impatient to wait for other players to play a game)
  • penalise players that click "Play" (instead of practicing by clicking "Practice" enough) but that don't clear at least the first block by making them wait say 30 blocks until they can play again (how long measured in blocks is the average game length? if it's 10 then they miss about 3 games). this is to prevent players spamming to win. this could just be stored on the backend, but could also be stored in a smart contract as a form of game player credibility..

net::ERR_CERT_COMMON_NAME_INVALID

Relates to #77 , only affects mobile devices, desktop works ok

Sometimes the mobile device can't connect to multiple desktop players at all though... when I inspected the browser console for the mobile device it gave error WebSocket connection to 'wss://139.144.96.196:5000/socket.io/?EIO=4&transport=websocket' failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID even though the certificate had common name clawbird.com (worked with Let's Encrypt, but then didn't work after i swapped to Sectigo CA, but then when i swapped back to Let's Encrypt it didn't work there anymore either on mobile

websocket.js:44 WebSocket connection to 'wss://clawbird.com:5000/socket.io/?EIO=4&transport=websocket' failed: One or more reserved bits are on: reserved1 = 1, reserved2 = 0, reserved3 = 0

Relates to this repository with latest code in PR #85

The error in production when i run yarn && yarn start is:

WebSocket connection to 'wss://clawbird.com:5000/socket.io/?EIO=4&transport=websocket' failed: One or more reserved bits are on: reserved1 = 1, reserved2 = 0, reserved3 = 0

It says the error is occurring on this line of the engine.io-client dependency https://github.com/socketio/engine.io-client/blob/main/lib/transports/websocket.ts#L78

It occurs in the file /src/sketches/sketch.js where I try to instantiate and connect to a Socket.IO endpoint

An old post mentioned they resolved a similar issue by upgrading Nginx, so I tried updating from Nginx 1.18 to 1.23.3 so nginx -V returned nginx version: nginx/1.23.3 but it still returned the same error

So I enabled Websockets debugging mentioned here https://socket.io/docs/v4/logging-and-debugging/ and then viewed the errors in the server logs:

[HPM] Upgrading to WebSocket
[HPM] Error occurred while proxying request clawbird.com:5000/socket.io/?EIO=4&transport=websocket to undefined [ECONNRESET] (https://nodejs.org/api/errors.html#errors_common_system_errors)
[HPM] WebSocket error: Error: read ECONNRESET
    at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
  errno: -104,
  code: 'ECONNRESET',
  syscall: 'read'
}
[HPM] Upgrading to WebSocket
[HPM] Error occurred while proxying request clawbird.com:5000/socket.io/?EIO=4&transport=websocket to undefined [ECONNRESET] (https://nodejs.org/api/errors.html#errors_common_system_errors)
[HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
    at new NodeError (node:internal/errors:399:5)
    at _write (node:internal/streams/writable:322:11)
    at Writable.write (node:internal/streams/writable:337:10)
    at Sender.sendFrame (/var/www/.yarn/__virtual__/ws-virtual-b72ae8e9d2/0/cache/ws-npm-8.11.0-ab72116a01-316b33aba3.zip/node_modules/ws/lib/sender.js:469:20)
    at Sender.send (/var/www/.yarn/__virtual__/ws-virtual-b72ae8e9d2/0/cache/ws-npm-8.11.0-ab72116a01-316b33aba3.zip/node_modules/ws/lib/sender.js:359:12)
    at WebSocket.send (/var/www/.yarn/__virtual__/ws-virtual-b72ae8e9d2/0/cache/ws-npm-8.11.0-ab72116a01-316b33aba3.zip/node_modules/ws/lib/websocket.js:468:18)
    at send (/var/www/.yarn/cache/engine.io-npm-6.4.0-1d74810449-bff0127303.zip/node_modules/engine.io/build/transports/websocket.js:84:29)
    at Object.encodePacket (/var/www/.yarn/cache/engine.io-parser-npm-5.0.6-6021cc2c82-e92255b546.zip/node_modules/engine.io-parser/build/cjs/encodePacket.js:10:12)
    at WebSocket.send (/var/www/.yarn/cache/engine.io-npm-6.4.0-1d74810449-bff0127303.zip/node_modules/engine.io/build/transports/websocket.js:95:29)
    at Socket.flush (/var/www/.yarn/cache/engine.io-npm-6.4.0-1d74810449-bff0127303.zip/node_modules/engine.io/build/socket.js:417:28) {
  code: 'ERR_STREAM_WRITE_AFTER_END'

Maybe Nginx 1.18 and 1.23.3 aren't compatible with NPM package socket.io-client 4.6.0 ?

I'm already using the latest Yarn 3 and Node.js 19.6.0

So I updated package.json to use latest 4.6.1

    "socket.io": "^4.6.1",
    "socket.io-client": "^4.6.1",

but that still returned the same error when i restarted the website.

It returns the following to curl request:

curl "https://clawbird.com:5000/socket.io/?EIO=4&transport=websocket"

{"code":3,"message":"Bad request"}

The error in development when after I change WSS=false in constants/index.js and in src/constants.js, and run yarn && yarn run dev using HTTP/WS i get error:

WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket' failed:

screenshot:

Screen Shot 2023-02-21 at 2 56 16 pm

and the server logs are:

[0] [HPM] Upgrading to WebSocket
[0] :socket sending packet "open" ({"sid":"JsX-unkjfAg0-P_0AAqB","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000})
[0] 2023-02-21T05:11:13.833Z engine:socket flushing buffer to transport
[0] 2023-02-21T05:11:13.833Z engine:ws writing "0{"sid":"JsX-unkjfAg0-P_0AAqB","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}"
[0] 2023-02-21T05:11:13.833Z engine:transport setting request
[0] 2023-02-21T05:11:13.833Z socket.io:server incoming connection with id JsX-unkjfAg0-P_0AAqB
[0] 2023-02-21T05:11:13.837Z engine applying middleware n°1
[0] 2023-02-21T05:11:13.837Z engine writing headers: {"Access-Control-Allow-Origin":"http://localhost:5000","Vary":"Origin"}
[0] 2023-02-21T05:11:13.838Z engine handshaking client "Yd2CGv_Vai5thT_pAAqC"
[0] 2023-02-21T05:11:13.839Z engine:transport readyState updated from undefined to open (websocket)
[0] 2023-02-21T05:11:13.839Z engine:socket readyState updated from undefined to opening
[0] 2023-02-21T05:11:13.839Z engine:socket readyState updated from opening to open
[0] 2023-02-21T05:11:13.839Z engine:socket sending packet "open" ({"sid":"Yd2CGv_Vai5thT_pAAqC","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000})
[0] 2023-02-21T05:11:13.839Z engine:socket flushing buffer to transport
[0] 2023-02-21T05:11:13.839Z engine:ws writing "0{"sid":"Yd2CGv_Vai5thT_pAAqC","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}"
[0] 2023-02-21T05:11:13.839Z engine:transport setting request
[0] 2023-02-21T05:11:13.839Z socket.io:server incoming connection with id Yd2CGv_Vai5thT_pAAqC
[0] 2023-02-21T05:11:13.841Z engine applying middleware n°1
[0] 2023-02-21T05:11:13.841Z engine writing headers: {"Access-Control-Allow-Origin":"http://localhost:5000","Vary":"Origin"}
[0] 2023-02-21T05:11:13.841Z engine handshaking client "zzvpEfEzbVvHqk_RAAqD"
[0] 2023-02-21T05:11:13.841Z engine:transport readyState updated from undefined to open (websocket)
[0] 2023-02-21T05:11:13.841Z engine:socket readyState updated from undefined to opening
[0] 2023-02-21T05:11:13.841Z engine:socket readyState updated from opening to open
[0] 2023-02-21T05:11:13.841Z engine:socket sending packet "open" ({"sid":"zzvpEfEzbVvHqk_RAAqD","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000})
[0] 2023-02-21T05:11:13.841Z engine:socket flushing buffer to transport
[0] 2023-02-21T05:11:13.841Z engine:ws writing "0{"sid":"zzvpEfEzbVvHqk_RAAqD","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}"
[0] 2023-02-21T05:11:13.841Z engine:transport setting request
[0] 2023-02-21T05:11:13.841Z socket.io:server incoming connection with id zzvpEfEzbVvHqk_RAAqD
[0] 2023-02-21T05:11:13.843Z engine applying middleware n°1
[0] 2023-02-21T05:11:13.843Z engine writing headers: {"Access-Control-Allow-Origin":"http://localhost:5000","Vary":"Origin"}
[0] 2023-02-21T05:11:13.843Z engine handshaking client "jMT4b1SUEl2Wb2kmAAqE"
[0] 2023-02-21T05:11:13.843Z engine:transport readyState updated from undefined to open (websocket)
[0] 2023-02-21T05:11:13.843Z engine:socket readyState updated from undefined to opening
[0] 2023-02-21T05:11:13.843Z engine:socket readyState updated from opening to open
[0] 2023-02-21T05:11:13.847Z engine:socket sending packet "open" ({"sid":"jMT4b1SUEl2Wb2kmAAqE","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000})
[0] 2023-02-21T05:11:13.847Z engine:socket flushing buffer to transport
[0] 2023-02-21T05:11:13.847Z engine:ws writing "0{"sid":"jMT4b1SUEl2Wb2kmAAqE","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}"
[0] 2023-02-21T05:11:13.847Z engine:transport setting request
[0] 2023-02-21T05:11:13.847Z socket.io:server incoming connection with id jMT4b1SUEl2Wb2kmAAqE
[0] 2023-02-21T05:11:13.850Z engine:transport readyState updated from open to closed (websocket)
[0] 2023-02-21T05:11:13.850Z engine:socket readyState updated from open to closed
[0] 2023-02-21T05:11:13.850Z socket.io:client client close with reason transport close
[0] 2023-02-21T05:11:13.850Z socket.io-parser encoding packet {"type":2,"data":["gameDataPlayers",{}],"nsp":"/"}
[0] 2023-02-21T05:11:13.850Z socket.io-parser encoded {"type":2,"data":["gameDataPlayers",{}],"nsp":"/"} as 2["gameDataPlayers",{}]
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
[0]     at Socket.Writable.write (node:internal/streams/writable:336:10)
[0]     at ClientRequest.<anonymous> (~/code/github/ltfschoen/flappytips2/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
[0]     at ClientRequest.emit (node:events:513:28)
[0]     at Socket.socketOnData (node:_http_client:574:11)
[0]     at Socket.emit (node:events:513:28)
[0]     at addChunk (node:internal/streams/readable:315:12)
[0]     at readableAddChunk (node:internal/streams/readable:289:9)
[0]     at Socket.Readable.push (node:internal/streams/readable:228:10) {
[0]   code: 'ERR_STREAM_WRITE_AFTER_END'
[0] }
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
[0]     at Socket.Writable.write (node:internal/streams/writable:336:10)
[0]     at ClientRequest.<anonymous> (~/code/github/ltfschoen/flappytips2/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
[0]     at ClientRequest.emit (node:events:513:28)
[0]     at Socket.socketOnData (node:_http_client:574:11)
[0]     at Socket.emit (node:events:513:28)
[0]     at addChunk (node:internal/streams/readable:315:12)
[0]     at readableAddChunk (node:internal/streams/readable:289:9)
[0]     at Socket.Readable.push (node:internal/streams/readable:228:10) {
[0]   code: 'ERR_STREAM_WRITE_AFTER_END'
[0] }
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
[0]     at Socket.Writable.write (node:internal/streams/writable:336:10)
[0]     at ClientRequest.<anonymous> (~/code/github/ltfschoen/flappytips2/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
[0]     at ClientRequest.emit (node:events:513:28)
[0]     at Socket.socketOnData (node:_http_client:574:11)
[0]     at Socket.emit (node:events:513:28)
[0]     at addChunk (node:internal/streams/readable:315:12)
[0]     at readableAddChunk (node:internal/streams/readable:289:9)
[0]     at Socket.Readable.push (node:internal/streams/readable:228:10) {
[0]   code: 'ERR_STREAM_WRITE_AFTER_END'
[0] }
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
[0]     at Socket.Writable.write (node:internal/streams/writable:336:10)
[0]     at ClientRequest.<anonymous> (~/code/github/ltfschoen/flappytips2/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
[0]     at ClientRequest.emit (node:events:513:28)
[0]     at Socket.socketOnData (node:_http_client:574:11)
[0]     at Socket.emit (node:events:513:28)
[0]     at addChunk (node:internal/streams/readable:315:12)
[0]     at readableAddChunk (node:internal/streams/readable:289:9)
[0]     at Socket.Readable.push (node:internal/streams/readable:228:10) {
[0]   code: 'ERR_STREAM_WRITE_AFTER_END'
[0] }
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
[0]     at Socket.Writable.write (node:internal/streams/writable:336:10)
[0]     at ClientRequest.<anonymous> (~/code/github/ltfschoen/flappytips2/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
[0]     at ClientRequest.emit (node:events:513:28)
[0]     at Socket.socketOnData (node:_http_client:574:11)
[0]     at Socket.emit (node:events:513:28)
[0]     at addChunk (node:internal/streams/readable:315:12)
[0]     at readableAddChunk (node:internal/streams/readable:289:9)
[0]     at Socket.Readable.push (node:internal/streams/readable:228:10) {
[0]   code: 'ERR_STREAM_WRITE_AFTER_END'
[0] }
[0] [HPM] WebSocket error: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
[0]     at new NodeError (node:internal/errors:387:5)
[0]     at _write (node:internal/streams/writable:321:11)
    at Socket.Writable.write ([HPM] Client disconnected

...

But if I remove the following code:

// https://github.com/chimurai/http-proxy-middleware/blob/master/examples/websocket/index.js
httpServer.on('upgrade', wsProxy.upgrade); // optional: upgrade externally
...
// https://www.npmjs.com/package/http-proxy-middleware#external-websocket-upgrade
const wsProxy = createProxyMiddleware({
 	  target: proxy_url,
 	  changeOrigin: true,
 	  ws: true,
 	  logger: console,
});
...
app.use('/socket.io/', wsProxy);

Then it works in development successfully without any errors and i can player multiplayer properly.

It seems like when it's working successfully, it uses both:

  • ws://localhost:4000/ws
  • ws://localhost:5000/socket.io/?EIO=4&transport=websocket

So if I removed those same lines from production,

and in the backend i changed the host to the IP address of the production server to https://${HOST_IP_ADDRESS}:5000

const httpServer = `https://clawbird.com:5000`
 const io = require("socket.io")(httpServer, {
   transports: ["websocket"],
   path: "/socket.io/",
   cors: {
     origin: proxy_url,
     credentials: true,
   }
 });

and in the frontend set the socket endpoint to https://clawbird.com:5000

const socketEndpoint = ;
const socket = io('https://clawbird.com:5000', {
  transports: ["websocket"],
  addTrailingSlash: true,
  path: "/socket.io/",
  withCredentials: true,
});

and lastly changed the following:

PORT = 5000;
httpServer.listen(
  PORT,
  process.env.NODE_ENV === 'production' ? HOST_IP_ADDRESS : '0.0.0.0',
);

then it worked again in production too

Linode deployment websockets solution

  • Replace usage of https://flappytips.herokuapp.com with the actual production endpoint like https://139.144.96.196 (since we're using Linode instead.
  • Linode production deployment was modified in production and got rid of WSS errors with multiplayer. But local code in development still produces this error when use yarn run dev.
WebSocket connection to 'ws://localhost:4000/ws' failed: 
WebSocketClient @ WebSocketClient.js:16
initSocket @ socket.js:24
(anonymous) @ socket.js:48
websocket.js:40 WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket' failed: 
  • Linode process.env was not previously working with WSS variable. Had to define it as let WSS = true in each file instead. Some changes were made to CORS too. Figure out why that allowed it to overcome this error websocket.js:40 WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket' failed: in production or on Heroku the error was WebSocket connection to 'wss://flappytips.herokuapp.com:5000/socket.io/?EIO=4&transport=websocket' failed: WebSocket is closed before the connection is established., such that if this happens in-game then it doesn't record the game ended block, and sits "Waiting for results...". Incorporate these hotfixes into the code, like changes in snippets below:
const corsWhitelist = [
  'https://139.144.96.196:443',
  'http://139.144.96.196:80',
  'https://139.144.96.196:4000',
  'https://139.144.96.196:5000',
  'http://139.144.96.196:4000',
  'http://139.144.96.196:5000',

if (process.env.NODE_ENV === 'production' && process.env.WSS) {
  proxy_url = `https:///139.144.96.196:${proxy_port}`;
} else if (process.env.NODE_ENV === 'production' && process.env.WSS !== true) {
  proxy_url = `http:///139.144.96.196:${proxy_port}`;
} else if (process.env.NODE_ENV !== 'production') {
  proxy_url = 'http:///139.144.96.196:5000';

let socketEndpoint = process.env.NODE_ENV === 'production'
  ? (
    process.env.WSS === true
    ? `wss://http://139.144.96.196:${PORT}`
    : `ws://http://139.144.96.196:${PORT}`

It was not necessary to do these things:

https://stackoverflow.com/a/31481025/3208553
https://socket.io/docs/v4/using-multiple-nodes/
https://devcenter.heroku.com/articles/session-affinity
8aaaadc

CSP Twitter image

Unsure how to update CSP when it displays the syndication.twitter.com/i/jot/embeds?l=%7B%22widget_origin%22%3A%22https%3A%2F%2Fclawbird.com%2F%22%2C%22widget_frame%22%3Afalse%2C%22language%22%3A%22en%22%2C%22message%22%3A%.......... net::ERR_BLOCKED_BY_CLIENT error in console when it opens the "Share & Request Tip (on-chain)!" modal, since i think that embed might be dynamic

This isn't important because the image still loads and it all still works

twitter relevant parts of the CSP are already

 Content-Security-Policy "default-src 'self' 'unsafe-eval'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' platform.twitter.com syndication.twitter.com; script-src-elem 'self' https://platform.twitter.com/widgets.js https://platform.twitter.com/js/button.e7f9415a2e000feaab02c86dd5802747.js; img-src 'self' data: https://syndication.twitter.com; frame-src 'self' https://platform.twitter.com/";

Update to React 18

react-dom.development.js:86 Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

Replace old syntax with new, like useEffect, etc

Request all tips to a governance account in the smart contract where tips claimed

with a fee split between going to the oracle, some into a pot to fund transactions to the smart contract to record each winner automatically and the rest going to the player.

custom chain governance proposal so community can vote as the oracle about who in game result was the winner in case of disputes, then the oracle account address needs to report the outcome to Zeitgeist within 24 hours

Refer to this code that shows an approach to splitting fees https://github.com/SkymanOne/vote-pray-love/blob/d44e042306a323ad64c9d9ebc5f42d3d84acdd17/pallets/slashing-voting/src/lib.rs

German mentioned:

Prevent multiple blocks overtaking each other making gameplay impossible

Fix issue where the block obstacle after the current block obstacle might go much faster and overtake, making it impossible to get through... make min space between obstacles somehow. This sometimes impacts one player but not another since game speed varies between players which is a consistency issue.

cargo-contract error[E0432]: unresolved import `contract_build::metadata::METADATA_FILE

When I ran cargo install cargo-contract --version 2.0.0-rc.1 on macOS it gave the following error:

    Compiling wasm-opt v0.111.0
    error[E0432]: unresolved import `contract_build::metadata::METADATA_FILE`
      --> /Users/luke/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-contract-2.0.0-rc.1/src/cmd/extrinsics/mod.rs:85:5
      |
    85 | use contract_build::metadata::METADATA_FILE;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `METADATA_FILE` in `metadata`

    For more information about this error, try `rustc --explain E0432`.
    The following warnings were emitted during compilation:

    warning: Git command failed with status: exit status: 128
    warning: Could not find `.git/HEAD` searching from `/Users/luke/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-contract-2.0.0-rc.1` upwards!

    error: could not compile `cargo-contract` due to previous error
    error: failed to compile `cargo-contract v2.0.0-rc.1`, intermediate artifacts can be found at `/var/folders/69/n6p09m3d6t5d7tw5kn9f3kkm0000gq/T/cargo-installytfbPN`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.