From 806087b6d99a99f9523ad5e666899c4dd3a9662a Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Thu, 23 May 2019 17:15:22 -0700 Subject: [PATCH 01/36] Swap out assets with user submissions --- assets/avatar-sheet.png | Bin 0 -> 2611 bytes assets/earth-01.png | Bin 0 -> 3145 bytes assets/grass-32x32.png | Bin 0 -> 1986 bytes assets/slime-blue.png | Bin 0 -> 682 bytes components/index.js | 34 +++++++++++++++++----------------- 5 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 assets/avatar-sheet.png create mode 100644 assets/earth-01.png create mode 100644 assets/grass-32x32.png create mode 100644 assets/slime-blue.png diff --git a/assets/avatar-sheet.png b/assets/avatar-sheet.png new file mode 100644 index 0000000000000000000000000000000000000000..735479e6c4c397aee5ae5d556b02eb4d9becb870 GIT binary patch literal 2611 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 literal 0 HcmV?d00001 diff --git a/assets/grass-32x32.png b/assets/grass-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..147342125324f252a43547faf322da4d640ebea9 GIT binary patch literal 1986 zcmV;z2R-M?s9>M``&~-il000McNliru;{hH8Bn&S%rS<>-2Tw^v zK~z}7eOJ$G8|fK-c4j2YqmiOnf#g`&s#J^)3Bo?Cv2epe*&YPFhBcUi4-I=+%u9xdKlg}Gv1|hvNiL) zKcDA$-|vgZd$Gy&i)qjkS?*)AyoBBTBlI*201$4bU}sy%m15BAE_#~9e<$-I|F*rc zirxJq3~LGiAhR~$cl6LX07&LV2zuRxooykrHb7*#k7QoN-o*(U2{%)SEcX#^rr4qX zX;ck%uy=8S&GHhzBS++Tdt)_VW4%y>n0(+=0Khs-ApOYYgZ(#O;*0q<=F0-Ka})Dr z0quh*9C?J1>0sX&EEa0RW603Gc_B;K(CbQU_B?h=c1~**Lz@u~}ZiJ6(gU z3V5e$5GX=B+d`vi004TLh2K8?#2MyFG0tRIQxI;ZxH?p+cv?nJvjQ0oYYO6Nna}n# z3(uY(1}Y?wv0N#Ja5Ke0=yeylQVj95jP*j1%NbejBeOQZu%;lg+-K*>ych_AFwoO1 zj9=#wZl*X9@-eI_5C~lBg(4daH&bkAv%G}8ixYHS&M-#&AhNS9B=aKH3q{Vjr&)}d za5IIk|2AO9lrY&4;%OO;s)618qtB(gR5*yeixcQ|7m+#7d82AzSW~dIv%n743q|bi z9|ZtsXIoscuXA~Rx3#mtK?3kFaJO{Sf68K5Qy3%RW(r$73)s6jVWWE&Cv0$7Q?Onr zvQ4TW0Bmop2F_*H1|AD0XDY;Q6qeLMxS4_@kC1-k!Z;1Vk~$E(QHb3r+=K}?Vd8SS zgGSZB$aLT)OpF}~Bh$frS%7gG!jB)$@$~UBj&F1fr2!iM89>U|ct8FW(2?~*@iPlD zYl8q{2@~aVJEeF()=r?|y59q&t=-Am7>k1{2 z%!>#&Qvs|A;bscMONAwsf*=~j)ABC{3^|I-^`X~Y>|LC&v;TKque%stDjZ0oYIrOl z-c#uah;*35X#6_Q&bM|J0u{SkdjEM6DJuQ-i)m!m1_(D(5EuS5smQ7T-DqQSrozs) znAfw#2;S)$8dU>>gE>Uz`fw8_!p#(Z8vKl>kC*Y|hjWZf2Vysh^dlF#(MGf|!N_zl zrG)T){0Z#?Pe-DK31*8C=tdjXVZv*H9~>l~q=Npz5t-}%LMupi{mX|RsCRb)s-9n} zMS0e)j7$f)QVi01&kyk7{x#E-_JAiVi}=^y`-m1M zkW~SlE1Ro8sv0d!pq-oe{+q9u;({OHdfjD4XljZCfc*-hphEf9e8vrsAi;&q(G)j2$T; zQqjT$PgWN3&%-+A%L2xZ1nt~}IyZ%}BVo{`!o{bOpukmDM(Ho^T1!6af>~~!lry=M@+v9FG z%KA?LjX%7_^^0kYi&t(MKg9OND(q|v;btnZHPQI>uU&Sw#hS5RC>mIKI)DUW91c z2jgiO+PR6r!5pM}HpFfey5U`1^ty{FC4|{x1lqY7xS-G)Hl>8{WMvW0o*zQ&MzOWC zfLCunLi|lSRiUz4QU_y4LNYG~xJ`4Rb7k`a0svyyyD9ZF3nSBkb(nygFflS6bgpbn zDIv(Jce9!;M)2zGM@-ICM3(zJBLrHjsDyqD&>}&jlQ_M#v%vcAzbW}=$lXobzg;Ku zBDX=fyM|ML{d4@+hEQa=5C6qBSBeGn!hchWr)3Ol3cvRQR$SQ;Dnt=#MYJ#p>;dfq zZ`8&g-trKVRqwJoUlusS%pV7=6^=Y&JoPjSZo)+V+gpqs2~x&J|IL?B{_tP?5ArHF Ue-5p8*Z=?k07*qoM6N<$f>&CvGXMYp literal 0 HcmV?d00001 diff --git a/assets/slime-blue.png b/assets/slime-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..b3dabec6cecf325d5f8aba85ba2a4377c82b559e GIT binary patch literal 682 zcmV;b0#*HqP)WFU8GbZ8()Nlj2>E@cM*00It4L_t(o!|hf*YXU(K zeRnZPAXkfMlR}$=tHOCH0%`mq2_$6@@&{5`hm;0Pl^>8_;b|$@XcbTn5p7Ng*j%bz zXZLp3^COiGh~S?4-n=(+Gr%cMaqMC3@!#YS_gTAwIyX-MfVdW{Pv0K4zIPh}2?J%o zMb8%=i2A*)iCDXU)69#W54TzY01SqsHHkG5YhMB>@I#WYvw+97K=fJ>gW(7O;8rUr zdj8s;Mq5{~8wqf5c3D;u&AOYpuxxF$H`*LfkThd7>ux3$j1~w|YW!EJ@b$CGOv2V> zpK2`wNmVFG>`;U}oNj15XI(&RAr%T0Y+cJy)(m_T36%;=37rbIsp?IBGcwd7s)9Bo z6>K?kIZKm&y7vJ6O=$-qaYi#=mCMLa#+aX5a0(^<{Bkk0V4$12 zft@pQ44l;iBNt0kO4$W+CNcpXA$AX)k^*?A1pt<%8$c>QO_@_GD6l-DDY$MH8gX`6%aeaz_m3?808KG4)y70V Q(EtDd07*qoM6N<$f|gDXH~;_u literal 0 HcmV?d00001 diff --git a/components/index.js b/components/index.js index 4e86cd1..078dd05 100644 --- a/components/index.js +++ b/components/index.js @@ -36,8 +36,8 @@ async function UI () { let copycat_mode = false; let default_walk_speed = 5; let default_jump_speed = 25; - let map_size = 30; - let tile_size = 50; + let map_size = 32; + let worldSize = 64; let tile_index = {}; //keyboard stuff @@ -307,38 +307,38 @@ async function UI () { for(let i=0; i<30; i++){ bricks.push(new Sprite(brick, 512, 512, 50, 50, i*50, 600)); }*/ - let grass = images.getImage('grass.jpg'); + let grass = images.getImage('grass-32x32.png'); for (let j = 0; j< map_size; j++) { for (let i = 0; i < map_size; i++) { - let tile = new Sprite(grass, 100, 100, 50, 50, i * 50, j * 50) + let tile = new Sprite(grass, 32, 32, 32, 32, i * 32, j * 32) tile.bg = true; bricks.push(tile); } } - let rocks = images.getImage('rocks.jpg'); + let rocks = images.getImage('dark-earth-01.png'); for (let j = 0; j< map_size; j++) { - bricks.push(new Sprite(rocks, 400, 400, tile_size, tile_size, j * tile_size, -tile_size)); - bricks.push(new Sprite(rocks, 400, 400, tile_size, tile_size, j * tile_size, map_size * tile_size)); - bricks.push(new Sprite(rocks, 400, 400, tile_size, tile_size, -tile_size, j * tile_size)); - bricks.push(new Sprite(rocks, 400, 400, tile_size, tile_size, map_size * tile_size, j * tile_size)); + bricks.push(new Sprite(rocks, 32, 32, 32, 32, j * worldSize, -worldSize)); + bricks.push(new Sprite(rocks, 32, 32, 32, 32, j * worldSize, map_size * worldSize)); + bricks.push(new Sprite(rocks, 32, 32, 32, 32, -worldSize, j * worldSize)); + bricks.push(new Sprite(rocks, 32, 32, 32, 32, map_size * worldSize, j * worldSize)); } - let wall = images.getImage('brick.png'); + let wall = images.getImage('earth-01.png'); - for (let i = 2; i < 20; i++) { bricks.push(new Sprite(wall, 200, 200, 50, 50, i * 50, 2 * 50)); } - for (let i = 2; i < 15; i++) { bricks.push(new Sprite(wall, 200, 200, 50, 50, i * 50, 12 * 50)); } - for (let i = 2; i < 20; i++) { bricks.push(new Sprite(wall, 200, 200, 50, 50, 18 * 50, i * 50)); } + for (let i = 2; i < 20; i++) { bricks.push(new Sprite(wall, 32, 32, 32, 32, i * 50, 2 * 50)); } + for (let i = 2; i < 15; i++) { bricks.push(new Sprite(wall, 32, 32, 32, 32, i * 50, 12 * 50)); } + for (let i = 2; i < 20; i++) { bricks.push(new Sprite(wall, 32, 32, 32, 32, 18 * 50, i * 50)); } } function loadPlayers () { - let mario = images.getImage('mario.png'); + let avatar = images.getImage('avatar-sheet.png'); - player = new Sprite(mario, 480, 640, 48, 64, 380, 380); + player = new Sprite(avatar, 19, 26, 19, 26, 380, 380); player.dx = 0; player.dy = 0; player.walkspeed = default_walk_speed; @@ -356,10 +356,10 @@ async function UI () { players.push(player2); } - let goomba = images.getImage('goomba.png'); + let slime = images.getImage('slime-blue.png'); for(let i = 0; i<7; i++) { - let player3 = new Sprite(goomba, 900, 900, 48, 64, i * 200, i * 200 + 100); + let player3 = new Sprite(slime, 32, 32, 64, 64, i * 200, i * 200 + 100); player3.dx = Math.floor( application.machine.generator.next.percent() * 3 + 2 ); player3.dy = Math.floor( application.machine.generator.next.percent() * 3 + 2); From 2b8c0b5a12c476b0965bed6e8526c37965ee702a Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 29 May 2019 23:09:04 -0700 Subject: [PATCH 02/36] snapshot for public demo/collab edit --- README.md | 29 +++++++++- components/canvas.js | 2 +- config.json | 2 + constants.js | 7 ++- inputs/weapons.json | 9 --- inputs/weapons/axe.json | 6 ++ inputs/weapons/cutlass.json | 6 ++ inputs/weapons/dagger.json | 6 ++ inputs/weapons/halberd.json | 6 ++ inputs/weapons/hammer.json | 6 ++ inputs/weapons/rapier.json | 6 ++ inputs/weapons/sword.json | 6 ++ package.json | 10 ++-- rpg.js | 6 +- scripts/agents.js | 33 +++++++---- scripts/build.js | 20 +++++++ scripts/rpg.js | 111 ++++++++++++++++++++++++++++++++++++ types/agent.js | 80 ++++++++++++++++---------- types/player.js | 31 +++++++++- types/rpg.js | 61 +++++++++++++++----- types/world.js | 8 ++- webpack.config.js | 27 ++++++--- 22 files changed, 391 insertions(+), 87 deletions(-) delete mode 100644 inputs/weapons.json create mode 100644 inputs/weapons/axe.json create mode 100644 inputs/weapons/cutlass.json create mode 100644 inputs/weapons/dagger.json create mode 100644 inputs/weapons/halberd.json create mode 100644 inputs/weapons/hammer.json create mode 100644 inputs/weapons/rapier.json create mode 100644 inputs/weapons/sword.json create mode 100644 scripts/build.js create mode 100644 scripts/rpg.js diff --git a/README.md b/README.md index f9f5506..d5ac8fb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,32 @@ -# RPG() +# `@fabric/rpg` simple Fabric-hosted RPG ## Quick Start `npm install && npm start` + +## Developers +``` +mkdir ~/workspace && cd ~/workspace # create workspace (if not exist) and cd + +# fork & clone each repo for contributions you might make +git clone git@github.com:YOUR_USERNAME/rpg.git +git clone git@github.com:YOUR_USERNAME/http.git +git clone git@github.com:YOUR_USERNAME/fabric.git + +# configure symlinks for each independent repository +cd fabric && git checkout rpg-0.2.0-develop && yarn link +cd ../http && git checkout fabric-0.1.0 && yarn link && yarn link @fabric/core +cd ../rpg && git checkout rpg-0.2.0-develop && yarn link @fabric/core && yarn link @fabric/http + +# install & start +yarn install +yarn start + +# enable use of development version for target... +yarn link @fabric/core +yarn link @fabric/http + +# Restart the main process and check dependencies. +# Good luck, have fun! +# ~ E +``` diff --git a/components/canvas.js b/components/canvas.js index 74b5af1..28092dd 100644 --- a/components/canvas.js +++ b/components/canvas.js @@ -109,7 +109,7 @@ class Canvas extends Fabric.App { } render () { - return ``; + return `${history.render()}`; } } diff --git a/config.json b/config.json index 78aa5c8..1515143 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,6 @@ { + "name": "@rpg/core", + "synopsis": "Peer-to-peer game engine, built with Fabric.", "authority": "chat.roleplaygateway.com:9999", "entropy": "6170cc426cbe663cef4342dc73860f2ab26c0c0e411c390ca064208bada65e6e", "provide": ["rpg"], diff --git a/constants.js b/constants.js index b47186b..07e8d22 100644 --- a/constants.js +++ b/constants.js @@ -3,7 +3,7 @@ // prime constants const GENESIS_HASH = 'f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef'; const HTTP_HOST = 'api.roleplaygateway.com'; -const TICK_INTERVAL = 10000; +const TICK_INTERVAL = 1000; const FRAMES_PER_BLOCK = 60 * 60 * 10 * 1000; const HEADER_LENGTH = 64; @@ -20,6 +20,8 @@ const MAX_LOOT_WORTH = 30; const MIN_ITEM_DURABILITY = 20; const MAX_ITEM_DURABILITY = 50; +const SPRITE_SIZE_IN_BYTES = Math.pow(256, 3) / 8; // 3x 256-bit dimensions + module.exports = { GENESIS_HASH, BIG_LIST_LIMIT, @@ -32,5 +34,6 @@ module.exports = { MIN_LOOT_WORTH, MAX_LOOT_WORTH, MIN_ITEM_DURABILITY, - MAX_ITEM_DURABILITY + MAX_ITEM_DURABILITY, + SPRITE_SIZE_IN_BYTES }; diff --git a/inputs/weapons.json b/inputs/weapons.json deleted file mode 100644 index 76a2b4b..0000000 --- a/inputs/weapons.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { "name": "sword", "attack": 4 , "speed": 5 }, - { "name": "cutlass", "attack": 4 , "speed": 4 }, - { "name": "rapier", "attack": 4 , "speed": 6 }, - { "name": "axe", "attack": 5 , "speed": 3 }, - { "name": "dagger", "attack": 2 , "speed": 7 }, - { "name": "hammer", "attack": 4 , "speed": 4 }, - { "name": "halberd", "attack": 5 , "speed": 3, "range": 2 } -] diff --git a/inputs/weapons/axe.json b/inputs/weapons/axe.json new file mode 100644 index 0000000..542f62a --- /dev/null +++ b/inputs/weapons/axe.json @@ -0,0 +1,6 @@ +{ + "name": "axe", + "attack": 5, + "speed": 3, + "range": 2 +} diff --git a/inputs/weapons/cutlass.json b/inputs/weapons/cutlass.json new file mode 100644 index 0000000..e007954 --- /dev/null +++ b/inputs/weapons/cutlass.json @@ -0,0 +1,6 @@ +{ + "name": "cutlass", + "attack": 5, + "speed": 3, + "range": 2 +} diff --git a/inputs/weapons/dagger.json b/inputs/weapons/dagger.json new file mode 100644 index 0000000..5593b2f --- /dev/null +++ b/inputs/weapons/dagger.json @@ -0,0 +1,6 @@ +{ + "name": "dagger", + "attack": 5, + "speed": 4, + "range": 1 +} diff --git a/inputs/weapons/halberd.json b/inputs/weapons/halberd.json new file mode 100644 index 0000000..9ebcfb0 --- /dev/null +++ b/inputs/weapons/halberd.json @@ -0,0 +1,6 @@ +{ + "name": "halberd", + "attack": 5, + "speed": 1, + "range": 4 +} diff --git a/inputs/weapons/hammer.json b/inputs/weapons/hammer.json new file mode 100644 index 0000000..775b62d --- /dev/null +++ b/inputs/weapons/hammer.json @@ -0,0 +1,6 @@ +{ + "name": "hammer", + "attack": 5, + "speed": 3, + "range": 3 +} diff --git a/inputs/weapons/rapier.json b/inputs/weapons/rapier.json new file mode 100644 index 0000000..cb6a093 --- /dev/null +++ b/inputs/weapons/rapier.json @@ -0,0 +1,6 @@ +{ + "name": "rapier", + "attack": 5, + "speed": 3, + "range": 3 +} diff --git a/inputs/weapons/sword.json b/inputs/weapons/sword.json new file mode 100644 index 0000000..98f4097 --- /dev/null +++ b/inputs/weapons/sword.json @@ -0,0 +1,6 @@ +{ + "name": "sword", + "attack": 5, + "speed": 2, + "range": 3 +} diff --git a/package.json b/package.json index 560f9d6..6ef47b0 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,19 @@ { "name": "@fabric/rpg", - "version": "0.1.0-dev", + "version": "0.2.0-dev", "description": "RPG is a template for building role-playing games.", "main": "types/rpg.js", "scripts": { "agents": "node scripts/agents.js", - "build": "webpack", + "build:scripts": "webpack scripts/rpg.js", + "build": "npm run compile", + "compile": "node scripts/build.js && npm run build:scripts", "coverage": "nyc report --reporter html npm test", "dev": "webpack-dev-server", "docs": "npm run build && jsdoc -c jsdoc.json types README.md -d docs && ecstatic docs", "report:coverage": "nyc report --reporter=text-lcov > reports/coverage.lcov && codecov", "review:coverage": "npm run coverage && ecstatic reports/code-coverage", - "start": "npm run build && node rpg.js", + "start": "npm run compile && node rpg.js", "test": "nyc mocha tests" }, "repository": { @@ -53,7 +55,7 @@ "nyc": "^13.1.0", "source-map": "^0.7.3", "webpack": "^4.27.1", - "webpack-cli": "^3.1.2", + "webpack-cli": "^3.3.2", "webpack-dev-server": "^3.1.10" }, "nyc": { diff --git a/rpg.js b/rpg.js index 3386596..bd78f18 100644 --- a/rpg.js +++ b/rpg.js @@ -4,12 +4,13 @@ const config = require('./config'); const RPG = require('./types/rpg'); const Gateway = require('./services/rpg'); -const Server = require('@fabric/http'); +const Server = require('@fabric/http/types/server'); async function main () { let gateway = new Gateway(); let server = new Server(config); let rpg = new RPG({ + name: '@rpg/core', path: './stores/rpg' }); @@ -29,8 +30,7 @@ async function main () { // our original expectations. This could be fast, say 60 "ticks" per second, // or slow, like IdleRPG's 10-minute rounds. rpg.on('tick', async function (id) { - let state = await rpg._GET(`/blobs/${id}`); - console.log(`got tick, ${id}:`, state); + console.log(`got tick, ${id}`); }); // Generic message handler. diff --git a/scripts/agents.js b/scripts/agents.js index 45e52e5..92deaed 100644 --- a/scripts/agents.js +++ b/scripts/agents.js @@ -2,23 +2,34 @@ // Fabric Core & HTTP (for the Legacy Web) const Fabric = require('@fabric/core'); -const HTTP = require('@fabric/http'); +// const Server = require('@fabric/http/types/server'); // internal types -const Agent = require('./types/agent'); +const Agent = require('../types/agent'); async function main () { let api = new Fabric.Remote({ host: 'api.roleplaygateway.com' }); let mobs = await api._GET('/mobs'); - let server = new HTTP(); - let agents = mobs.map(function (mob) { - let agent = new Agent(mob); - return agent; - }); - - if (this.status && this.status === 'ready') { - await server.start(); - } + let shuttles = await api._GET('/shuttles'); + let agents = []; + + // Mobs (NPCs) + mobs.map(function (mob) { + return new Agent(Object.assign({ + type: 'Mob' + }, mob)); + }).forEach((agent) => agents.push(agent)); + + // Shuttles (Automated Vehicles) + shuttles.map(function (shuttle) { + return new Agent(Object.assign({ + type: 'Shuttle' + }, shuttle)); + }).forEach((agent) => agents.push(agent)); + + // let server = new Server(); + // server.define('Agent', Agent); + // await server.start(); console.log(`[RPG:AGENTS]`, `Controlling actions for ${agents.length} agents.`); diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..ef17bfd --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,20 @@ +'use strict'; + +const config = require('../config'); + +const SPA = require('@fabric/http/types/spa'); +const Compiler = require('@fabric/http/types/compiler'); + +async function main () { + let spa = new SPA(config); + let compiler = new Compiler({ + document: spa + }); + + compiler.compileTo('assets/index.html'); + compiler.compileTo('assets/spa.html'); + + process.exit(); +} + +main(); diff --git a/scripts/rpg.js b/scripts/rpg.js new file mode 100644 index 0000000..dce84a4 --- /dev/null +++ b/scripts/rpg.js @@ -0,0 +1,111 @@ +'use strict'; + +// Core RPG lib +const RPG = require('../types/rpg'); + +// Dependencies +const Circuit = require('@fabric/core').Circuit; +const Service = require('@fabric/core').Service; +const App = require('@fabric/http').App; + +async function main () { + window.App = App; + window.Circuit = Circuit; + window.Service = Service; + + // Core RPG Engine + window.RPG = RPG; + // window.D3GraphViz = D3GraphViz; + + window.app = new App(); + window.app.rpg = new RPG(); + + window.app.rpg.on('tick', function (tick) { + console.log('received tick from RPG:', tick); + }); + + window.app.rpg.on('message', function (msg) { + console.log('received message from RPG:', msg); + }); + + window.app.service = new Service(); + window.app.circuit = new Circuit({ + gates: [], + wires: [ + { name: 'ready', from: 'init', to: 'ready' } + ] + }); + + /* window.graph = D3GraphViz.graphviz('#svg', { + fit: true, + width: 800, + height: 600 + }).renderDot(window.app.circuit.dot); */ + + // TODO: fix verification + window.app._verifyElements(); + + // TODO: move these into App + window.app.actions = []; + window.app.bindings = []; + + window.app.circuit.on('/', async function (msg) { + console.log('[FABRIC:WEB]', 'Circuit emitted:', msg, msg['@data']); + switch (msg['@type']) { + default: + console.error('unhandled circuit message type:', msg['@type']); + break; + case 'KeyUp': + console.log('KeyUp:', msg['@data']); + break; + case 'Snapshot': + console.log('Received snapshot:', msg['@data']); + break; + } + }); + + let elements = document.querySelectorAll('*[data-bind]'); + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + let binding = element.getAttribute('data-bind'); + + console.log('binding the element:', binding, element); + + element.addEventListener('keyup', async function (event) { + if (!event.target) return false; + if (!event.target.value) return false; + await window.app.service._PUT('/source', event.target.value); + }); + + console.log('binding:', binding, element); + + window.app.circuit.on(binding, function (data) { + console.log('received replacement data (from circuit!) targeted for binding:', binding); + element.innerHTML = data; + }); + + window.app.on(binding, function (data) { + console.log('received replacement data targeted for binding:', binding); + element.innerHTML = data; + }); + + window.app.bindings.push(element); + } + + let actionables = document.querySelectorAll('*[data-action]'); + for (let i = 0; i < actionables.length; i++) { + let element = actionables[i]; + let action = element.getAttribute('data-action'); + element.addEventListener('click', async function (event) { + return window.app.circuit[action](event); + }); + window.app.actions.push(element); + } + + // start the RPG :) + await window.app.rpg.start(); + + console.log('[RPG:CORE]', 'ready!'); +} + +main(); diff --git a/types/agent.js b/types/agent.js index 1a4370d..c64fa30 100644 --- a/types/agent.js +++ b/types/agent.js @@ -16,42 +16,53 @@ class Agent { this.ticks = 0; } - async move (direction) { + async move (direction, type = 'Mob') { let agent = this; - let result = await this.remote._POST(`/movements`, { - direction: direction, - mobID: agent.id + actor: { id: agent.id, type: type }, + object: { direction }, + target: '/movements' }); - console.log('result of movement:', result); - return result; } async compute () { ++this.ticks; - console.log(`[AGENT:${this.id}]`, `beginning tick:`, this.ticks, 'mood:', this.current.mood); - - if (this.current.mood && this.current.mood === 'aggressive') { - let place = await this.getCurrentLocation(); - - if (place.active.length) { - console.log('found character!', place.active[0]); - // TODO: attack! - } else { - console.log('No nearby characters found. Moving...'); - - let exits = await this.getExits(); - let choice = this.randomFrom(exits); - - console.log('options:', exits); - console.log('choice:', choice); - - let movement = await this.move(choice.direction); - - console.log('movement:', movement); - } + console.log(`[AGENT:${this.id}]`, `beginning tick:`, this.ticks, 'current:', this.current); + + switch (this.settings.type) { + default: + console.log('Unhandled type:', this.settings.type); + break; + case 'Shuttle': + if (this.current.mode && this.current.mode === 'active') { + // let movement = await this.move('in', 'Shuttle'); + // console.log('movement:', movement); + } + break; + case 'Mob': + if (this.current.mood && this.current.mood === 'aggressive') { + let place = await this.getCurrentLocation(); + + if (place.active.length) { + console.log('found character!', place.active[0]); + // TODO: attack! + } else { + console.log('No nearby characters found. Moving...'); + + let exits = await this.getExits(); + let choice = this.randomFrom(exits); + + console.log('options:', exits); + console.log('choice:', choice); + + let movement = await this.move(choice.direction, this.type); + + console.log('movement:', movement); + } + } + break; } } @@ -66,8 +77,19 @@ class Agent { } async getState () { - let mob = await this.remote._GET(`/mobs/${this.id}`); - return mob; + let state = null; + switch (this.settings.type) { + default: + console.log('unhandled type:', this.settings.type); + break; + case 'Shuttle': + state = await this.remote._GET(`/shuttles/${this.id}`); + break; + case 'Mob': + state = await this.remote._GET(`/mobs/${this.id}`); + break; + } + return state; } async getCurrentLocation () { diff --git a/types/player.js b/types/player.js index 14c6080..dce3769 100644 --- a/types/player.js +++ b/types/player.js @@ -1,18 +1,45 @@ 'use strict'; const Fabric = require('@fabric/core'); +const Entity = require('./entity'); -class Player { +class Player extends Entity { /** * In-game representation of our humble hero, the {@link User}. * @param {Object} [settings={}] Settings to pass from the player's will. * @return {Player} Player-controllable instance of themselves. */ constructor (settings = {}) { - this.settings = Object.assign({}, settings); + super(settings); + + this.settings = Object.assign({ + created: Date.now() + }, settings); + this.position = null; + this.presence = 'offline'; + this.name = null; + this.key = new Fabric.Key(); this.id = this.key.address; + + return this; + } + + async _revealToWorld () { + await this._presenceToReady(); + return this; + } + + async _presenceFromReady () { + this.presence = 'online'; + this.commit(); + return this; + } + + async _setName (name) { + this.name = name; + this.commit(); return this; } diff --git a/types/rpg.js b/types/rpg.js index 0b0fca9..fd8371d 100644 --- a/types/rpg.js +++ b/types/rpg.js @@ -1,5 +1,8 @@ +// # `@rpg/core` +// Peer-to-peer game engine, powered by Fabric. 'use strict'; +// Constants const { TICK_INTERVAL, GENESIS_HASH @@ -8,6 +11,11 @@ const { // Fabric Core const Fabric = require('@fabric/core'); +// Dependencies +const BN = require('bn.js'); +const One = new BN('1'); +const Zero = new BN('0'); + // ### Internal Types // Here we've created a few internal classes to keep IdleRPG well-organized. const Encounter = require('./encounter'); @@ -17,6 +25,7 @@ const Player = require('./player'); /** * Primary RPG builder. + * @property {State} state Holds state for the game. */ class RPG extends Fabric { /** @@ -27,15 +36,14 @@ class RPG extends Fabric { constructor (configuration) { super(configuration); + // Begin @internal typing this['@type'] = 'RPG'; this['@configuration'] = Object.assign({ name: 'RPG', path: './stores/rpg', authority: 'api.roleplaygateway.com', persistent: true, - globals: { - tick: 0 - }, + globals: { tick: 0 }, canvas: { height: 300, width: 400 @@ -57,10 +65,14 @@ class RPG extends Fabric { this['@world'] = new World(this['@configuration']['entropy']); this['@player'] = new Player(); this['@genesis'] = this['@configuration'].genesis || GENESIS_HASH; + this['@entity'] = Object.assign({ + clock: 0, + entropy: this['@configuration']['entropy'] + }, this.state); + + // old vestiges of times long since past this['@data'] = Object.assign({ - globals: { - tick: 0 - } + globals: { tick: 0 } }, this['@configuration']); this.timer = null; @@ -79,18 +91,27 @@ class RPG extends Fabric { return Encounter; } + /** + * Increment the {@link Machine} by one clock cycle. + * @param {Boolean} [notify=true] When `true` will emit {@link Meessage} events. + * @return {Promise} Resolves with {@link State} `@id` (for recordkeeping). + */ async tick (notify = true) { console.log('[RPG]', 'Beginning tick...', Date.now()); console.log('[RPG]', 'STATE (@entity)', this['@entity']); let origin = new Fabric.State(this['@entity']); + let observer = new Fabric.Observer(origin['@data']); // Our first and primary order of business is to update the clock. Once // we've computed the game state for the next round, we can share it with // the world. // // Let's finish our work up front. - this['@entity'].clock++; + this['@entity'].clock = One.add(new BN(this['@entity'].clock)).toString(); + this['@entity'].entropy = this.machine.sip(); + + console.log('[ENTROPY:CHECK]', 'clock:', this['@entity'].clock, 'entropy:', this['@entity'].entropy); // let commit = this.commit(); // console.log('tick:', commit); @@ -224,7 +245,19 @@ class RPG extends Fabric { async dump () { let id = await this.save(); - require('fs').writeSync(`inputs/${id}`, JSON.stringify(this.state)); + let dump = JSON.stringify(this.state); + console.log('memory dump:', dump); + return dump; + } + + /** + * Compute the game state. + * @param {Mixed} [input=null] Player input, if any. + * @return {Promise} Resolves with result. + */ + async compute (input = null) { + let object = new Fabric.State(input); + return object.id; } async save () { @@ -232,7 +265,7 @@ class RPG extends Fabric { let data = JSON.stringify(this.state); let state = new Fabric.State(data); - console.log('[RPG]', 'attempting to save:', data); + console.log('[RPG]', 'saving:', data); try { let memory = await this._PUT('/memories', data); @@ -267,6 +300,8 @@ class RPG extends Fabric { console.error('Could not load restore:', E); } + this.commit(); + return data; } @@ -274,14 +309,12 @@ class RPG extends Fabric { await super.start(); // TODO: clean up the clock - this.state.clock = this['@configuration'].globals.tick; - this.state.entropy = {}; - this.state.players = {}; - this.state.peers = {}; + this.state.clock = Zero; await this.restore(); + // TODO: import latest from Gateway // await this._syncFromGateway(); - await this['@world'].start(); + // await this['@world'].start(); // TODO: document the process this.timer = setInterval(this.tick.bind(this), this['@configuration'].interval); diff --git a/types/world.js b/types/world.js index 15d0860..274c5b9 100644 --- a/types/world.js +++ b/types/world.js @@ -1,5 +1,6 @@ 'use strict'; +const config = require('../config'); const Fabric = require('@fabric/core'); const Map = require('./map'); @@ -17,8 +18,11 @@ class World { * @return {World} Instance of the World. */ constructor (settings = {}) { - this.settings = Object.assign({}, settings); - this.map = new Map(settings); + this.settings = Object.assign({ + seed: 1, + entropy: config.entropy + }, settings); + this.map = new Map(this.settings); this.agent = new Player(); this.origin = new Point(); this.status = null; diff --git a/webpack.config.js b/webpack.config.js index fd649b9..948ec4e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,17 +1,26 @@ // # Fabric Webpack Config +// This file configures `webpack`, a build tool for JavaScript. +// +// We always enable JavaScript's "strict" mode first. +'use strict'; + +const webpack = require('webpack'); const path = require('path'); module.exports = { - entry: './components/index.js', mode: 'development', - devtool: 'source-map', + entry: './scripts/rpg.js', target: 'web', - devServer: { - contentBase: './assets', - watchContentBase: true - }, output: { - filename: 'index.js', - path: path.resolve(__dirname, 'assets') - } + path: path.resolve(__dirname, 'assets/scripts'), + filename: 'index.min.js' + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('production'), + APP_ENV: JSON.stringify('browser') + } + }) + ] }; From 686056314d693fdee38445d57574557195dff52d Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Sat, 1 Jun 2019 12:30:09 -0700 Subject: [PATCH 03/36] Import assets, resource definitions --- README.md | 5 + assets/index.html | 279 ++++++++++++++++++++++++++++++------- assets/styles/screen.css | 42 ++++++ components/pixel-editor.js | 15 ++ config.json | 30 ++-- constants.js | 2 +- inputs/weapons.js | 12 ++ inputs/weapons/staff.json | 6 + package.json | 4 +- scripts/rpg.js | 65 +++++++-- types/player.js | 12 +- types/rpg.js | 84 +++++++++-- types/world.js | 3 + 13 files changed, 466 insertions(+), 93 deletions(-) create mode 100644 assets/styles/screen.css create mode 100644 components/pixel-editor.js create mode 100644 inputs/weapons.js create mode 100644 inputs/weapons/staff.json diff --git a/README.md b/README.md index d5ac8fb..fcc39ed 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,8 @@ yarn link @fabric/http # Good luck, have fun! # ~ E ``` + +### API +See output of `npm run docs` for an HTTP-serving URL with well-organized +documentation. All source code is included, so feel free to submit proposals +for potential changes. diff --git a/assets/index.html b/assets/index.html index 224881f..ed0dbde 100644 --- a/assets/index.html +++ b/assets/index.html @@ -1,56 +1,229 @@ - - - - RPG - - - -
-

