@lagless/create 0.0.38 → 0.0.39
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 +26 -0
- package/dist/index.js +96 -16
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/templates/pixi-react/AGENTS.md +57 -27
- package/templates/pixi-react/CLAUDE.md +225 -49
- package/templates/pixi-react/README.md +16 -6
- package/templates/pixi-react/__packageName__-backend/package.json +1 -0
- package/templates/pixi-react/__packageName__-backend/src/main.ts +2 -0
- package/templates/pixi-react/__packageName__-frontend/package.json +8 -0
- package/templates/pixi-react/__packageName__-frontend/src/app/game-view/grid-background.tsx +4 -0
- package/templates/pixi-react/__packageName__-frontend/src/app/game-view/player-view.tsx +68 -0
- package/templates/pixi-react/__packageName__-frontend/src/app/game-view/runner-provider.tsx +57 -0
- package/templates/pixi-react/__packageName__-frontend/src/app/hooks/use-start-multiplayer-match.ts +5 -5
- package/templates/pixi-react/__packageName__-frontend/src/app/screens/title.screen.tsx +18 -1
- package/templates/pixi-react/__packageName__-simulation/package.json +7 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/arena.ts +12 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/schema/ecs.yaml +90 -6
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/apply-move-input.system.ts +73 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/boundary.system.ts +2 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/damping.system.ts +2 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/index.ts +8 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/integrate.system.ts +2 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/physics-step.system.ts +65 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/player-connection.system.ts +158 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/player-leave.system.ts +70 -0
- package/templates/pixi-react/__packageName__-simulation/src/lib/systems/save-prev-transform.system.ts +46 -0
- package/templates/pixi-react/docs/01-schema-and-codegen.md +244 -0
- package/templates/pixi-react/docs/02-ecs-systems.md +293 -0
- package/templates/pixi-react/docs/03-determinism.md +204 -0
- package/templates/pixi-react/docs/04-input-system.md +255 -0
- package/templates/pixi-react/docs/05-signals.md +175 -0
- package/templates/pixi-react/docs/06-rendering.md +256 -0
- package/templates/pixi-react/docs/07-multiplayer.md +277 -0
- package/templates/pixi-react/docs/08-physics2d.md +266 -0
- package/templates/pixi-react/docs/08-physics3d.md +312 -0
- package/templates/pixi-react/docs/09-recipes.md +362 -0
- package/templates/pixi-react/docs/10-common-mistakes.md +224 -0
- package/templates/pixi-react/docs/api-quick-reference.md +254 -0
- package/templates/pixi-react/package.json +6 -0
- /package/templates/pixi-react/__packageName__-backend/{tsconfig.json → tsconfig.json.ejs} +0 -0
- /package/templates/pixi-react/__packageName__-frontend/{tsconfig.json → tsconfig.json.ejs} +0 -0
- /package/templates/pixi-react/__packageName__-frontend/{vite.config.ts → vite.config.ts.ejs} +0 -0
- /package/templates/pixi-react/__packageName__-simulation/{.swcrc → .swcrc.ejs} +0 -0
- /package/templates/pixi-react/__packageName__-simulation/{tsconfig.json → tsconfig.json.ejs} +0 -0
- /package/templates/pixi-react/{tsconfig.base.json → tsconfig.base.json.ejs} +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# API Quick Reference
|
|
2
|
+
|
|
3
|
+
## Entity Management
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Create entity
|
|
7
|
+
const entity = entities.createEntity();
|
|
8
|
+
|
|
9
|
+
// Remove entity (recycled to LIFO stack)
|
|
10
|
+
entities.removeEntity(entity);
|
|
11
|
+
|
|
12
|
+
// Add component to entity
|
|
13
|
+
entities.addComponent(entity, MyComponent);
|
|
14
|
+
|
|
15
|
+
// Remove component from entity
|
|
16
|
+
entities.removeComponent(entity, MyComponent);
|
|
17
|
+
|
|
18
|
+
// Check if entity has component
|
|
19
|
+
entities.hasComponent(entity, MyComponent); // boolean
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Component Access
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// Hot path — typed array access (fastest, use in system loops)
|
|
26
|
+
component.unsafe.fieldName[entity] // read
|
|
27
|
+
component.unsafe.fieldName[entity] = value; // write
|
|
28
|
+
|
|
29
|
+
// Convenient — cursor (single entity, creates object)
|
|
30
|
+
const cursor = component.getCursor(entity);
|
|
31
|
+
cursor.fieldName; // read
|
|
32
|
+
cursor.fieldName = value; // write
|
|
33
|
+
|
|
34
|
+
// Bulk set — multiple fields at once
|
|
35
|
+
component.set(entity, { fieldA: 1, fieldB: 2 });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Singletons
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Direct property access
|
|
42
|
+
singleton.fieldName // read
|
|
43
|
+
singleton.fieldName = value // write
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Player Resources
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Indexed by player slot (0 to maxPlayers-1)
|
|
50
|
+
playerResource.fieldName[slot] // read
|
|
51
|
+
playerResource.fieldName[slot] = value // write
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Filters
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Iterate matching entities
|
|
58
|
+
for (const entity of filter) { ... }
|
|
59
|
+
|
|
60
|
+
// Entity count
|
|
61
|
+
filter.length
|
|
62
|
+
|
|
63
|
+
// Raw entity array
|
|
64
|
+
filter.entities // number[]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Input (RPCs)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Client side — send input (in drainInputs callback)
|
|
71
|
+
addRPC(InputClass, { field1: value1, field2: value2 });
|
|
72
|
+
|
|
73
|
+
// System side — read inputs for current tick
|
|
74
|
+
const rpcs = inputProvider.collectTickRPCs(tick, InputClass);
|
|
75
|
+
for (const rpc of rpcs) {
|
|
76
|
+
rpc.meta.playerSlot // which player (255 = server)
|
|
77
|
+
rpc.meta.seq // sequence number
|
|
78
|
+
rpc.data.field1 // input data fields
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Get all RPCs at a tick (regardless of type)
|
|
82
|
+
const buffer = inputProvider.getFrameRPCBuffer(tick);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Signals
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Define
|
|
89
|
+
@ECSSignal()
|
|
90
|
+
class MySignal extends Signal<{ value: number }> {}
|
|
91
|
+
|
|
92
|
+
// Emit (in system)
|
|
93
|
+
signal.emit(tick, { value: 42 });
|
|
94
|
+
|
|
95
|
+
// Subscribe (in view)
|
|
96
|
+
signal.Predicted.subscribe(event => { ... }); // instant
|
|
97
|
+
signal.Verified.subscribe(event => { ... }); // permanent
|
|
98
|
+
signal.Cancelled.subscribe(event => { ... }); // rolled back
|
|
99
|
+
|
|
100
|
+
// Unsubscribe
|
|
101
|
+
const sub = signal.Predicted.subscribe(handler);
|
|
102
|
+
sub.unsubscribe();
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## PRNG (Deterministic Random)
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
prng.getFloat() // [0, 1)
|
|
109
|
+
prng.getRandomInt(from, to) // [from, to) exclusive upper
|
|
110
|
+
prng.getRandomIntInclusive(from, to) // [from, to] inclusive upper
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## MathOps (Deterministic Math)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { MathOps } from '@lagless/math';
|
|
117
|
+
|
|
118
|
+
await MathOps.init(); // must call before use
|
|
119
|
+
|
|
120
|
+
MathOps.sin(x)
|
|
121
|
+
MathOps.cos(x)
|
|
122
|
+
MathOps.tan(x)
|
|
123
|
+
MathOps.atan2(y, x)
|
|
124
|
+
MathOps.sqrt(x)
|
|
125
|
+
MathOps.pow(base, exp)
|
|
126
|
+
MathOps.log(x)
|
|
127
|
+
MathOps.exp(x)
|
|
128
|
+
MathOps.clamp(value, min, max)
|
|
129
|
+
|
|
130
|
+
// Safe JS Math (no MathOps needed):
|
|
131
|
+
Math.abs(x), Math.min(a,b), Math.max(a,b)
|
|
132
|
+
Math.floor(x), Math.ceil(x), Math.round(x), Math.trunc(x)
|
|
133
|
+
Math.sign(x), Math.fround(x)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## ECSConfig
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
config.maxEntities // default 1024
|
|
140
|
+
config.maxPlayers // default 4
|
|
141
|
+
config.tickRate // ticks per second (e.g., 20)
|
|
142
|
+
config.frameLength // seconds per tick (e.g., 0.05)
|
|
143
|
+
config.snapshotRate // ticks between snapshots
|
|
144
|
+
config.inputDelay // default input delay ticks
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## ECSSimulation
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
simulation.tick // current tick number
|
|
151
|
+
simulation.interpolationFactor // 0-1, for visual interpolation
|
|
152
|
+
simulation.clock.deltaTime // render frame delta time
|
|
153
|
+
simulation.mem.buffer // raw ArrayBuffer
|
|
154
|
+
|
|
155
|
+
// Hash tracking
|
|
156
|
+
simulation.enableHashTracking(interval);
|
|
157
|
+
|
|
158
|
+
// State transfer
|
|
159
|
+
simulation.applyExternalState(buffer, tick);
|
|
160
|
+
|
|
161
|
+
// Callbacks
|
|
162
|
+
simulation.onTick(callback);
|
|
163
|
+
simulation.onRollback(callback);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## ECSRunner
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
runner.start() // begin simulation
|
|
170
|
+
runner.update(deltaTime) // advance one frame
|
|
171
|
+
runner.dispose() // cleanup
|
|
172
|
+
|
|
173
|
+
runner.Simulation // ECSSimulation instance
|
|
174
|
+
runner.InputProviderInstance // input provider
|
|
175
|
+
runner.DIContainer // DI container
|
|
176
|
+
runner.Core // typed access to all ECS objects
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Prefabs
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { Prefab } from '@lagless/core';
|
|
183
|
+
|
|
184
|
+
const entity = Prefab.create(entities)
|
|
185
|
+
.with(ComponentA, { field: value })
|
|
186
|
+
.with(ComponentB, { field: value })
|
|
187
|
+
.build();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## VisualSmoother2d
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { VisualSmoother2d } from '@lagless/misc';
|
|
194
|
+
|
|
195
|
+
const smoother = new VisualSmoother2d();
|
|
196
|
+
|
|
197
|
+
const pos = smoother.update(
|
|
198
|
+
prevX, prevY, // previous tick position
|
|
199
|
+
currX, currY, // current tick position
|
|
200
|
+
interpFactor, // 0-1 interpolation factor
|
|
201
|
+
deltaTime, // render frame dt
|
|
202
|
+
);
|
|
203
|
+
// pos.x, pos.y — smoothed position
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## FilterViews (Pixi.js)
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { FilterViews, filterView } from '@lagless/pixi-react';
|
|
210
|
+
import { RunnerTicker } from '@lagless/pixi-react';
|
|
211
|
+
|
|
212
|
+
// Auto-manage entity views
|
|
213
|
+
<FilterViews filter={runner.Core.MyFilter} View={MyView} />
|
|
214
|
+
|
|
215
|
+
// Define view
|
|
216
|
+
const MyView = filterView(
|
|
217
|
+
({ entity, runner }, ref) => <pixiContainer ref={ref} />,
|
|
218
|
+
{
|
|
219
|
+
onUpdate: ({ entity, runner }, container) => { ... },
|
|
220
|
+
onDestroy: ({ entity, runner }, container) => { ... },
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Connect simulation to Pixi render loop
|
|
225
|
+
<RunnerTicker runner={runner} />
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Relay (Multiplayer)
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// Client
|
|
232
|
+
import { RelayConnection } from '@lagless/relay-client';
|
|
233
|
+
const conn = new RelayConnection({ serverUrl, scope });
|
|
234
|
+
await conn.connect();
|
|
235
|
+
|
|
236
|
+
// Server
|
|
237
|
+
import { RelayGameServer } from '@lagless/relay-game-server';
|
|
238
|
+
const server = new RelayGameServer({ port, roomType, matchmaking });
|
|
239
|
+
server.start();
|
|
240
|
+
|
|
241
|
+
// Server hooks — ctx methods
|
|
242
|
+
ctx.emitServerEvent(InputClass, data);
|
|
243
|
+
ctx.getPlayers();
|
|
244
|
+
ctx.endMatch(results);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Debug
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { DebugPanel } from '@lagless/react';
|
|
251
|
+
|
|
252
|
+
<DebugPanel runner={runner} hashVerification={true} />
|
|
253
|
+
// Toggle with F3
|
|
254
|
+
```
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": true,
|
|
3
3
|
"scripts": {
|
|
4
|
+
"dev": "concurrently -k -n backend,frontend,player -c blue,green,yellow \"pnpm dev:backend\" \"pnpm dev:frontend\" \"pnpm dev:player\"",
|
|
4
5
|
"dev:frontend": "pnpm --filter <%= packageName %>-frontend dev",
|
|
5
6
|
"dev:backend": "pnpm --filter <%= packageName %>-backend dev",
|
|
7
|
+
"dev:player": "lagless-dev-player --game-url http://localhost:<%= frontendPort %> --server-url ws://localhost:<%= serverPort %> --scope <%= packageName %>",
|
|
6
8
|
"codegen": "npx @lagless/codegen -c <%= packageName %>-simulation/src/lib/schema/ecs.yaml"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@lagless/dev-player": "^<%= laglessVersion %>",
|
|
12
|
+
"concurrently": "^9.1.0"
|
|
7
13
|
}
|
|
8
14
|
}
|
|
File without changes
|
|
File without changes
|
/package/templates/pixi-react/__packageName__-frontend/{vite.config.ts → vite.config.ts.ejs}
RENAMED
|
File without changes
|
|
File without changes
|
/package/templates/pixi-react/__packageName__-simulation/{tsconfig.json → tsconfig.json.ejs}
RENAMED
|
File without changes
|
|
File without changes
|