openvibers/openvibe.quest Commits
Quest: Publish a creator world (games.world.published, contracts 0.194.0) (#57)
Changes
@@@ -137,6 +137,7 @@ OpenVibe.Events delivers an event when a site really does something. Quest count| `run.job.succeeded` | OpenVibe.Run | `payload.requester` when it is a user (an app's or a service's job is nobody's) || `inventory.item.equipped` | OpenVibe.Inventory | `payload.owner` || `bot.robot.online` | OpenVibe.Bot | `payload.owner`, the robot's owner (Bot names it since contracts 0.192.0) |++| `games.world.published` | OpenVibe.Games | `payload.owner`, who published a creator world for the first time (contracts 0.194.0) |**App quests** (contracts 0.193.0, [server/quests/app-quests.js](server/quests/app-quests.js)): a developer app definesquests on its own project's events with an app token (audience `openvibe.quest`, capability `quest.app_quest.manage`).@@@ -64,6 +64,8 @@ function personOf(event) {case 'inventory.item.equipped': // payload.owner (usr_…)case 'bot.robot.online': // payload.owner (usr_…, contracts 0.192.0; absent from older events)return isUser(p.owner) ? p.owner : null;++ case 'games.world.published': // payload.owner (identity.subject-ref@1, contracts 0.194.0)++ return refUser(p.owner);default:// An app's own event (app quests, server/quests/app-quests.js): the actor, when the app published it// on_behalf_of a person; an app acting as itself is nobody.@@@ -214,6 +214,15 @@ const QUESTS = Object.freeze([reward: { badge: { id: 'robot-pilot', name: 'Robot Pilot' }, coins: 20 },repeatable: false,},++ {++ id: 'publish-a-world',++ site: 'openvibe.games',++ title: 'Publish a creator world',++ why: 'Build a world in the map editor and publish it: anyone can join it from their browser.',++ steps: [{ event_type: 'games.world.published', count: 1, label: 'Publish one of your worlds at openvibe.games/worlds' }],++ reward: { badge: { id: 'world-builder', name: 'World Builder' }, coins: 20 },++ repeatable: false,++ },{id: 'settle-in',site: 'openvibe.network',@@@ -438,6 +447,7 @@ const EVENTS = Object.freeze([{ event_type: 'run.job.succeeded', site: 'openvibe.run', what: 'A job finished on a node; the payload names its requester (a service or app requester is ignored).' },{ event_type: 'inventory.item.equipped', site: 'inventory.openvibe.network', what: 'An item was equipped; the payload names its owner, never what else they own.' },{ event_type: 'bot.robot.online', site: 'openvibe.bot', what: 'A device connected and serves a robot; the payload names the robot\'s owner (events from before Bot named it are ignored).' },++ { event_type: 'games.world.published', site: 'openvibe.games', what: 'A player published one of their creator worlds for the first time; the payload names the owner.' },]);// The events Quest actually counts: the consumer subscribes to exactly these, and a quest may not count an event@@@ -198,6 +198,11 @@ const progressOf = async (t, subject, questId) => (await rows(t, 'SELECT step, cassert.ok(!(await done()).includes('bring-a-robot-online'), 'an event that does not name the owner credits nobody');await deliver(t, envelope('bot.robot.online', { robot_id: 'rob_1', device_id: 'dev_1', owner: sam.subject }, { source: 'bot' }));assert.ok((await done()).includes('bring-a-robot-online'));++ const world = { world: 'wk3j9x0a2bq', title: 'Moon base', url: 'https://openvibe.games/play?world=wk3j9x0a2bq', published_at: iso() };++ await deliver(t, envelope('games.world.published', { ...world, owner: { type: 'service', id: 'games' } }, { source: 'games' }));++ assert.ok(!(await done()).includes('publish-a-world'), 'a world a service published is nobody\'s');++ await deliver(t, envelope('games.world.published', { ...world, owner: { type: 'user', id: sam.subject } }, { source: 'games' }));++ assert.ok((await done()).includes('publish-a-world'));});await check('an event that names nobody creditable is ignored: no subject, a guest, a service, an unknown type', async () => {
@@ -137,6 +137,7 @@ OpenVibe.Events delivers an event when a site really does something. Quest count
@@ -137,6 +137,7 @@ OpenVibe.Events delivers an event when a site really does something. Quest count
| `run.job.succeeded` | OpenVibe.Run | `payload.requester` when it is a user (an app's or a service's job is nobody's) |
| `run.job.succeeded` | OpenVibe.Run | `payload.requester` when it is a user (an app's or a service's job is nobody's) |
| `inventory.item.equipped` | OpenVibe.Inventory | `payload.owner` |
| `inventory.item.equipped` | OpenVibe.Inventory | `payload.owner` |
| `bot.robot.online` | OpenVibe.Bot | `payload.owner`, the robot's owner (Bot names it since contracts 0.192.0) |
| `bot.robot.online` | OpenVibe.Bot | `payload.owner`, the robot's owner (Bot names it since contracts 0.192.0) |
+| `games.world.published` | OpenVibe.Games | `payload.owner`, who published a creator world for the first time (contracts 0.194.0) |
**App quests** (contracts 0.193.0, [server/quests/app-quests.js](server/quests/app-quests.js)): a developer app defines
**App quests** (contracts 0.193.0, [server/quests/app-quests.js](server/quests/app-quests.js)): a developer app defines
quests on its own project's events with an app token (audience `openvibe.quest`, capability `quest.app_quest.manage`).
quests on its own project's events with an app token (audience `openvibe.quest`, capability `quest.app_quest.manage`).
@@ -64,6 +64,8 @@ function personOf(event) {
@@ -64,6 +64,8 @@ function personOf(event) {
case 'inventory.item.equipped': // payload.owner (usr_…)
case 'inventory.item.equipped': // payload.owner (usr_…)
case 'bot.robot.online': // payload.owner (usr_…, contracts 0.192.0; absent from older events)
case 'bot.robot.online': // payload.owner (usr_…, contracts 0.192.0; absent from older events)
return isUser(p.owner) ? p.owner : null;
return isUser(p.owner) ? p.owner : null;
+ case 'games.world.published': // payload.owner (identity.subject-ref@1, contracts 0.194.0)
+ return refUser(p.owner);
default:
default:
// An app's own event (app quests, server/quests/app-quests.js): the actor, when the app published it
// An app's own event (app quests, server/quests/app-quests.js): the actor, when the app published it
// on_behalf_of a person; an app acting as itself is nobody.
// on_behalf_of a person; an app acting as itself is nobody.
@@ -214,6 +214,15 @@ const QUESTS = Object.freeze([
@@ -214,6 +214,15 @@ const QUESTS = Object.freeze([
reward: { badge: { id: 'robot-pilot', name: 'Robot Pilot' }, coins: 20 },
reward: { badge: { id: 'robot-pilot', name: 'Robot Pilot' }, coins: 20 },
repeatable: false,
repeatable: false,
},
},
+ {
+ id: 'publish-a-world',
+ site: 'openvibe.games',
+ title: 'Publish a creator world',
+ why: 'Build a world in the map editor and publish it: anyone can join it from their browser.',
+ steps: [{ event_type: 'games.world.published', count: 1, label: 'Publish one of your worlds at openvibe.games/worlds' }],
+ reward: { badge: { id: 'world-builder', name: 'World Builder' }, coins: 20 },
+ repeatable: false,
+ },
{
{
id: 'settle-in',
id: 'settle-in',
site: 'openvibe.network',
site: 'openvibe.network',
@@ -438,6 +447,7 @@ const EVENTS = Object.freeze([
@@ -438,6 +447,7 @@ const EVENTS = Object.freeze([
{ event_type: 'run.job.succeeded', site: 'openvibe.run', what: 'A job finished on a node; the payload names its requester (a service or app requester is ignored).' },
{ event_type: 'run.job.succeeded', site: 'openvibe.run', what: 'A job finished on a node; the payload names its requester (a service or app requester is ignored).' },
{ event_type: 'inventory.item.equipped', site: 'inventory.openvibe.network', what: 'An item was equipped; the payload names its owner, never what else they own.' },
{ event_type: 'inventory.item.equipped', site: 'inventory.openvibe.network', what: 'An item was equipped; the payload names its owner, never what else they own.' },
{ event_type: 'bot.robot.online', site: 'openvibe.bot', what: 'A device connected and serves a robot; the payload names the robot\'s owner (events from before Bot named it are ignored).' },
{ event_type: 'bot.robot.online', site: 'openvibe.bot', what: 'A device connected and serves a robot; the payload names the robot\'s owner (events from before Bot named it are ignored).' },
+ { event_type: 'games.world.published', site: 'openvibe.games', what: 'A player published one of their creator worlds for the first time; the payload names the owner.' },
]);
]);
// The events Quest actually counts: the consumer subscribes to exactly these, and a quest may not count an event
// The events Quest actually counts: the consumer subscribes to exactly these, and a quest may not count an event
@@ -198,6 +198,11 @@ const progressOf = async (t, subject, questId) => (await rows(t, 'SELECT step, c
@@ -198,6 +198,11 @@ const progressOf = async (t, subject, questId) => (await rows(t, 'SELECT step, c
assert.ok(!(await done()).includes('bring-a-robot-online'), 'an event that does not name the owner credits nobody');
assert.ok(!(await done()).includes('bring-a-robot-online'), 'an event that does not name the owner credits nobody');
await deliver(t, envelope('bot.robot.online', { robot_id: 'rob_1', device_id: 'dev_1', owner: sam.subject }, { source: 'bot' }));
await deliver(t, envelope('bot.robot.online', { robot_id: 'rob_1', device_id: 'dev_1', owner: sam.subject }, { source: 'bot' }));
assert.ok((await done()).includes('bring-a-robot-online'));
assert.ok((await done()).includes('bring-a-robot-online'));
+ const world = { world: 'wk3j9x0a2bq', title: 'Moon base', url: 'https://openvibe.games/play?world=wk3j9x0a2bq', published_at: iso() };
+ await deliver(t, envelope('games.world.published', { ...world, owner: { type: 'service', id: 'games' } }, { source: 'games' }));
+ assert.ok(!(await done()).includes('publish-a-world'), 'a world a service published is nobody\'s');
+ await deliver(t, envelope('games.world.published', { ...world, owner: { type: 'user', id: sam.subject } }, { source: 'games' }));
+ assert.ok((await done()).includes('publish-a-world'));
});
});
await check('an event that names nobody creditable is ignored: no subject, a guest, a service, an unknown type', async () => {
await check('an event that names nobody creditable is ignored: no subject, a guest, a service, an unknown type', async () => {