@jgengine/sql 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -3
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/sqlPersistence.js +21 -18
- package/package.json +4 -4
- package/skills/jgengine/SKILL.md +51 -0
- package/skills/jgengine/api.md +434 -0
- package/skills/jgengine/capabilities.md +29 -0
- package/skills/jgengine/reference-cartridge.md +72 -0
- package/skills/jgengine-multiplayer/SKILL.md +36 -0
- package/skills/jgengine-multiplayer/api.md +611 -0
- package/skills/jgengine-multiplayer/capabilities.md +9 -0
- package/skills/jgengine-multiplayer/reference.md +69 -0
- package/llms.txt +0 -877
package/README.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# @jgengine/sql
|
|
2
2
|
|
|
3
|
-
Postgres persistence for the [JGengine](https://github.com/Noisemaker111/jgengine) host: `HostPersistence` over a structural pool interface (no hard `pg` dependency).
|
|
3
|
+
Postgres persistence for the [JGengine](https://github.com/Noisemaker111/jgengine) host: `HostPersistence` over a structural pool interface (no hard `pg` dependency). Apache-2.0.
|
package/dist/sqlPersistence.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clampLimit,
|
|
1
|
+
import { clampLimit, FEED_RING_LIMIT } from "@jgengine/core/runtime/hostPersistence";
|
|
2
2
|
import { applyRunReset } from "@jgengine/core/runtime/persistenceScope";
|
|
3
3
|
const SCHEMA_STATEMENTS = [
|
|
4
4
|
`CREATE TABLE IF NOT EXISTS jg_game_servers (
|
|
@@ -34,13 +34,15 @@ const SCHEMA_STATEMENTS = [
|
|
|
34
34
|
PRIMARY KEY (game_id, scope, stat, server_id, user_id)
|
|
35
35
|
)`,
|
|
36
36
|
`CREATE INDEX IF NOT EXISTS jg_leaderboard_top ON jg_leaderboard_rows (game_id, scope, stat, value)`,
|
|
37
|
-
`CREATE TABLE IF NOT EXISTS
|
|
37
|
+
`CREATE TABLE IF NOT EXISTS jg_feed_entries (
|
|
38
38
|
server_id text NOT NULL,
|
|
39
39
|
action text NOT NULL,
|
|
40
|
+
seq bigserial NOT NULL,
|
|
41
|
+
entry jsonb NOT NULL,
|
|
40
42
|
updated_at bigint NOT NULL,
|
|
41
|
-
|
|
42
|
-
PRIMARY KEY (server_id, action)
|
|
43
|
+
PRIMARY KEY (server_id, action, seq)
|
|
43
44
|
)`,
|
|
45
|
+
`CREATE INDEX IF NOT EXISTS jg_feed_entries_order ON jg_feed_entries (server_id, action, seq)`,
|
|
44
46
|
];
|
|
45
47
|
export async function ensureSchema(pool) {
|
|
46
48
|
for (const statement of SCHEMA_STATEMENTS) {
|
|
@@ -127,7 +129,7 @@ export function sqlPersistence(pool, now = Date.now) {
|
|
|
127
129
|
await client.query(`DELETE FROM jg_world_chunks WHERE server_id = $1`, [reset.serverId]);
|
|
128
130
|
}
|
|
129
131
|
if (reset.wipeServerSession) {
|
|
130
|
-
await client.query(`DELETE FROM
|
|
132
|
+
await client.query(`DELETE FROM jg_feed_entries WHERE server_id = $1`, [reset.serverId]);
|
|
131
133
|
await client.query(`DELETE FROM jg_leaderboard_rows WHERE game_id = $1 AND server_id = $2`, [
|
|
132
134
|
reset.gameId,
|
|
133
135
|
reset.serverId,
|
|
@@ -143,7 +145,7 @@ export function sqlPersistence(pool, now = Date.now) {
|
|
|
143
145
|
await client.query(`DELETE FROM jg_world_chunks WHERE server_id = $1`, [serverId]);
|
|
144
146
|
}
|
|
145
147
|
if (reset.wipeServerSession) {
|
|
146
|
-
await client.query(`DELETE FROM
|
|
148
|
+
await client.query(`DELETE FROM jg_feed_entries WHERE server_id = $1`, [serverId]);
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
if (reset.wipeServerSession) {
|
|
@@ -208,22 +210,23 @@ export function sqlPersistence(pool, now = Date.now) {
|
|
|
208
210
|
]);
|
|
209
211
|
},
|
|
210
212
|
async loadFeed({ serverId, action }) {
|
|
211
|
-
const result = await pool.query(`SELECT
|
|
212
|
-
|
|
213
|
-
return row === undefined ? [] : asJson(row.entries);
|
|
213
|
+
const result = await pool.query(`SELECT entry FROM jg_feed_entries WHERE server_id = $1 AND action = $2 ORDER BY seq ASC`, [serverId, action]);
|
|
214
|
+
return result.rows.map((row) => asJson(row.entry));
|
|
214
215
|
},
|
|
215
216
|
async appendFeed({ serverId, action, entry }) {
|
|
216
217
|
let entries = [];
|
|
217
218
|
await transact(async (client) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
219
|
+
await client.query(`INSERT INTO jg_feed_entries (server_id, action, entry, updated_at) VALUES ($1, $2, $3, $4)`, [serverId, action, JSON.stringify(entry), now()]);
|
|
220
|
+
await client.query(`DELETE FROM jg_feed_entries
|
|
221
|
+
WHERE server_id = $1 AND action = $2
|
|
222
|
+
AND seq < (
|
|
223
|
+
SELECT seq FROM jg_feed_entries
|
|
224
|
+
WHERE server_id = $1 AND action = $2
|
|
225
|
+
ORDER BY seq DESC
|
|
226
|
+
LIMIT 1 OFFSET $3
|
|
227
|
+
)`, [serverId, action, FEED_RING_LIMIT - 1]);
|
|
228
|
+
const result = await client.query(`SELECT entry FROM jg_feed_entries WHERE server_id = $1 AND action = $2 ORDER BY seq ASC`, [serverId, action]);
|
|
229
|
+
entries = result.rows.map((row) => asJson(row.entry));
|
|
227
230
|
});
|
|
228
231
|
return entries;
|
|
229
232
|
},
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Postgres persistence for the JGengine host: implements HostPersistence over a structural pool interface (no hard pg dependency).",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"repository": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
15
|
"CHANGELOG.md",
|
|
16
|
-
"
|
|
16
|
+
"skills"
|
|
17
17
|
],
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"test": "bun test src"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@jgengine/core": "^0.
|
|
34
|
+
"@jgengine/core": "^0.11.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"pg-mem": "^3.0.5"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jgengine
|
|
3
|
+
description: Route game work to the smallest useful JGengine guidance set.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JGengine intake and router
|
|
7
|
+
|
|
8
|
+
Read the repository [README](../../../README.md) for package truth, commands, layering, and license. This skill decides what to load; it is not an engine manual.
|
|
9
|
+
|
|
10
|
+
## 1. Establish the target
|
|
11
|
+
|
|
12
|
+
Capture only decisions that change architecture:
|
|
13
|
+
|
|
14
|
+
- player point of view and controls
|
|
15
|
+
- world shape and authored content
|
|
16
|
+
- core loop, interaction, combat, and progression
|
|
17
|
+
- solo, local, hosted, or persistent multiplayer
|
|
18
|
+
- HUD/menu states and art direction
|
|
19
|
+
- one observable completion scenario
|
|
20
|
+
|
|
21
|
+
For a new in-repo game, scaffold with `bun run new:game <id> --name "Title"`. Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
|
|
22
|
+
|
|
23
|
+
## 2. Select domains
|
|
24
|
+
|
|
25
|
+
Load only rows the target needs. Do not preload every domain.
|
|
26
|
+
|
|
27
|
+
| Need | Skill | Owner |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| scene placement, terrain painting, paths, asset authoring | `jgengine-editor` | editor document and authoring tools |
|
|
30
|
+
| runtime world, movement, input, interaction, AI, navigation, audio | `jgengine-world` | spatial simulation and world runtime |
|
|
31
|
+
| game state, systems, inventory, crafting, quests, economy, turns | `jgengine-gameplay` | non-combat rules and state |
|
|
32
|
+
| targeting, stats, damage, effects, abilities, projectiles, loot | `jgengine-combat` | combat resolution |
|
|
33
|
+
| HUD, menus, layout, accessibility, touch presentation | `jgengine-ui` | player-facing interface |
|
|
34
|
+
| models, sprites, materials, audio files, source licensing | `jgengine-assets` | asset discovery and catalogs |
|
|
35
|
+
| authority, transport, replication, sessions, host persistence | `jgengine-multiplayer` | network topology and persistence adapters |
|
|
36
|
+
|
|
37
|
+
Scenes normally require both `jgengine-editor` for authoring and `jgengine-world` for runtime consumption. Combat and multiplayer are opt-in, not default intake.
|
|
38
|
+
|
|
39
|
+
## 3. Discover before designing
|
|
40
|
+
|
|
41
|
+
Within each selected domain:
|
|
42
|
+
|
|
43
|
+
1. Search `capabilities.md` by intent; it maps needs to imports.
|
|
44
|
+
2. Open `api.md` only when exact signatures or export inventory are needed.
|
|
45
|
+
3. Open the linked reference only for a deeper recipe or trap.
|
|
46
|
+
|
|
47
|
+
If no capability fits, identify the upstream package seam before writing game-local code. In this repository, implement a reusable primitive with its first adopter; if editor-owned content cannot be expressed, file the editor gap first.
|
|
48
|
+
|
|
49
|
+
## 4. Build and finish
|
|
50
|
+
|
|
51
|
+
Keep scene coordinates in the editor document, state serializable, randomness injected, and hot-path work bounded. Use `jgengine-verify` for evidence appropriate to the change. Inside this monorepo, `workflow` owns issue/PR completion. Visual polish work also uses the `jgengine-ui` quality reference.
|