@aethervtt/shard-project 0.0.1
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/define.d.ts +55 -0
- package/dist/define.js +56 -0
- package/dist/docs.d.ts +26 -0
- package/dist/docs.js +1336 -0
- package/dist/host.d.ts +47 -0
- package/dist/host.js +118 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +61 -0
- package/dist/manifest.d.ts +49 -0
- package/dist/manifest.js +147 -0
- package/dist/reload.d.ts +53 -0
- package/dist/reload.js +247 -0
- package/dist/session.d.ts +73 -0
- package/dist/session.js +144 -0
- package/dist/sourcemap.d.ts +31 -0
- package/dist/sourcemap.js +91 -0
- package/dist/templates.d.ts +9 -0
- package/dist/templates.js +283 -0
- package/package.json +55 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
const json = (value) => `${JSON.stringify(value, null, 2)}
|
|
2
|
+
`;
|
|
3
|
+
function manifest(name) {
|
|
4
|
+
return {
|
|
5
|
+
$schema: "./.shard/schemas/shard.schema.json",
|
|
6
|
+
name,
|
|
7
|
+
version: "0.1.0",
|
|
8
|
+
engine: "0.x",
|
|
9
|
+
entry: "scripts/main.ts",
|
|
10
|
+
startScene: "scenes/main.scene.json",
|
|
11
|
+
seed: 1,
|
|
12
|
+
window: { width: 1280, height: 720, msaa: 4 },
|
|
13
|
+
plugins: ["render/forward", "input"]
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const emptyScene = {
|
|
17
|
+
$schema: "../.shard/schemas/scene.schema.json",
|
|
18
|
+
version: 1,
|
|
19
|
+
assets: { red: { type: "Material", value: { baseColor: "#c0392b", roughness: 0.4 } } },
|
|
20
|
+
resources: { "render/AmbientLight": { color: [1, 1, 1], brightness: 500 } },
|
|
21
|
+
entities: [
|
|
22
|
+
{
|
|
23
|
+
name: "sun",
|
|
24
|
+
components: {
|
|
25
|
+
"render/DirectionalLight": { illuminance: "daylight" },
|
|
26
|
+
"core/Transform": { rotationEuler: [-50, 30, 0] }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "camera",
|
|
31
|
+
components: {
|
|
32
|
+
"render/Camera3d": { fovY: 60 },
|
|
33
|
+
"core/Transform": { translation: [0, 2, 6], rotationEuler: [-15, 0, 0] }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "cube",
|
|
38
|
+
components: {
|
|
39
|
+
"render/Mesh3d": { mesh: { path: "procedural:cube?size=1.5" } },
|
|
40
|
+
"render/MeshMaterial": { material: { path: "#red" } },
|
|
41
|
+
"core/Transform": { translation: [0, 0.5, 0] }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
const explorerScene = (name) => ({
|
|
47
|
+
$schema: "../.shard/schemas/scene.schema.json",
|
|
48
|
+
version: 1,
|
|
49
|
+
assets: {
|
|
50
|
+
rock: { type: "Material", value: { baseColor: "#7a5c48", roughness: 0.95 } },
|
|
51
|
+
ice: { type: "Material", value: { baseColor: "#c9d6df", roughness: 0.6 } },
|
|
52
|
+
hull: { type: "Material", value: { baseColor: "#d35400", roughness: 0.35 } },
|
|
53
|
+
glass: {
|
|
54
|
+
type: "Material",
|
|
55
|
+
value: { baseColor: "#1b2631", roughness: 0.1, emissive: "#5dade2", emissiveLuminance: 400 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
resources: { "render/AmbientLight": { color: [0.6, 0.7, 1], brightness: 300 } },
|
|
59
|
+
entities: [
|
|
60
|
+
{
|
|
61
|
+
name: "sun",
|
|
62
|
+
components: {
|
|
63
|
+
"render/DirectionalLight": { illuminance: "daylight" },
|
|
64
|
+
"core/Transform": { rotationEuler: [-25, 50, 0] }
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "planet",
|
|
69
|
+
components: {
|
|
70
|
+
"render/Mesh3d": { mesh: { path: "procedural:sphere?radius=40&segments=64" } },
|
|
71
|
+
"render/MeshMaterial": { material: { path: "#rock" } },
|
|
72
|
+
"core/Transform": { translation: [0, -20, -140] }
|
|
73
|
+
},
|
|
74
|
+
children: [
|
|
75
|
+
{
|
|
76
|
+
name: "moon",
|
|
77
|
+
components: {
|
|
78
|
+
"render/Mesh3d": { mesh: { path: "procedural:sphere?radius=6&segments=32" } },
|
|
79
|
+
"render/MeshMaterial": { material: { path: "#ice" } },
|
|
80
|
+
"core/Transform": { translation: [70, 35, 20] }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "ship",
|
|
87
|
+
components: {
|
|
88
|
+
[`${name}/Ship`]: {},
|
|
89
|
+
"render/Mesh3d": { mesh: { path: "procedural:box?x=1.6&y=0.5&z=3.2" } },
|
|
90
|
+
"render/MeshMaterial": { material: { path: "#hull" } },
|
|
91
|
+
"core/Transform": {}
|
|
92
|
+
},
|
|
93
|
+
children: [
|
|
94
|
+
{
|
|
95
|
+
name: "cockpit",
|
|
96
|
+
components: {
|
|
97
|
+
"render/Mesh3d": { mesh: { path: "procedural:sphere?radius=0.45" } },
|
|
98
|
+
"render/MeshMaterial": { material: { path: "#glass" } },
|
|
99
|
+
"core/Transform": { translation: [0, 0.3, -0.6] }
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "camera",
|
|
104
|
+
components: {
|
|
105
|
+
"render/Camera3d": { fovY: 65 },
|
|
106
|
+
"core/Transform": { translation: [0, 2.2, 8], rotationEuler: [-10, 0, 0] }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
const explorerMain = (name) => `import { defineSystem, FixedUpdate, quat, t, vec3 } from '@aethervtt/shard-core'
|
|
114
|
+
import { addActions, defineActions } from '@aethervtt/shard-input'
|
|
115
|
+
import { defineProject } from '@aethervtt/shard-project'
|
|
116
|
+
import { FixedTime } from '@aethervtt/shard-runtime'
|
|
117
|
+
import { Transform } from '@aethervtt/shard-transform'
|
|
118
|
+
|
|
119
|
+
/** Flight controls. Bindings are data: an agent (or player) can remap them without code changes. */
|
|
120
|
+
export const Controls = defineActions('${name}/Controls', {
|
|
121
|
+
thrust: { kind: 'button', bindings: ['Key:KeyW', 'Key:Space', 'Gamepad:RightTrigger'] },
|
|
122
|
+
steer: { kind: 'axis2d', bindings: [{ composite: 'arrows' }, 'Gamepad:LeftStick'], deadZone: 0.15 },
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const project = defineProject({
|
|
126
|
+
name: '${name}',
|
|
127
|
+
build(app) {
|
|
128
|
+
addActions(app.world, Controls)
|
|
129
|
+
app.addSystems(FixedUpdate, fly)
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
export const Ship = project.component(
|
|
134
|
+
'Ship',
|
|
135
|
+
{
|
|
136
|
+
speed: t.f32({ min: 0, unit: 'm/s', description: 'Current forward speed.' }),
|
|
137
|
+
maxSpeed: t.f32({ default: 40, min: 0, unit: 'm/s', description: 'Speed cap.' }),
|
|
138
|
+
acceleration: t.f32({ default: 15, min: 0, unit: 'm/s\xB2', description: 'Thrust acceleration.' }),
|
|
139
|
+
drag: t.f32({ default: 0.4, min: 0, description: 'Fraction of speed lost per second without thrust.' }),
|
|
140
|
+
turnRate: t.f32({ default: 1.2, min: 0, unit: 'rad/s', description: 'Steering speed.' }),
|
|
141
|
+
},
|
|
142
|
+
{ description: 'A ship the player flies: steer with arrows or the left stick, thrust with W.' },
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
const spin = quat.create()
|
|
146
|
+
const forward = vec3.create()
|
|
147
|
+
|
|
148
|
+
/** Steers and moves every ship along its -Z axis. */
|
|
149
|
+
const fly = defineSystem({
|
|
150
|
+
name: '${name}/fly',
|
|
151
|
+
setup: (world) => ({ ships: world.query({ with: [Ship, Transform] }) }),
|
|
152
|
+
run: ({ ships }, world) => {
|
|
153
|
+
const dt = world.resource(FixedTime).step
|
|
154
|
+
const controls = world.resource(Controls.resource)
|
|
155
|
+
const [yaw, pitch] = controls.axis2d('steer')
|
|
156
|
+
const thrust = controls.pressed('thrust')
|
|
157
|
+
for (const table of ships.tables) {
|
|
158
|
+
const speed = table.column(Ship, 'speed')
|
|
159
|
+
const maxSpeed = table.column(Ship, 'maxSpeed')
|
|
160
|
+
const acceleration = table.column(Ship, 'acceleration')
|
|
161
|
+
const drag = table.column(Ship, 'drag')
|
|
162
|
+
const turnRate = table.column(Ship, 'turnRate')
|
|
163
|
+
const translation = table.column(Transform, 'translation')
|
|
164
|
+
const rotation = table.column(Transform, 'rotation')
|
|
165
|
+
for (let i = 0; i < table.count; i++) {
|
|
166
|
+
const s = thrust
|
|
167
|
+
? Math.min(maxSpeed[i]!, speed[i]! + acceleration[i]! * dt)
|
|
168
|
+
: speed[i]! * Math.max(0, 1 - drag[i]! * dt)
|
|
169
|
+
speed[i] = s
|
|
170
|
+
const r = rotation.subarray(i * 4, i * 4 + 4)
|
|
171
|
+
quat.fromEuler(spin, pitch * turnRate[i]! * dt, -yaw * turnRate[i]! * dt, 0)
|
|
172
|
+
quat.normalize(r, quat.multiply(r, r, spin))
|
|
173
|
+
vec3.transformQuat(forward, [0, 0, -1], r)
|
|
174
|
+
translation[i * 3] = translation[i * 3]! + forward[0]! * s * dt
|
|
175
|
+
translation[i * 3 + 1] = translation[i * 3 + 1]! + forward[1]! * s * dt
|
|
176
|
+
translation[i * 3 + 2] = translation[i * 3 + 2]! + forward[2]! * s * dt
|
|
177
|
+
}
|
|
178
|
+
table.markChanged(Ship)
|
|
179
|
+
table.markChanged(Transform)
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
export default project
|
|
185
|
+
`;
|
|
186
|
+
const emptyMain = (name) => `import { defineProject } from '@aethervtt/shard-project'
|
|
187
|
+
|
|
188
|
+
const project = defineProject({
|
|
189
|
+
name: '${name}',
|
|
190
|
+
build(_app) {
|
|
191
|
+
// Register systems here: app.addSystems(Update, mySystem)
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
export default project
|
|
196
|
+
`;
|
|
197
|
+
const explorerTest = (name) => `import { expect, test } from '@aethervtt/shard-testing'
|
|
198
|
+
|
|
199
|
+
test('holding thrust flies the ship forward', async ({ game }) => {
|
|
200
|
+
await game.load('scenes/main.scene.json')
|
|
201
|
+
game.input.hold('${name}/Controls.thrust')
|
|
202
|
+
await game.step(120)
|
|
203
|
+
const ship = game.get('ship', '${name}/Ship')
|
|
204
|
+
expect(ship.speed).toBeGreaterThan(10)
|
|
205
|
+
expect(game.get('ship', 'core/Transform').translation[2]).toBeLessThan(-10)
|
|
206
|
+
await game.screenshot('thrust.png')
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test('the ship coasts to a stop without thrust', async ({ game }) => {
|
|
210
|
+
await game.load('scenes/main.scene.json')
|
|
211
|
+
await game.step(60)
|
|
212
|
+
expect(game.get('ship', '${name}/Ship').speed).toBe(0)
|
|
213
|
+
})
|
|
214
|
+
`;
|
|
215
|
+
const emptyTest = `import { expect, test } from '@aethervtt/shard-testing'
|
|
216
|
+
|
|
217
|
+
test('the start scene loads', async ({ game }) => {
|
|
218
|
+
await game.load('scenes/main.scene.json')
|
|
219
|
+
await game.step(10)
|
|
220
|
+
expect(game.get('cube', 'core/Transform').translation[1]).toBe(0.5)
|
|
221
|
+
})
|
|
222
|
+
`;
|
|
223
|
+
function projectTemplate(options) {
|
|
224
|
+
const { name, template, engineSpec } = options;
|
|
225
|
+
const deps = Object.fromEntries(
|
|
226
|
+
[
|
|
227
|
+
"@aethervtt/shard-cli",
|
|
228
|
+
"@aethervtt/shard-core",
|
|
229
|
+
"@aethervtt/shard-input",
|
|
230
|
+
"@aethervtt/shard-project",
|
|
231
|
+
"@aethervtt/shard-runtime",
|
|
232
|
+
"@aethervtt/shard-testing",
|
|
233
|
+
"@aethervtt/shard-transform"
|
|
234
|
+
].map((p) => [p, engineSpec])
|
|
235
|
+
);
|
|
236
|
+
return {
|
|
237
|
+
"shard.json": json(manifest(name)),
|
|
238
|
+
"package.json": json({
|
|
239
|
+
name,
|
|
240
|
+
private: true,
|
|
241
|
+
type: "module",
|
|
242
|
+
scripts: {
|
|
243
|
+
validate: "shard validate",
|
|
244
|
+
test: "shard test",
|
|
245
|
+
check: "shard check",
|
|
246
|
+
dev: "shard dev",
|
|
247
|
+
docs: "shard docs"
|
|
248
|
+
},
|
|
249
|
+
dependencies: deps,
|
|
250
|
+
// Engine packages ship as TypeScript source, so type checks need their ambient types.
|
|
251
|
+
devDependencies: {
|
|
252
|
+
"@types/node": "^26.6.2",
|
|
253
|
+
"@webgpu/types": "^0.1.74",
|
|
254
|
+
typescript: "^7.0.2"
|
|
255
|
+
}
|
|
256
|
+
}),
|
|
257
|
+
"scripts/main.ts": template === "explorer" ? explorerMain(name) : emptyMain(name),
|
|
258
|
+
"scenes/main.scene.json": json(template === "explorer" ? explorerScene(name) : emptyScene),
|
|
259
|
+
"tests/main.test.ts": template === "explorer" ? explorerTest(name) : emptyTest,
|
|
260
|
+
"shaders/.gitkeep": "",
|
|
261
|
+
"assets/.gitkeep": "",
|
|
262
|
+
".gitignore": ".shard/\nnode_modules/\n",
|
|
263
|
+
".mcp.json": json({
|
|
264
|
+
mcpServers: { shard: { command: "pnpm", args: ["exec", "shard", "mcp"] } }
|
|
265
|
+
}),
|
|
266
|
+
"tsconfig.json": json({
|
|
267
|
+
compilerOptions: {
|
|
268
|
+
target: "ES2023",
|
|
269
|
+
lib: ["ES2023", "DOM"],
|
|
270
|
+
module: "ESNext",
|
|
271
|
+
moduleResolution: "bundler",
|
|
272
|
+
strict: true,
|
|
273
|
+
noEmit: true,
|
|
274
|
+
skipLibCheck: true,
|
|
275
|
+
types: ["node", "@webgpu/types"]
|
|
276
|
+
},
|
|
277
|
+
include: ["scripts", "tests"]
|
|
278
|
+
})
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
export {
|
|
282
|
+
projectTemplate
|
|
283
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aethervtt/shard-project",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@aethervtt/shard-animation": "0.0.1",
|
|
20
|
+
"@aethervtt/shard-assets": "0.0.1",
|
|
21
|
+
"@aethervtt/shard-audio": "0.0.1",
|
|
22
|
+
"@aethervtt/shard-core": "0.0.1",
|
|
23
|
+
"@aethervtt/shard-gltf": "0.0.1",
|
|
24
|
+
"@aethervtt/shard-gpu": "0.0.1",
|
|
25
|
+
"@aethervtt/shard-input": "0.0.1",
|
|
26
|
+
"@aethervtt/shard-nav": "0.0.1",
|
|
27
|
+
"@aethervtt/shard-noise": "0.0.1",
|
|
28
|
+
"@aethervtt/shard-particles": "0.0.1",
|
|
29
|
+
"@aethervtt/shard-physics": "0.0.1",
|
|
30
|
+
"@aethervtt/shard-platform": "0.0.1",
|
|
31
|
+
"@aethervtt/shard-procgen": "0.0.1",
|
|
32
|
+
"@aethervtt/shard-protocol": "0.0.1",
|
|
33
|
+
"@aethervtt/shard-render": "0.0.1",
|
|
34
|
+
"@aethervtt/shard-runtime": "0.0.1",
|
|
35
|
+
"@aethervtt/shard-save": "0.0.1",
|
|
36
|
+
"@aethervtt/shard-scene": "0.0.1",
|
|
37
|
+
"@aethervtt/shard-sprite": "0.0.1",
|
|
38
|
+
"@aethervtt/shard-terrain": "0.0.1",
|
|
39
|
+
"@aethervtt/shard-text": "0.0.1",
|
|
40
|
+
"@aethervtt/shard-transform": "0.0.1",
|
|
41
|
+
"@aethervtt/shard-ui": "0.0.1"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/aethervtt/shard.git",
|
|
46
|
+
"directory": "packages/project"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
51
|
+
},
|
|
52
|
+
"shard": {
|
|
53
|
+
"buildSha": "d6c5c6aef4675575c878b02cf2c8f738b4190229"
|
|
54
|
+
}
|
|
55
|
+
}
|