openvibers/openvibe.games CHANGELOG.md
CHANGELOG.md
| 1 | # Changelog |
| 2 | |
| 3 | All notable changes to OpenVibe.Games are recorded here. The format follows |
| 4 | [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project |
| 5 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 6 | |
| 7 | ## [Unreleased] |
| 8 | |
| 9 | ### Added |
| 10 | |
| 11 | - **Two official example creator worlds** (plan §4 Games: "creator worlds (none published yet)"; review next step 3): **Beacon Hill**, a sculpted hill with a beacon, a flagstone path, benches, a campfire and three zones, and **The Workshop**, a walled yard with a shed you can walk into, workbenches, crates, props, resource nodes and lamps. They are ordinary v2 map documents (`apps/server/src/worlds/exampleWorlds.ts`) that staff can open in `/editor`, published for the official OpenVibe account by `apps/server/src/worlds/seedExampleWorlds.ts` (`pnpm --filter @openvibe/server exec tsx scripts/seedExampleWorlds.ts`, idempotent, `--revise` updates a changed map) so `/worlds` and the home's creator-worlds section have real entries. A person's world is never touched. `exampleWorlds.test.ts`, `seedExampleWorlds.test.ts`. |
| 12 | |
| 13 | - Share: a creator world's page, a round's result page, the leaderboard and the in-game round result now open the network's one Share sheet (`data-ov-share-sheet`, openvibe-shared 4.114.0) with the page's public address. The result page now starts the shared navbar itself, which is what loads the sheet on the first press; `navigator.share` is gone. |
| 14 | |
| 15 | ### Fixed |
| 16 | |
| 17 | - **A drag at the end of the casual game's view did nothing.** The game view (`.cg-view`) is a scroll area |
| 18 | inside a page that scrolls, and it held its overscroll, so once the view reached its end a finger drag moved |
| 19 | neither the view nor the page and the footer could not be reached from the game. The view now passes the drag |
| 20 | to the page at its end (`apps/client/src/casual/casual.css`; `apps/client/src/casual/casual-scroll.test.ts` |
| 21 | swipes at the view's end and checks the page moved). |
| 22 | - **A shared /worlds link had no preview.** The creator worlds page carried no Open Graph or Twitter tags, so a |
| 23 | link posted in Discord or X showed a bare URL. It now has the same card as the home page, with its own title and |
| 24 | description. |
| 25 | - **No step said to make a pickaxe.** Scraplandia's first steps went from the Stone Axe to "Mine at the quarry", whose |
| 26 | hint only said a Stone Pickaxe breaks the deposits, and the gather step finished after a single stone. A new |
| 27 | "Craft a Stone Pickaxe" step follows the axe (2 Wood Planks and 3 Stones), and the gather hint says a log and five |
| 28 | stones make the first two tools: one pile and one log are exactly both recipes. |
| 29 | - **Townsfolk in the Salvage Rush arena.** The 90-second scrap round ran the content's seven NPC spawns, laid out for |
| 30 | the procedural world: townsfolk and a warden wandered inside the arena and Rustjaw thugs stood just past its edge. |
| 31 | A world definition can now name its own NPCs (`npcSpawns`; `[]` is none), and the arena has none. A map's own NPCs |
| 32 | still win. |
| 33 | - **Touch play's HUD was buried under the controls.** On a phone, the stick (bottom left) and the button grid (bottom |
| 34 | right) covered the first-steps panel, and the HP, food, water and stamina bars sat on the hotbar. While the touch |
| 35 | controls show: |
| 36 | - the hotbar sits above the buttons, in smaller slots; |
| 37 | - the bars sit above the stick; |
| 38 | - the waypoint sits under the OpenVibe button; |
| 39 | - the first-steps panel sits at the top right and shows only the step in progress; |
| 40 | - the debug status line hides. |
| 41 | - **An empty pile kept saying "E — gather".** When a node had nothing left (the gate piles empty fast when several new |
| 42 | scrappers arrive together), its prompt still offered to gather and E did nothing. It now says so, for example |
| 43 | "Loose Stones — picked clean, back within 2 mins" or "Oak Tree — felled, grows back within … mins". A production |
| 44 | play-test found it: a new guest walked to the north gate, and the branch pile had been emptied a minute earlier. |
| 45 | - **"City Warden — LMB attack".** Looking at a friendly warden or townsperson now shows just their name, so the prompt |
| 46 | no longer invites you to hit them; a swing still lands. Wildlife says "LMB hunt", and Rustjaw thugs keep "⚠ — LMB |
| 47 | attack". |
| 48 | - **Scrap City drew every static on its own.** It is several hundred static meshes (walls, wall plates, buildings, |
| 49 | roofs, doors, stalls), and each textured one had a material of its own because its texture's scale follows its size. |
| 50 | That came to 165 draw calls in view and 298 shadow casters, with 525 materials. |
| 51 | - Statics are now merged once built (`render/staticBatches.ts`). Each static's texture tiling is baked into its UVs |
| 52 | and its tint into its vertex colours, so everything with one texture shares one material, per 32 m square. |
| 53 | - Lamps, the fountain, statics the water reflects, painted surfaces, per-face styles and glb models stay as they |
| 54 | were. |
| 55 | - A live map save unbatches, rebuilds what changed and batches again. |
| 56 | - Lamp lights switch off by day, not just dim: at intensity 0 a lamp still cost every pixel it was included on. A |
| 57 | batch is under the lamps' size limit (60 m; the terrain is 210 m). |
| 58 | - In town: statics went from 165 draw calls to 44 and shadow casters from 439 to 178. The frame came to about 230 |
| 59 | draw calls (720 this evening), and a test browser ran at 38–44 fps instead of 8–13. |
| 60 | - **Far avatars floated, twisted.** The merged copy that stands in for an avatar past 18 m baked each part's world |
| 61 | position, but a Babylon clone keeps its source's parent, so the joint chain applied a second time: far NPCs hung |
| 62 | 0.4 m up, out of shape. Each copy now drops its parent before baking, and a test checks that the copy stands on its |
| 63 | feet at a person's height. |
| 64 | - **NPCs cost 150 draw calls in town.** Each avatar is about 14 meshes, so eleven townsfolk and wardens in view drew |
| 65 | 150 times a frame. Past 18 m an avatar, NPC or remote player, now draws as one merged copy of itself in its rest |
| 66 | pose and skips the animation work. It switches back to the full rig inside 16 m. In town, NPC draws went from 150 |
| 67 | to 4. |
| 68 | - **Shadows halved the frame rate in town.** The shadow pass drew every caster in Scrap City again each frame (about |
| 69 | 390 more draw calls, ~720 a frame), and a test browser dropped from ~50 to ~10 fps. The shadow map is now redrawn |
| 70 | every third frame, and at once whenever the shadow box moves, so no shadow jumps. That gives ~490 draw calls and |
| 71 | about twice the frame rate in the same browser. |
| 72 | - **Lamps blacked out the world.** A material uses its first 8 lights, and the town's lamp lights (off by day) were |
| 73 | created before the sun and sky. A map with a dozen lamps rendered every surface black. The sun and sky now have the |
| 74 | higher render priority. Each lamp lights only the geometry in its range, and its light goes with its post, so a live |
| 75 | map save no longer leaks lights. |
| 76 | - **Terrain paint never showed.** The layered surface sampled the paint mask at the base texture's tiled UV. On any |
| 77 | terrain (base tiling half its extent), the mask read its clamped edge, so one layer covered the whole terrain. The |
| 78 | mask now spans the surface once, and each layer tiles by its own count. |
| 79 | - **Nights lit from under the ground.** After sunset the sun's light kept the set sun's direction, pointing up out of |
| 80 | the ground, so the ground got only sky light and nights were brown-black. The light is now the moon's, shining down |
| 81 | from the moon, a little stronger than the old night light. |
| 82 | - **The first-person hand.** The right fist stood upright in front of a held tool, a flat skin-coloured block in the |
| 83 | corner of the screen, and the gun's grip sat below the screen's edge. Now the fist closes on each tool's grip, the |
| 84 | gun's grip or low on an axe or pickaxe handle (the head leaning away), and the forearm runs out of the lower-right |
| 85 | corner. The arm is lit a little from within, like the tool, so it reads at night. |
| 86 | - **The status line under the OpenVibe button.** In the world, the tick, fps and entity line sat under the folded |
| 87 | Frame's button in the same corner. It now sits below it. |
| 88 | |
| 89 | ### Added |
| 90 | |
| 91 | - **Salvage Rush's arena looks like a scrapyard pit.** A plated wall runs round the edge with a lamp on each corner |
| 92 | (they light after dark). Scrap heaps sit against the wall and crates and drums in the far corners, all clear of the |
| 93 | eight scrap spots and the new spawn pad. It was a bare floor with four coloured boxes. |
| 94 | - **A waypoint for the first steps.** An arrow and a distance at the top of the screen point toward the current |
| 95 | step's target: "Oak Tree · 30 m". The target is the nearest node in sight that has something left (a starter pile, |
| 96 | preferring whichever gives what you still lack; an oak; a stone deposit; a scrap pile), else a landmark. The haul step |
| 97 | points at the recovery bird's announced landing. "The forest is west of town" was all a new scrapper had to find a |
| 98 | forest over 100 m away. |
| 99 | - Landmarks are map zones with the wilds' own rules, so they change no gameplay: The Forest, The Quarry and The |
| 100 | Scrapyard on Scrap Island. They can be moved in the editor. |
| 101 | - **The town's props look like things.** Merchant stalls have a counter, posts, a striped awning in the stall's colour |
| 102 | and goods on top. Crates have edge boards, barrels have hoops and a bung, and the storage box has a lid and a latch. |
| 103 | The workbench has legs, a shelf, a vise and a saw, the still has a dome and a pipe, planters grow, and the campfire |
| 104 | is logs in a ring of stones under an unlit flame. Every prop was its collision shape in one flat colour (the plaza's |
| 105 | stalls were purple boxes). Each look is one vertex-coloured mesh, so a prop is still one draw call. |
| 106 | - **NPCs look like people.** Wardens, townsfolk and Rustjaw thugs wear the players' own low-poly avatar, so they |
| 107 | walk, turn and flinch like players. Each is dressed by its archetype (wardens in denim or slate, Rustjaw in rust |
| 108 | with beards and buzz cuts, townsfolk in everyday colours) and varied by its id. Dust hares are small faceted hares. |
| 109 | Every NPC was a tinted box with a box head. |
| 110 | - **NPCs in the map, and 26 on Scrap Island.** The v2 map document has `npcs` (an archetype and a ground position), |
| 111 | and the editor's Entity tool places them: City warden, Townsfolk, Dust hare and Rustjaw thug. A map with NPCs |
| 112 | replaces the content's seven spawns, which were laid out for the procedural world and stood Rustjaw thugs just |
| 113 | outside Scrap City's wall. The server matches the NPCs to the map's spawns at boot and on every live save: a spawn |
| 114 | placed, moved or deleted in the editor shows up at once, and an NPC no spawn claims goes, saved row and all. Scrap |
| 115 | Island now has: |
| 116 | - a warden at each gate and one by the spawn square; |
| 117 | - six townsfolk on the avenues and the ring road; |
| 118 | - nine hares at the farm and in the fields; |
| 119 | - six Rustjaw thugs at the far end of the scrapyard, in the deep forest and on Rust Ridge, all beyond a leash's |
| 120 | reach of a gate. |
| 121 | |
| 122 | Creator worlds still run no NPCs, and a creator map with NPC spawns says so. |
| 123 | - **Something flies off a hit.** Wood chips off a tree or branches, grit off a rock, sparks off scrap, leaves off a |
| 124 | bush. Before, a swing changed nothing on screen until the server moved a count. Nothing flies toward the camera, and a |
| 125 | point-blank hit throws smaller pieces. Two thin-instanced meshes draw every particle. |
| 126 | - **Sound.** Scraplandia had none. Every sound is synthesised in the browser, so there are no files to load: |
| 127 | - footsteps on grass, stone and water; |
| 128 | - a jump and a landing as hard as the fall; |
| 129 | - a swing's whoosh; |
| 130 | - the hit: a thunk on wood, a clack on stone, a ring on scrap, a thud on people; |
| 131 | - a blip when something goes into the pack, and a chime on a level-up; |
| 132 | - wind that gusts and rises in a storm, rain, birds by day and crickets at night. |
| 133 | |
| 134 | Under water everything is muffled. Settings has a volume slider (saved; 0 is off). Audio starts on the first click |
| 135 | or key, as browsers require. |
| 136 | - **First steps.** A new scrapper gets a checklist in the corner: |
| 137 | - gather branches and stones (piles just inside every city gate); |
| 138 | - craft a Stone Axe; |
| 139 | - chop an oak in the west forest; |
| 140 | - mine at the quarry; |
| 141 | - salvage the scrapyard; |
| 142 | - extract a haul. |
| 143 | |
| 144 | Each step says where to go. Progress only moves forward and is kept in the browser, and the list can be closed. |
| 145 | - **Scrap Island, Scraplandia's world (2026-10-19).** Production ran on an empty map document, so a new player |
| 146 | spawned over open water and drowned within seconds. `apps/server/src/maps/scrapIsland.ts` generates the island as an |
| 147 | ordinary v2 map document, the file the editor saves, so staff keep editing it in `/editor`. It is deterministic. |
| 148 | - **Scrap City**, walled and safe: the plaza and fountain, four market stalls, a crafting corner, avenues and a ring |
| 149 | road, houses, tenements and shacks, lamps and four watchtowers. |
| 150 | - **The wilds**: the scrapyard (container stacks, a crane, wrecks, scrap piles), the forest (about 200 oaks, berries |
| 151 | and branches), the terraced quarry, Rust Ridge and its radio mast, the lake and jetty, the farm, the shipwreck |
| 152 | beach, and ruins along the dirt roads. |
| 153 | - The terrain is one 420 m heightfield painted grass, rock and sand. |
| 154 | - Scraplandia's world id is now `scrap_island`: resources re-seed and characters move to the new spawn. |
| 155 | - Write it with `scripts/writeScrapIsland.ts` (docs/games.md). |
| 156 | |
| 157 | - **KTX2 surface textures (M6).** The built-in surface textures ship as KTX2 (Basis ETC1S with mipmaps), beside their |
| 158 | JPEGs: |
| 159 | - the GPU keeps them compressed, about a seventh of the memory of a decoded JPEG, which matters most on the low tier; |
| 160 | - they decode in workers instead of on the main thread; |
| 161 | - they are a quarter smaller (1.64 MB against 2.17 MB). |
| 162 | |
| 163 | Babylon's decoder is vendored same-origin in `/ktx2/` (`apps/client/scripts/vendorKtx2.sh`, following |
| 164 | `@babylonjs/core`), so nothing loads from Babylon's CDN. `apps/client/scripts/encodeKtx2.sh` makes the files with |
| 165 | basisu, flipped vertically, because Babylon does not flip compressed data the way it flips a JPEG. This was checked |
| 166 | pixel by pixel in Chrome. |
| 167 | |
| 168 | The JPEG is used when WebAssembly or workers are missing, with `?ktx2=0`, with `openvibe.ktx2=off`, or when a KTX2 |
| 169 | fails to load. The server answers `.ktx2` as `image/ktx2`. |
| 170 | |
| 171 | - **The casual line and Vibe Heroes (plan T22).** `packages/casual` is a kit for small phone-first games: data |
| 172 | packs, a server-fought auto-battle, shards and fusing, levels and star-ups, gear, energy, an idle chest with |
| 173 | offline earnings, daily and weekly quests, a login calendar, earned-only summons with published odds and a |
| 174 | visible guarantee, and a gem store whose products list exactly what they give. Vibe Heroes (20 original |
| 175 | heroes, 100 stages in 10 chapters) plays at `/play/vibe-heroes` as a guest, and a sign-in on the same browser |
| 176 | takes the save into the account. Every currency or item change is a ledger line written in the same |
| 177 | transaction as the state, under the client's idempotency key (migration 0007). The page loads under 30 KB of |
| 178 | gzip JS, has its own share card, and keeps an ad link's `src`/`c` with the first visit. |
| 179 | |
| 180 | - **Scraplandia "Haul" mode.** Recovery-bird extractions now score haul points: |
| 181 | each secured valuable scores `count × valuable.points`. Valuable item definitions |
| 182 | carry per-item points (`salvage_core` 20, `pepper_tonic` 30, |
| 183 | `blueprint_scrap_pistol` 40, `blueprint_still` 60), defaulting to 10. |
| 184 | - **Scraplandia haul leaderboard.** `scraplandia` joins the leaderboard, ranked by |
| 185 | summed haul points across all time and this week. The leaderboard page gains a |
| 186 | Salvage Rush / Scraplandia haul switch. |
| 187 | - **Haul achievements.** Scraplandia gains First haul, Big haul (≥ 500 in one |
| 188 | extraction) and Haul week (≥ 2,000 in one week), evaluated server-side after each |
| 189 | recorded haul and shown in the in-game Achievements panel. |
| 190 |