RPG: the many worlds

-

Use `[spacebar]` to activate the currently-selected ability. Arrow keys to move. This is only a demo!

- -

ERA: dev

-

Source: fabric:github.com/FabricLabs/rpg -

-
- - -
+ + + + Peer-to-peer game engine, built with Fabric. · @rpg/core + + + + + + + + + + +

@rpg/core

+

Peer-to-peer game engine, built with Fabric.

+ + +
+

Version: undefined

+

Clock: undefined

+

Source: fabric:github.com/FabricLabs/web +

+
+ +

Settings

+ { + "name": "@rpg/core", + "synopsis": "Peer-to-peer game engine, built with Fabric.", + "handle": "html", + "language": "en", + "components": {}, + "offline": false, + "authority": "chat.roleplaygateway.com:9999", + "entropy": "6170cc426cbe663cef4342dc73860f2ab26c0c0e411c390ca064208bada65e6e", + "provide": [ + "rpg" + ], + "secure": true, + "height": 256, + "width": 256, + "depth": 256, + "path": "./stores/chat.roleplaygateway.com", + "seed": "alpha beta kappa gamma delta omega", + "resources": { + "Character": { + "name": "Character", + "components": { + "list": "rpg-character-list", + "view": "rpg-character-view" + } + }, + "Map": { + "name": "Map", + "components": { + "list": "rpg-map-list", + "view": "rpg-map-view" + } + }, + "Tile": { + "name": "Tile", + "components": { + "list": "rpg-tile-list", + "view": "rpg-tile-view" + } + }, + "Peer": { + "name": "Peer", + "components": { + "list": "rpg-peer-list", + "view": "rpg-peer-view" + } + }, + "Place": { + "name": "Place", + "components": { + "list": "rpg-place-list", + "view": "rpg-place-view" + } + }, + "Player": { + "name": "Player", + "author": "Fabric Labs", + "components": { + "list": "rpg-player-list", + "view": "rpg-player-view" + }, + "description": "Basic user component.", + "site": "https://fabric.pub/" + } + } +} +

