Skip to content

Commit

Permalink
Merge pull request #18 from prototyp3-dev/feature/dynamic-screenshot-…
Browse files Browse the repository at this point in the history
…text-riv-v0.2.2-alpha

Feature/dynamic screenshot text riv v0.2.2 alpha
  • Loading branch information
felipefg authored Feb 24, 2024
2 parents 6942704 + c195c76 commit 50fd889
Show file tree
Hide file tree
Showing 81 changed files with 10,588 additions and 2,312 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.sunodo
.venv
__pycache__
cartridges
cartridges
cache
frontend/app/contracts
!frontend/app/contracts/RivesScoreNFT.sol
8 changes: 7 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ WORKDIR /opt/tools
RUN <<EOF
apt-get update && \
apt-get install -y --no-install-recommends wget=1.21.2-2ubuntu1 ca-certificates=20230311ubuntu0.22.04.1 \
build-essential=12.9ubuntu3 sqlite3=3.37.2-2ubuntu0.3 git=1:2.34.1-1ubuntu1.10 squashfs-tools=1:4.5-3build1&& \
build-essential=12.9ubuntu3 sqlite3=3.37.2-2ubuntu0.3 git=1:2.34.1-1ubuntu1.10 squashfs-tools=1:4.5-3build1 \
libjpeg-dev=8c-2ubuntu10 zlib1g-dev=1:1.2.11.dfsg-2ubuntu9.2 libfreetype6-dev=2.11.1+dfsg-1ubuntu0.2 && \
wget -O machine-emulator-tools.deb https://github.com/cartesi/machine-emulator-tools/releases/download/v${MACHINE_EMULATOR_TOOLS_VERSION}/machine-emulator-tools-v${MACHINE_EMULATOR_TOOLS_VERSION}.deb && \
rm -rf /var/lib/apt/lists/*
EOF
Expand Down Expand Up @@ -76,10 +77,15 @@ WORKDIR /opt/cartesi/dapp
COPY main.py .
COPY cartesapp cartesapp
COPY app app
COPY misc/Rives-Logo.png misc/Rives-Logo.png
COPY misc/snake.sqfs misc/snake.sqfs
COPY misc/2048.sqfs misc/2048.sqfs
COPY misc/freedoom.sqfs misc/freedoom.sqfs
COPY misc/antcopter.sqfs misc/antcopter.sqfs
COPY misc/monky.sqfs misc/monky.sqfs
COPY misc/test.rivlog misc/test.rivlog

COPY misc/font misc/font

FROM base as dapp

Expand Down
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,20 @@ docker build --tag sunodo/sdk:0.2.0-riv . --target sunodo-riv --progress plain .

## Building

Build with:
Build backend with:

```shell
sunodo build
```

You should also install the frontend dependencies. First install [cartesi client](https://github.com/prototyp3-dev/cartesi-client), then run:

```shell
cd frontend
yarn
npm link cartesi-client
```

## Running

Run the DApp environment with:
Expand All @@ -46,7 +54,31 @@ Run the DApp environment with:
sunodo run
```

## Generating frontend libs
Then deploy the Rives Screenshot NFT contract (you should have already installed frontend dependencies)

```shell
cd frontend
MNEMONIC='test test test test test test test test test test test junk'
RPC_URL=http://127.0.0.1:8545
DAPP_ADDRESS=0x70ac08179605AF2D9e75782b8DEcDD3c22aA4D0C
BITMASK_ADDRESS=0xF5B2d8c81cDE4D6238bBf20D3D77DB37df13f735
forge create --constructor-args $DAPP_ADDRESS --rpc-url "$RPC_URL" --mnemonic "$MNEMONIC" --json contracts/RivesScoreNFT.sol:RivesScoreNFT --libraries node_modules/@cartesi/util/contracts/Bitmask.sol:Bitmask:$BITMASK_ADDRESS
```

Finally, run the frontend

```shell
cd frontend
yarn dev
```

You will be able to mint the NFTs once the epoch has finished, but you can increase the time of the local dev chain with

```shell
curl -H "Content-Type: application/json" -X POST --data '{"id":1337,"jsonrpc":"2.0","method":"evm_increaseTime","params":[864000]}' http://localhost:8545
```

## (Cartesapp) Generating frontend libs

Import cartesapp manager and add module

Expand Down
2 changes: 0 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
from . import setup, cartridge, replay, scoreboard
# TODO: use settings file instead of init
41 changes: 28 additions & 13 deletions app/cartridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
from cartesi.abi import String, Bytes, Bytes32, UInt

from cartesapp.storage import Entity, helpers, seed
from cartesapp.manager import query, mutation, get_metadata, event, output, add_output, emit_event, contract_call
from cartesapp.context import get_metadata
from cartesapp.input import query, mutation
from cartesapp.output import event, output, add_output, emit_event, contract_call

from .riv import riv_get_cartridge_info, riv_get_cartridge_screenshot, riv_get_cartridges_path, riv_get_cover, riv_get_cartridge_outcard
from .setup import AppSettings
from .riv import riv_get_cartridge_info, riv_get_cartridge_screenshot, riv_get_cartridges_path, riv_get_cover, riv_get_cartridge_outcard, replay_log
from .settings import AppSettings

LOGGER = logging.getLogger(__name__)

Expand All @@ -26,7 +28,7 @@
class Cartridge(Entity):
id = helpers.PrimaryKey(str, 64)
name = helpers.Required(str, index=True, unique=True)
user_address = helpers.Required(str, 66)
user_address = helpers.Required(str, 42)
info = helpers.Optional(helpers.Json, lazy=True)
created_at = helpers.Required(int)
cover = helpers.Optional(bytes, lazy=True)
Expand All @@ -36,25 +38,31 @@ def initialize_data():
cartridge_example_file = open('misc/snake.sqfs','rb')
cartridge_example_data = cartridge_example_file.read()
cartridge_example_file.close()
create_cartridge(cartridge_example_data,msg_sender="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
create_cartridge(cartridge_example_data,msg_sender="0xAf1577F6A113da0bc671a59D247528811501cF94")
if AppSettings.rivemu_path is None: os.remove('misc/snake.sqfs')

cartridge_example_file = open('misc/freedoom.sqfs','rb')
cartridge_example_data = cartridge_example_file.read()
cartridge_example_file.close()
create_cartridge(cartridge_example_data,msg_sender="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
create_cartridge(cartridge_example_data,msg_sender="0xAf1577F6A113da0bc671a59D247528811501cF94")
if AppSettings.rivemu_path is None: os.remove('misc/freedoom.sqfs')

cartridge_example_file = open('misc/antcopter.sqfs','rb')
cartridge_example_data = cartridge_example_file.read()
cartridge_example_file.close()
create_cartridge(cartridge_example_data,msg_sender="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
create_cartridge(cartridge_example_data,msg_sender="0xAf1577F6A113da0bc671a59D247528811501cF94")
if AppSettings.rivemu_path is None: os.remove('misc/antcopter.sqfs')

cartridge_example_file = open('misc/monky.sqfs','rb')
cartridge_example_data = cartridge_example_file.read()
cartridge_example_file.close()
create_cartridge(cartridge_example_data,msg_sender="0xAf1577F6A113da0bc671a59D247528811501cF94")
if AppSettings.rivemu_path is None: os.remove('misc/monky.sqfs')

cartridge_example_file = open('misc/2048.sqfs','rb')
cartridge_example_data = cartridge_example_file.read()
cartridge_example_file.close()
create_cartridge(cartridge_example_data,msg_sender="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
create_cartridge(cartridge_example_data,msg_sender="0xAf1577F6A113da0bc671a59D247528811501cF94")
if AppSettings.rivemu_path is None: os.remove('misc/2048.sqfs')


Expand Down Expand Up @@ -190,8 +198,6 @@ def cartridge(payload: CartridgePayload) -> bool:
def cartridge_info(payload: CartridgePayload) -> bool:
cartridge = helpers.select(c for c in Cartridge if c.id == payload.id).first()

LOGGER.info(cartridge)

if cartridge is not None:
cartridge_dict = cartridge.to_dict(with_lazy=True)
cartridge_dict['cover'] = base64.b64encode(cartridge_dict['cover'])
Expand Down Expand Up @@ -266,16 +272,25 @@ def create_cartridge(cartridge_data,**metadata):
Info(**cartridge_info_json)

# check if cartridge runs
riv_get_cartridge_outcard(data_hash,0,None,None)
test_replay_file = open('misc/test.rivlog','rb')
test_replay = test_replay_file.read()
test_replay_file.close()

# TODO: allow one of theses tests
# outcard_raw, outhash, screenshot = replay_log(data_hash,test_replay,'',b'')
# riv_get_cartridge_outcard(data_hash,0,None,None)

cartridge_cover = riv_get_cover(data_hash)
if cartridge_cover is None or len(cartridge_cover) == 0:
cartridge_cover = riv_get_cartridge_screenshot(data_hash,0)
# cartridge_cover = screenshot

user_address = metadata.get('msg_sender')
if user_address is not None: user_address = user_address.lower()
c = Cartridge(
id = data_hash,
name = cartridge_info_json['name'],
user_address = metadata.get('msg_sender'),
user_address = user_address,
created_at = metadata.get('timestamp') or 0,
info = cartridge_info_json,
cover = cartridge_cover
Expand All @@ -290,7 +305,7 @@ def delete_cartridge(cartridge_id,**metadata):
if cartridge is None:
raise Exception(f"Cartridge doesn't exist")

if cartridge.user_address != metadata['msg_sender']:
if cartridge.user_address != metadata['msg_sender'].lower():
raise Exception(f"Sender not allowed")

cartridge.delete()
Expand Down
112 changes: 112 additions & 0 deletions app/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import os
from enum import Enum
from PIL import Image, ImageFont, ImageDraw
from Crypto.Hash import SHA256
import base58
import tempfile
import io

from .protobuf_models import unixfs_pb2, merkle_dag_pb2

DEFAULT_HEIGHT = 512
BG_EXTRA_WIDTH = 400
# MAIN_FONT_SIZE = 48
# AUX_FONT_SIZE = 16
MAIN_FONT_SIZE = 42
AUX_FONT_SIZE = 28

initial_space = 30
font2_initial_space = 60
final_space = 10
total_font_space = 80

class ScoreType(Enum):
default = 0
scoreboard = 1
tournament = 2


class GameplayHash:
cartridge_replays = {}
def __new__(cls):
return cls

@classmethod
def add(cls, cartridge_id, replay_hash):
if cls.cartridge_replays.get(cartridge_id) is None: cls.cartridge_replays[cartridge_id] = {}
cls.cartridge_replays[cartridge_id][replay_hash] = True

@classmethod
def check(cls, cartridge_id, replay_hash):
return cls.cartridge_replays.get(cartridge_id) is None \
or cls.cartridge_replays[cartridge_id].get(replay_hash) is None \
or cls.cartridge_replays[cartridge_id][replay_hash] == False

def get_cid(data: bytes) -> str:

unixf = unixfs_pb2.Data()
unixf.Type = 2 # file
unixf.Data = data
unixf.filesize = len(unixf.Data)

mdag = merkle_dag_pb2.MerkleNode()
mdag.Data = unixf.SerializeToString()

mdag_data = mdag.SerializeToString()

h = SHA256.new()
h.update(mdag_data)
sha256_code = "12"
size = hex(h.digest_size)[2:]
digest = h.hexdigest()
combined = f"{sha256_code}{size}{digest}"
multihash = base58.b58encode(bytes.fromhex(combined))
cid = multihash.decode('utf-8')

return cid


def screenshot_add_score(screenshot_data: bytes, game: str, score: int, user: str) -> bytes:
screenshot_temp = tempfile.NamedTemporaryFile()
screenshot_file = screenshot_temp.file
screenshot_file.write(screenshot_data)
screenshot_file.flush()

img = Image.open(screenshot_temp.name)

screenshot_temp.close()

height = int((DEFAULT_HEIGHT // img.size[1]) * img.size[1])
width = int(height / img.size[1] * img.size[0])

space_between = (height - initial_space - final_space - 3 * total_font_space) // 2

resized_img = img.resize((width,height))

bg_img = Image.new('RGB', ( width + BG_EXTRA_WIDTH, height), color='#202020')

draw = ImageDraw.Draw(bg_img)

font28 = ImageFont.truetype('misc/font/Retro Gaming.ttf',size=28)
font42 = ImageFont.truetype('misc/font/Retro Gaming.ttf',size=42)

draw.text((width + 10, initial_space), f"game",fill='#b3b3b3',font=font28)
draw.text((width + 10, font2_initial_space), f"{game}",fill='#b3b3b3',font=font42)

draw.text((width + 10, initial_space + total_font_space + space_between), f"score",fill='#b3b3b3',font=font28) # 170
draw.text((width + 10, font2_initial_space + total_font_space + space_between), f"{score}",fill='#b3b3b3',font=font42) # 200

draw.text((width + 10, initial_space + 2*(total_font_space + space_between)), f"user",fill='#b3b3b3',font=font28) # 300
draw.text((width + 10, font2_initial_space + 2*(total_font_space + space_between)), f"{user}",fill='#b3b3b3',font=font42) # 330
logo = Image.open('misc/Rives-Logo.png')

bgc = bg_img.convert('RGBA')
bgc.paste(logo,(width + BG_EXTRA_WIDTH - 100,10),logo.convert('RGBA'))

bgc.paste(resized_img,(0,0))

img_byte_arr = io.BytesIO()
bgc.save(img_byte_arr, format='PNG')

return img_byte_arr.getvalue()

20 changes: 20 additions & 0 deletions app/protobuf_models/merkle-dag.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";

//package merkleDag;

// An IPFS MerkleDAG Link
message MerkleLink {
bytes Hash = 1; // multihash of the target object
string Name = 2; // utf string name
uint64 Tsize = 3; // cumulative size of target object

// user extensions start at 50
}

// An IPFS MerkleDAG Node
message MerkleNode {
repeated MerkleLink Links = 2; // refs to other objects
bytes Data = 1; // opaque user data

// user extensions start at 50
}
27 changes: 27 additions & 0 deletions app/protobuf_models/merkle_dag_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions app/protobuf_models/unixfs.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
syntax = "proto2";

package unixfs.pb;

message Data {
enum DataType {
Raw = 0;
Directory = 1;
File = 2;
Metadata = 3;
Symlink = 4;
HAMTShard = 5;
}

required DataType Type = 1;
optional bytes Data = 2;
optional uint64 filesize = 3;
repeated uint64 blocksizes = 4;

optional uint64 hashType = 5;
optional uint64 fanout = 6;
}

message Metadata {
optional string MimeType = 1;
}
Loading

0 comments on commit 50fd889

Please sign in to comment.