Resources

+ undefinedundefinedundefinedundefinedundefinedundefined +

Circuit

+ + digraph "fsm" { + "init"; +} + + + + + + + + + + + + + + + + + +

State a31357775f1f5e11bb231b3b388b0f3490b3fc9b3385a8a95d04085ff19c4115

+
{
+  "name": "@rpg/core",
+  "synopsis": "Peer-to-peer game engine, built with Fabric.",
+  "authority": "chat.roleplaygateway.com:9999",
+  "entropy": "6170cc426cbe663cef4342dc73860f2ab26c0c0e411c390ca064208bada65e6e",
+  "provide": [
+    "rpg"
+  ],
+  "secure": true,
+  "height": 256,
+  "width": 256,
+  "depth": 256,
+  "path": "./stores/chat.roleplaygateway.com",
+  "seed": "alpha beta kappa gamma delta omega",
+  "resources": {
+    "Character": {
+      "name": "Character",
+      "components": {
+        "list": "rpg-character-list",
+        "view": "rpg-character-view"
+      }
+    },
+    "Map": {
+      "name": "Map",
+      "components": {
+        "list": "rpg-map-list",
+        "view": "rpg-map-view"
+      }
+    },
+    "Tile": {
+      "name": "Tile",
+      "components": {
+        "list": "rpg-tile-list",
+        "view": "rpg-tile-view"
+      }
+    },
+    "Peer": {
+      "name": "Peer",
+      "components": {
+        "list": "rpg-peer-list",
+        "view": "rpg-peer-view"
+      }
+    },
+    "Place": {
+      "name": "Place",
+      "components": {
+        "list": "rpg-place-list",
+        "view": "rpg-place-view"
+      }
+    },
+    "Player": {
+      "name": "Player",
+      "author": "Fabric Labs",
+      "components": {
+        "list": "rpg-player-list",
+        "view": "rpg-player-view"
+      },
+      "description": "Basic user component.",
+      "site": "https://fabric.pub/"
+    }
+  }
+}
+
+ + + + + + {"name":"BlankPage","types":{},"blobs":{},"states":{},"addresses":{},"collections":{},"tips":{}} + + + + + + + + + + + +
- - - + > # RPG `@fabric/rpg` + > ## STOP HERE AND READ ME FIRST! + > Before continuing, let us be the first to welcome you to the Source. While it + > might be confusing at first, there's a lot you can learn if you make the time. + > Use this URI: + > > https://www.roleplaygateway.com/ + > From there, links like `hub.roleplaygateway.com` might "pop up" from time to + > time. With a bit of navigating around, you can earn credit for your progress. + > Continue: + > > https://chat.roleplaygateway.com/ + > Offline: + > > https://www.roleplaygateway.com/medals/beta-tester + > Remember: never be afraid to explore! Curiosity might have killed the cat, but + > that's why he had nine lives. + > Good luck, have fun (`gl;hf o/`), and enjoy! + > — the RPG team + --> + +
+ \ No newline at end of file diff --git a/assets/styles/screen.css b/assets/styles/screen.css new file mode 100644 index 0000000..5f6750c --- /dev/null +++ b/assets/styles/screen.css @@ -0,0 +1,42 @@ +* { + margin: 0; + padding: 0; +} + +fabric-grid-row { + display: block; +} + +fabric-svg { + width: 800px; + height: 600px; +} + +textarea { + display: block; + width: 100%; + height: 10em; +} + +.bordered { + border: 1px solid black; +} + +# Semantic Overrides +.ui.menu .item img.logo { + margin-right: 1.5em; +} + +#details, +.main.container { + margin-top: 7em; +} + +.wireframe { + margin-top: 2em; +} + +.ui.footer.segment { + margin: 5em 0em 0em; + padding: 5em 0em; +} diff --git a/components/pixel-editor.js b/components/pixel-editor.js new file mode 100644 index 0000000..ce9c2a7 --- /dev/null +++ b/components/pixel-editor.js @@ -0,0 +1,15 @@ +'use strict'; + +const PixelEditor = require('pixel-editor'); + +class PixelEditorComponent { + constructor (settings = {}) { + this.settings = Object.assign({ + handle: 'rpg-pixel-editor' + }, settings); + this.editor = new PixelEditor(); + return this; + } +} + +module.exports = PixelEditorComponent; diff --git a/config.json b/config.json index 1515143..25589c5 100644 --- a/config.json +++ b/config.json @@ -12,40 +12,46 @@ "seed": "alpha beta kappa gamma delta omega", "resources": { "Character": { + "name": "Character", "components": { - "list": "/characters", - "view": "/characters/:id" + "list": "rpg-character-list", + "view": "rpg-character-view" } }, "Map": { + "name": "Map", "components": { - "list": "/maps", - "view": "/maps/:id" + "list": "rpg-map-list", + "view": "rpg-map-view" } }, "Tile": { + "name": "Tile", "components": { - "list": "/tiles", - "view": "/tiles/:id" + "list": "rpg-tile-list", + "view": "rpg-tile-view" } }, "Peer": { + "name": "Peer", "components": { - "list": "/peers", - "view": "/peers/:id" + "list": "rpg-peer-list", + "view": "rpg-peer-view" } }, "Place": { + "name": "Place", "components": { - "list": "/places", - "view": "/places/:id" + "list": "rpg-place-list", + "view": "rpg-place-view" } }, "Player": { + "name": "Player", "author": "Fabric Labs", "components": { - "list": "/players", - "view": "/players/:id" + "list": "rpg-player-list", + "view": "rpg-player-view" }, "description": "Basic user component.", "site": "https://fabric.pub/" diff --git a/constants.js b/constants.js index 07e8d22..80bebc6 100644 --- a/constants.js +++ b/constants.js @@ -3,7 +3,7 @@ // prime constants const GENESIS_HASH = 'f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef'; const HTTP_HOST = 'api.roleplaygateway.com'; -const TICK_INTERVAL = 1000; +const TICK_INTERVAL = 10 * 60 * 1000; // 10 minutes * 60 seconds * 1000 millis const FRAMES_PER_BLOCK = 60 * 60 * 10 * 1000; const HEADER_LENGTH = 64; diff --git a/inputs/weapons.js b/inputs/weapons.js new file mode 100644 index 0000000..e252369 --- /dev/null +++ b/inputs/weapons.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = [ + 'axe', + 'cutlass', + 'dagger', + 'halberd', + 'hammer', + 'rapier', + 'staff', + 'sword' +].map((name) => require(`./weapons/${name}`)); diff --git a/inputs/weapons/staff.json b/inputs/weapons/staff.json new file mode 100644 index 0000000..742b098 --- /dev/null +++ b/inputs/weapons/staff.json @@ -0,0 +1,6 @@ +{ + "name": "staff", + "attack": 5, + "speed": 2, + "range": 4 +} diff --git a/package.json b/package.json index 6ef47b0..7c5580e 100644 --- a/package.json +++ b/package.json @@ -39,12 +39,14 @@ "@fabric/core": "FabricLabs/fabric#pruning", "@fabric/http": "FabricLabs/web#fabric-0.1.0", "body-parser": "^1.18.3", + "canvas": "^2.4.1", "dotparser": "^0.4.0", "express": "^4.16.4", "express-session": "^1.15.6", - "level-browserify": "^2.0.0", + "level": "^5.0.1", "peer": "^0.2.10", "peerjs": "^0.3.18", + "pixel-editor": "^0.2.0", "ws": "^6.1.2" }, "devDependencies": { diff --git a/scripts/rpg.js b/scripts/rpg.js index dce84a4..189b733 100644 --- a/scripts/rpg.js +++ b/scripts/rpg.js @@ -1,24 +1,27 @@ 'use strict'; +const { TICK_INTERVAL } = require('../constants'); +const config = require('../config'); + // Core RPG lib const RPG = require('../types/rpg'); +const SPA = require('@fabric/http/types/spa'); // Dependencies -const Circuit = require('@fabric/core').Circuit; -const Service = require('@fabric/core').Service; -const App = require('@fabric/http').App; +const Circuit = require('@fabric/core/lib/circuit'); +const Service = require('@fabric/core/lib/service'); async function main () { - window.App = App; - window.Circuit = Circuit; + // type definitions + window.App = SPA; window.Service = Service; // Core RPG Engine window.RPG = RPG; // window.D3GraphViz = D3GraphViz; - window.app = new App(); - window.app.rpg = new RPG(); + window.app = new SPA(config); + window.app.rpg = window.rpg = new RPG(config); window.app.rpg.on('tick', function (tick) { console.log('received tick from RPG:', tick); @@ -26,14 +29,27 @@ async function main () { window.app.rpg.on('message', function (msg) { console.log('received message from RPG:', msg); + switch (msg['@type']) { + default: + console.error('Unhandled message type from RPG:', msg['@type']); + break; + case 'Transaction': + window.app.rpg._applyChanges(msg['@data'].changes); + break; + } }); window.app.service = new Service(); window.app.circuit = new Circuit({ gates: [], - wires: [ - { name: 'ready', from: 'init', to: 'ready' } - ] + loops: [ { name: 'tick', interval: TICK_INTERVAL } ], + wires: [ { name: 'ready', from: 'init', to: 'ready' } ] + }); + + window.app.circuit._registerMethod('_generateIdentity', async function () { + console.log('todo: generate here!'); + let identity = await window.app.rpg._createIdentity(); + console.log('identity created:', identity); }); /* window.graph = D3GraphViz.graphviz('#svg', { @@ -49,6 +65,11 @@ async function main () { window.app.actions = []; window.app.bindings = []; + // TODO: bind service stuff + window.app.service.on('source', function (source) { + console.log('[SCRIPTS:RPG]', 'service emitted source:', source); + }); + window.app.circuit.on('/', async function (msg) { console.log('[FABRIC:WEB]', 'Circuit emitted:', msg, msg['@data']); switch (msg['@type']) { @@ -64,13 +85,15 @@ async function main () { } }); + // TODO: bind updates from `@fabric/http` class let elements = document.querySelectorAll('*[data-bind]'); + + // iterates over elements which claim binding, use sha256(path) as target for (let i = 0; i < elements.length; i++) { let element = elements[i]; let binding = element.getAttribute('data-bind'); - console.log('binding the element:', binding, element); - + // listen for changes from rendered element element.addEventListener('keyup', async function (event) { if (!event.target) return false; if (!event.target.value) return false; @@ -97,11 +120,27 @@ async function main () { let element = actionables[i]; let action = element.getAttribute('data-action'); element.addEventListener('click', async function (event) { - return window.app.circuit[action](event); + console.log('click event on actionable element:', action, event); + let method = window.app.circuit.methods[action]; + if (!method) console.warn('NO METHOD ON CIRCUIT:', method); + let circuit = new Circuit({ + gates: [ action ] + }); + console.log('new circuit:', circuit); + console.log('method:', method); + + let result = await method.call(window.app); + + console.log('result:', result); + + return result; }); window.app.actions.push(element); } + // wires up router / push state / history + window.app.handler(); + // start the RPG :) await window.app.rpg.start(); diff --git a/types/player.js b/types/player.js index dce3769..20dde4f 100644 --- a/types/player.js +++ b/types/player.js @@ -1,6 +1,7 @@ 'use strict'; const Fabric = require('@fabric/core'); +const Avatar = require('@fabric/http/types/avatar'); const Entity = require('./entity'); class Player extends Entity { @@ -16,9 +17,14 @@ class Player extends Entity { created: Date.now() }, settings); + this.name = null; this.position = null; this.presence = 'offline'; - this.name = null; + this.avatar = new Avatar({ + height: 256, + width: 256, + alpha: null + }); this.key = new Fabric.Key(); this.id = this.key.address; @@ -27,7 +33,11 @@ class Player extends Entity { } async _revealToWorld () { + await this.avatar._drawAvatar(); + await this.avatar.render(); await this._presenceToReady(); + console.log('rendered avatar:', this.avatar.render()); + console.log('rendered avatar toDataURI:', this.avatar.toDataURI()); return this; } diff --git a/types/rpg.js b/types/rpg.js index fd8371d..f0138b8 100644 --- a/types/rpg.js +++ b/types/rpg.js @@ -18,7 +18,9 @@ const Zero = new BN('0'); // ### Internal Types // Here we've created a few internal classes to keep IdleRPG well-organized. +const Avatar = require('@fabric/http/types/avatar'); const Encounter = require('./encounter'); +// const Modulator = require('./modulator'); const Entity = require('./entity'); const World = require('./world'); const Player = require('./player'); @@ -27,7 +29,7 @@ const Player = require('./player'); * Primary RPG builder. * @property {State} state Holds state for the game. */ -class RPG extends Fabric { +class RPG extends Fabric.Service { /** * Build an RPG with the {@link Fabric} tools. * @param {Object} configuration Settings to configure the RPG with. @@ -45,8 +47,8 @@ class RPG extends Fabric { persistent: true, globals: { tick: 0 }, canvas: { - height: 300, - width: 400 + height: 256, + width: 256 }, interval: TICK_INTERVAL }, configuration); @@ -56,15 +58,19 @@ class RPG extends Fabric { // We use human-friendly names and keep things as small as possible, so do // your part in keeping this well-maintained! this.state = { + identities: {}, // shared identities (public) channels: {}, // stores a list of channels. players: {}, // players are users... ! services: {}, // services are networks - users: {} // users are network clients + users: {}, // users are network clients, + tip: null }; - this['@world'] = new World(this['@configuration']['entropy']); + this['@avatar'] = new Avatar({ seed: this['@configuration']['entropy'] }); + this['@world'] = new World({ seed: this['@configuration']['entropy'] }); this['@player'] = new Player(); this['@genesis'] = this['@configuration'].genesis || GENESIS_HASH; + // this['@modulator'] = new Modulator(); this['@entity'] = Object.assign({ clock: 0, entropy: this['@configuration']['entropy'] @@ -76,6 +82,8 @@ class RPG extends Fabric { }, this['@configuration']); this.timer = null; + this.avatar = this['@avatar']; + this.machine = new Fabric.Machine(); this.remote = new Fabric.Remote({ host: this['@configuration'].authority }); @@ -91,6 +99,11 @@ class RPG extends Fabric { return Encounter; } + get identities () { + // TODO: ensure all private data remains private (prove!) + return this.state.identities; + } + /** * Increment the {@link Machine} by one clock cycle. * @param {Boolean} [notify=true] When `true` will emit {@link Meessage} events. @@ -142,8 +155,23 @@ class RPG extends Fabric { } async _POST (path, data) { - let id = await super._POST(path, data); - let obj = await super._GET(id); + console.log('posting:', path, data); + let result = null; + + try { + let id = await super._POST(path, data); + console.log('path (id?) from post:', id); + result = id; + } catch (E) { + console.log('RPG COULD NOT CREATE:', E); + } + + try { + let obj = await this._GET(result); + console.log('posted:', result, path, data); + } catch (E) { + console.error('RPG COULD NOT POST:', path, data, E); + } // assign state // this.state['players'][obj.id] = obj; @@ -151,7 +179,7 @@ class RPG extends Fabric { // commit this.commit(); - return id; + return result; } /* async _registerActor (data) { @@ -176,6 +204,37 @@ class RPG extends Fabric { return result; } */ + async _createIdentity () { + let item = null; + let result = null; + + // TODO: async generation + let key = new Fabric.Key(); + let struct = { + name: prompt('What shall be your name?'), + address: key.address, + private: key.private.toString('hex'), + public: key.public + }; + + try { + item = await this._POST(`/identities`, struct); + console.log('create identity posted:', item); + result = await this._GET(item); + } catch (E) { + console.error('broken:', E); + } + + this.identities[struct.address] = struct; + this.identity = struct; + + // TODO: remove public key from character, use only address (or direct hash) + return { + address: struct.address, + public: struct.public + }; + } + async _registerPlayer (data) { let result = null; let state = new Fabric.State(data); @@ -268,8 +327,9 @@ class RPG extends Fabric { console.log('[RPG]', 'saving:', data); try { - let memory = await this._PUT('/memories', data); - let doc = await this._PUT(`/blobs/${state.id}`, state.render()); + // let memory = await this._PUT('/memories', data); + // let doc = await this.store.set(`/memories/${state.id}`, state.render()); + let doc = await this.store.set(`/tip`, state.render()); result = state.id; } catch (E) { this.error('cannot save:', E); @@ -283,9 +343,9 @@ class RPG extends Fabric { let data = null; try { - blob = await this._GET(`/memories`); + blob = await this._GET(`/tip`); } catch (E) { - console.error('Could not GET', '/memories'); + console.error('Could not GET old state', E); } console.log('[RPG]', 'attempting to restore:', blob); diff --git a/types/world.js b/types/world.js index 274c5b9..5168e8c 100644 --- a/types/world.js +++ b/types/world.js @@ -22,11 +22,14 @@ class World { seed: 1, entropy: config.entropy }, settings); + this.map = new Map(this.settings); this.agent = new Player(); this.origin = new Point(); + this.status = null; this.timer = null; + return this; } From 04886e8e867232107ef0fdfb47456e564c5bbe0a Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Sat, 1 Jun 2019 13:05:01 -0700 Subject: [PATCH 04/36] Update docs --- docs/Entity.html | 10 +- docs/Map.html | 2 +- docs/Player.html | 4 +- docs/RPG.html | 418 +++++++++++++++++++++++++++++++++++++++++++- docs/Sprite.html | 254 +++++++++++++++++++++++++++ docs/Word.html | 2 +- docs/World.html | 6 +- docs/entity.js.html | 5 +- docs/index.html | 35 +++- docs/map.js.html | 2 +- docs/player.js.html | 43 ++++- docs/rpg.js.html | 333 +++++++++++++++++++++++++++++++---- docs/sprite.js.html | 103 +++++++++++ docs/word.js.html | 2 +- docs/world.js.html | 13 +- 15 files changed, 1173 insertions(+), 59 deletions(-) create mode 100644 docs/Sprite.html create mode 100644 docs/sprite.js.html diff --git a/docs/Entity.html b/docs/Entity.html index 5d622ff..5977f28 100644 --- a/docs/Entity.html +++ b/docs/Entity.html @@ -161,7 +161,7 @@
Parameters:
Source:
@@ -291,7 +291,7 @@

toJSONSource:
@@ -401,7 +401,7 @@

toObjectSource:
@@ -511,7 +511,7 @@

toRawSource:
@@ -577,7 +577,7 @@
Returns:
- +