@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
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
export const <%= projectName %>Arena = {
|
|
3
|
+
width: 40,
|
|
4
|
+
depth: 30,
|
|
5
|
+
playerRadius: 0.5,
|
|
6
|
+
moveSpeed: 5.0,
|
|
7
|
+
hashReportInterval: 120,
|
|
8
|
+
} as const;
|
|
9
|
+
<% } else { -%>
|
|
1
10
|
export const <%= projectName %>Arena = {
|
|
2
11
|
width: 800,
|
|
3
12
|
height: 600,
|
|
4
13
|
playerRadius: 20,
|
|
5
14
|
moveSpeed: 3.0,
|
|
15
|
+
<% if (simulationType === 'raw') { -%>
|
|
6
16
|
damping: 0.85,
|
|
17
|
+
<% } -%>
|
|
7
18
|
hashReportInterval: 120,
|
|
8
19
|
} as const;
|
|
20
|
+
<% } -%>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (simulationType === 'raw') { -%>
|
|
1
2
|
# Run codegen: npx @lagless/codegen -c <%= packageName %>-simulation/src/lib/schema/ecs.yaml
|
|
2
3
|
projectName: <%= projectName %>
|
|
3
4
|
|
|
@@ -43,10 +44,93 @@ inputs:
|
|
|
43
44
|
|
|
44
45
|
filters:
|
|
45
46
|
PlayerFilter:
|
|
46
|
-
include:
|
|
47
|
-
- Transform2d
|
|
48
|
-
- PlayerBody
|
|
47
|
+
include: [Transform2d, PlayerBody]
|
|
49
48
|
MovingFilter:
|
|
50
|
-
include:
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
include: [Transform2d, Velocity2d]
|
|
50
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
51
|
+
# Run codegen: npx @lagless/codegen -c <%= packageName %>-simulation/src/lib/schema/ecs.yaml
|
|
52
|
+
projectName: <%= projectName %>
|
|
53
|
+
simulationType: physics2d
|
|
54
|
+
|
|
55
|
+
# Transform2d (positionX/Y, rotation, prevPositionX/Y, prevRotation) and PhysicsRefs
|
|
56
|
+
# are auto-prepended by codegen when simulationType is physics2d.
|
|
57
|
+
|
|
58
|
+
components:
|
|
59
|
+
PlayerBody:
|
|
60
|
+
playerSlot: uint8
|
|
61
|
+
radius: float32
|
|
62
|
+
|
|
63
|
+
singletons:
|
|
64
|
+
GameState:
|
|
65
|
+
gamePhase: uint8
|
|
66
|
+
|
|
67
|
+
playerResources:
|
|
68
|
+
PlayerResource:
|
|
69
|
+
id: uint8[16]
|
|
70
|
+
entity: uint32
|
|
71
|
+
connected: uint8
|
|
72
|
+
lastReportedHash: uint32
|
|
73
|
+
lastReportedHashTick: uint32
|
|
74
|
+
hashMismatchCount: uint16
|
|
75
|
+
|
|
76
|
+
inputs:
|
|
77
|
+
PlayerJoined:
|
|
78
|
+
slot: uint8
|
|
79
|
+
playerId: uint8[16]
|
|
80
|
+
PlayerLeft:
|
|
81
|
+
slot: uint8
|
|
82
|
+
reason: uint8
|
|
83
|
+
MoveInput:
|
|
84
|
+
directionX: float32
|
|
85
|
+
directionY: float32
|
|
86
|
+
ReportHash:
|
|
87
|
+
hash: uint32
|
|
88
|
+
atTick: uint32
|
|
89
|
+
|
|
90
|
+
filters:
|
|
91
|
+
PlayerFilter:
|
|
92
|
+
include: [Transform2d, PhysicsRefs, PlayerBody]
|
|
93
|
+
<% } else { -%>
|
|
94
|
+
# Run codegen: npx @lagless/codegen -c <%= packageName %>-simulation/src/lib/schema/ecs.yaml
|
|
95
|
+
projectName: <%= projectName %>
|
|
96
|
+
simulationType: physics3d
|
|
97
|
+
|
|
98
|
+
# Transform3d (positionX/Y/Z, rotationX/Y/Z/W, prev*) and PhysicsRefs
|
|
99
|
+
# are auto-prepended by codegen when simulationType is physics3d.
|
|
100
|
+
|
|
101
|
+
components:
|
|
102
|
+
PlayerBody:
|
|
103
|
+
playerSlot: uint8
|
|
104
|
+
radius: float32
|
|
105
|
+
|
|
106
|
+
singletons:
|
|
107
|
+
GameState:
|
|
108
|
+
gamePhase: uint8
|
|
109
|
+
|
|
110
|
+
playerResources:
|
|
111
|
+
PlayerResource:
|
|
112
|
+
id: uint8[16]
|
|
113
|
+
entity: uint32
|
|
114
|
+
connected: uint8
|
|
115
|
+
lastReportedHash: uint32
|
|
116
|
+
lastReportedHashTick: uint32
|
|
117
|
+
hashMismatchCount: uint16
|
|
118
|
+
|
|
119
|
+
inputs:
|
|
120
|
+
PlayerJoined:
|
|
121
|
+
slot: uint8
|
|
122
|
+
playerId: uint8[16]
|
|
123
|
+
PlayerLeft:
|
|
124
|
+
slot: uint8
|
|
125
|
+
reason: uint8
|
|
126
|
+
MoveInput:
|
|
127
|
+
directionX: float32
|
|
128
|
+
directionY: float32
|
|
129
|
+
ReportHash:
|
|
130
|
+
hash: uint32
|
|
131
|
+
atTick: uint32
|
|
132
|
+
|
|
133
|
+
filters:
|
|
134
|
+
PlayerFilter:
|
|
135
|
+
include: [Transform3d, PhysicsRefs, PlayerBody]
|
|
136
|
+
<% } -%>
|
package/templates/pixi-react/__packageName__-simulation/src/lib/systems/apply-move-input.system.ts
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
import { ECSSystem, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
3
|
+
import { MoveInput, PlayerResource } from '../schema/code-gen/index.js';
|
|
4
|
+
import { PhysicsWorldManager3d } from '@lagless/physics3d';
|
|
5
|
+
import { PhysicsRefs } from '../schema/code-gen/index.js';
|
|
6
|
+
import { <%= projectName %>Arena } from '../arena.js';
|
|
7
|
+
|
|
8
|
+
const finite = (v: number): number => Number.isFinite(v) ? v : 0;
|
|
9
|
+
|
|
10
|
+
@ECSSystem()
|
|
11
|
+
export class ApplyMoveInputSystem implements IECSSystem {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly _InputProvider: InputProvider,
|
|
14
|
+
private readonly _PlayerResources: PlayerResources,
|
|
15
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
16
|
+
private readonly _WorldManager: PhysicsWorldManager3d,
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
public update(tick: number): void {
|
|
20
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, MoveInput);
|
|
21
|
+
|
|
22
|
+
for (const rpc of rpcs) {
|
|
23
|
+
const playerResource = this._PlayerResources.get(PlayerResource, rpc.meta.playerSlot);
|
|
24
|
+
const entity = playerResource.safe.entity;
|
|
25
|
+
|
|
26
|
+
// Sanitize input
|
|
27
|
+
const dirX = finite(rpc.data.directionX);
|
|
28
|
+
const dirZ = finite(rpc.data.directionY); // directionY maps to Z axis in 3D
|
|
29
|
+
|
|
30
|
+
const body = this._WorldManager.getBody(this._PhysicsRefs.unsafe.bodyHandle[entity]);
|
|
31
|
+
body.setLinvel(
|
|
32
|
+
{ x: dirX * <%= projectName %>Arena.moveSpeed, y: body.linvel().y, z: dirZ * <%= projectName %>Arena.moveSpeed },
|
|
33
|
+
true,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
39
|
+
import { ECSSystem, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
40
|
+
import { MoveInput, PlayerResource } from '../schema/code-gen/index.js';
|
|
41
|
+
import { PhysicsWorldManager2d } from '@lagless/physics2d';
|
|
42
|
+
import { PhysicsRefs } from '../schema/code-gen/index.js';
|
|
43
|
+
import { <%= projectName %>Arena } from '../arena.js';
|
|
44
|
+
|
|
45
|
+
const finite = (v: number): number => Number.isFinite(v) ? v : 0;
|
|
46
|
+
|
|
47
|
+
@ECSSystem()
|
|
48
|
+
export class ApplyMoveInputSystem implements IECSSystem {
|
|
49
|
+
constructor(
|
|
50
|
+
private readonly _InputProvider: InputProvider,
|
|
51
|
+
private readonly _PlayerResources: PlayerResources,
|
|
52
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
53
|
+
private readonly _WorldManager: PhysicsWorldManager2d,
|
|
54
|
+
) {}
|
|
55
|
+
|
|
56
|
+
public update(tick: number): void {
|
|
57
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, MoveInput);
|
|
58
|
+
|
|
59
|
+
for (const rpc of rpcs) {
|
|
60
|
+
const playerResource = this._PlayerResources.get(PlayerResource, rpc.meta.playerSlot);
|
|
61
|
+
const entity = playerResource.safe.entity;
|
|
62
|
+
|
|
63
|
+
// Sanitize input
|
|
64
|
+
const dirX = finite(rpc.data.directionX);
|
|
65
|
+
const dirY = finite(rpc.data.directionY);
|
|
66
|
+
|
|
67
|
+
const body = this._WorldManager.getBody(this._PhysicsRefs.unsafe.bodyHandle[entity]);
|
|
68
|
+
body.setLinvel({ x: dirX * <%= projectName %>Arena.moveSpeed, y: dirY * <%= projectName %>Arena.moveSpeed }, true);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
<% } else { -%>
|
|
1
73
|
import { ECSSystem, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
2
74
|
import { MoveInput, PlayerResource, Velocity2d } from '../schema/code-gen/index.js';
|
|
3
75
|
import { <%= projectName %>Arena } from '../arena.js';
|
|
@@ -21,3 +93,4 @@ export class ApplyMoveInputSystem implements IECSSystem {
|
|
|
21
93
|
}
|
|
22
94
|
}
|
|
23
95
|
}
|
|
96
|
+
<% } -%>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (simulationType === 'raw') { -%>
|
|
1
2
|
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
2
3
|
import { Transform2d, PlayerBody, PlayerFilter } from '../schema/code-gen/index.js';
|
|
3
4
|
import { <%= projectName %>Arena } from '../arena.js';
|
|
@@ -32,3 +33,4 @@ export class BoundarySystem implements IECSSystem {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}
|
|
36
|
+
<% } -%>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (simulationType === 'raw') { -%>
|
|
1
2
|
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
2
3
|
import { Velocity2d, MovingFilter } from '../schema/code-gen/index.js';
|
|
3
4
|
import { <%= projectName %>Arena } from '../arena.js';
|
|
@@ -16,3 +17,4 @@ export class DampingSystem implements IECSSystem {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
<% } -%>
|
|
@@ -3,9 +3,13 @@ import { SavePrevTransformSystem } from './save-prev-transform.system.js';
|
|
|
3
3
|
import { PlayerConnectionSystem } from './player-connection.system.js';
|
|
4
4
|
import { PlayerLeaveSystem } from './player-leave.system.js';
|
|
5
5
|
import { ApplyMoveInputSystem } from './apply-move-input.system.js';
|
|
6
|
+
<% if (simulationType === 'raw') { -%>
|
|
6
7
|
import { IntegrateSystem } from './integrate.system.js';
|
|
7
8
|
import { DampingSystem } from './damping.system.js';
|
|
8
9
|
import { BoundarySystem } from './boundary.system.js';
|
|
10
|
+
<% } else { -%>
|
|
11
|
+
import { PhysicsStepSystem } from './physics-step.system.js';
|
|
12
|
+
<% } -%>
|
|
9
13
|
import { HashVerificationSystem } from './hash-verification.system.js';
|
|
10
14
|
|
|
11
15
|
export const <%= projectName %>Systems: IECSSystemConstructor[] = [
|
|
@@ -13,8 +17,12 @@ export const <%= projectName %>Systems: IECSSystemConstructor[] = [
|
|
|
13
17
|
PlayerConnectionSystem,
|
|
14
18
|
PlayerLeaveSystem,
|
|
15
19
|
ApplyMoveInputSystem,
|
|
20
|
+
<% if (simulationType === 'raw') { -%>
|
|
16
21
|
IntegrateSystem,
|
|
17
22
|
DampingSystem,
|
|
18
23
|
BoundarySystem,
|
|
24
|
+
<% } else { -%>
|
|
25
|
+
PhysicsStepSystem,
|
|
26
|
+
<% } -%>
|
|
19
27
|
HashVerificationSystem,
|
|
20
28
|
];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (simulationType === 'raw') { -%>
|
|
1
2
|
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
2
3
|
import { Transform2d, Velocity2d, MovingFilter } from '../schema/code-gen/index.js';
|
|
3
4
|
|
|
@@ -16,3 +17,4 @@ export class IntegrateSystem implements IECSSystem {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
<% } -%>
|
package/templates/pixi-react/__packageName__-simulation/src/lib/systems/physics-step.system.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
3
|
+
import { PhysicsWorldManager3d } from '@lagless/physics3d';
|
|
4
|
+
import { Transform3d, PhysicsRefs, PlayerFilter } from '../schema/code-gen/index.js';
|
|
5
|
+
|
|
6
|
+
@ECSSystem()
|
|
7
|
+
export class PhysicsStepSystem implements IECSSystem {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly _WorldManager: PhysicsWorldManager3d,
|
|
10
|
+
private readonly _PlayerFilter: PlayerFilter,
|
|
11
|
+
private readonly _Transform3d: Transform3d,
|
|
12
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
public update(): void {
|
|
16
|
+
this._WorldManager.step();
|
|
17
|
+
|
|
18
|
+
// Sync dynamic bodies: Rapier → ECS Transform
|
|
19
|
+
const t = this._Transform3d.unsafe;
|
|
20
|
+
const pr = this._PhysicsRefs.unsafe;
|
|
21
|
+
for (const entity of this._PlayerFilter) {
|
|
22
|
+
const body = this._WorldManager.getBody(pr.bodyHandle[entity]);
|
|
23
|
+
const pos = body.translation();
|
|
24
|
+
t.positionX[entity] = pos.x;
|
|
25
|
+
t.positionY[entity] = pos.y;
|
|
26
|
+
t.positionZ[entity] = pos.z;
|
|
27
|
+
|
|
28
|
+
const rot = body.rotation();
|
|
29
|
+
t.rotationX[entity] = rot.x;
|
|
30
|
+
t.rotationY[entity] = rot.y;
|
|
31
|
+
t.rotationZ[entity] = rot.z;
|
|
32
|
+
t.rotationW[entity] = rot.w;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
37
|
+
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
38
|
+
import { PhysicsWorldManager2d } from '@lagless/physics2d';
|
|
39
|
+
import { Transform2d, PhysicsRefs, PlayerFilter } from '../schema/code-gen/index.js';
|
|
40
|
+
|
|
41
|
+
@ECSSystem()
|
|
42
|
+
export class PhysicsStepSystem implements IECSSystem {
|
|
43
|
+
constructor(
|
|
44
|
+
private readonly _WorldManager: PhysicsWorldManager2d,
|
|
45
|
+
private readonly _PlayerFilter: PlayerFilter,
|
|
46
|
+
private readonly _Transform2d: Transform2d,
|
|
47
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
public update(): void {
|
|
51
|
+
this._WorldManager.step();
|
|
52
|
+
|
|
53
|
+
// Sync dynamic bodies: Rapier → ECS Transform
|
|
54
|
+
const t = this._Transform2d.unsafe;
|
|
55
|
+
const pr = this._PhysicsRefs.unsafe;
|
|
56
|
+
for (const entity of this._PlayerFilter) {
|
|
57
|
+
const body = this._WorldManager.getBody(pr.bodyHandle[entity]);
|
|
58
|
+
const pos = body.translation();
|
|
59
|
+
t.positionX[entity] = pos.x;
|
|
60
|
+
t.positionY[entity] = pos.y;
|
|
61
|
+
t.rotation[entity] = body.rotation();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
<% } -%>
|
package/templates/pixi-react/__packageName__-simulation/src/lib/systems/player-connection.system.ts
CHANGED
|
@@ -1,3 +1,160 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
import { ECSConfig, ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources, Prefab } from '@lagless/core';
|
|
3
|
+
import { PlayerBody, PlayerJoined, PlayerResource, Transform3d, PhysicsRefs } from '../schema/code-gen/index.js';
|
|
4
|
+
import { PhysicsWorldManager3d } from '@lagless/physics3d';
|
|
5
|
+
import { BodyType } from '@lagless/physics-shared';
|
|
6
|
+
import { <%= projectName %>Arena } from '../arena.js';
|
|
7
|
+
|
|
8
|
+
@ECSSystem()
|
|
9
|
+
export class PlayerConnectionSystem implements IECSSystem {
|
|
10
|
+
private readonly _playerPrefab = Prefab.create()
|
|
11
|
+
.with(Transform3d)
|
|
12
|
+
.with(PhysicsRefs)
|
|
13
|
+
.with(PlayerBody, { radius: <%= projectName %>Arena.playerRadius });
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly _ECSConfig: ECSConfig,
|
|
17
|
+
private readonly _InputProvider: InputProvider,
|
|
18
|
+
private readonly _Transform3d: Transform3d,
|
|
19
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
20
|
+
private readonly _PlayerBody: PlayerBody,
|
|
21
|
+
private readonly _EntitiesManager: EntitiesManager,
|
|
22
|
+
private readonly _PlayerResources: PlayerResources,
|
|
23
|
+
private readonly _WorldManager: PhysicsWorldManager3d,
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
public update(tick: number): void {
|
|
27
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, PlayerJoined);
|
|
28
|
+
const maxPlayers = this._ECSConfig.maxPlayers;
|
|
29
|
+
const spacing = <%= projectName %>Arena.width * 0.5 / Math.max(maxPlayers, 1);
|
|
30
|
+
|
|
31
|
+
for (const rpc of rpcs) {
|
|
32
|
+
const slot = rpc.data.slot;
|
|
33
|
+
const entity = this._EntitiesManager.createEntity(this._playerPrefab);
|
|
34
|
+
|
|
35
|
+
this._PlayerBody.unsafe.playerSlot[entity] = slot;
|
|
36
|
+
|
|
37
|
+
const spawnX = -<%= projectName %>Arena.width * 0.25 + slot * spacing;
|
|
38
|
+
const spawnY = 1.0;
|
|
39
|
+
const spawnZ = 0;
|
|
40
|
+
|
|
41
|
+
const t = this._Transform3d.unsafe;
|
|
42
|
+
t.positionX[entity] = spawnX;
|
|
43
|
+
t.positionY[entity] = spawnY;
|
|
44
|
+
t.positionZ[entity] = spawnZ;
|
|
45
|
+
t.prevPositionX[entity] = spawnX;
|
|
46
|
+
t.prevPositionY[entity] = spawnY;
|
|
47
|
+
t.prevPositionZ[entity] = spawnZ;
|
|
48
|
+
// Identity quaternion
|
|
49
|
+
t.rotationX[entity] = 0;
|
|
50
|
+
t.rotationY[entity] = 0;
|
|
51
|
+
t.rotationZ[entity] = 0;
|
|
52
|
+
t.rotationW[entity] = 1;
|
|
53
|
+
t.prevRotationX[entity] = 0;
|
|
54
|
+
t.prevRotationY[entity] = 0;
|
|
55
|
+
t.prevRotationZ[entity] = 0;
|
|
56
|
+
t.prevRotationW[entity] = 1;
|
|
57
|
+
|
|
58
|
+
// Create physics body
|
|
59
|
+
const body = this._WorldManager.createDynamicBody();
|
|
60
|
+
body.setTranslation({ x: spawnX, y: spawnY, z: spawnZ }, true);
|
|
61
|
+
body.setLinearDamping(5.0);
|
|
62
|
+
|
|
63
|
+
// Create ball collider
|
|
64
|
+
const collider = this._WorldManager.createBallCollider(<%= projectName %>Arena.playerRadius, body);
|
|
65
|
+
|
|
66
|
+
// Store handles in PhysicsRefs
|
|
67
|
+
const pr = this._PhysicsRefs.unsafe;
|
|
68
|
+
pr.bodyHandle[entity] = body.handle;
|
|
69
|
+
pr.colliderHandle[entity] = collider.handle;
|
|
70
|
+
pr.bodyType[entity] = BodyType.DYNAMIC;
|
|
71
|
+
|
|
72
|
+
// Register in collider-entity map
|
|
73
|
+
this._WorldManager.registerCollider(collider.handle, entity);
|
|
74
|
+
|
|
75
|
+
const playerResource = this._PlayerResources.get(PlayerResource, slot);
|
|
76
|
+
playerResource.safe.entity = entity;
|
|
77
|
+
playerResource.safe.connected = 1;
|
|
78
|
+
for (let i = 0; i < rpc.data.playerId.length; i++) {
|
|
79
|
+
playerResource.unsafe.id[i] = rpc.data.playerId[i];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
85
|
+
import { ECSConfig, ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources, Prefab } from '@lagless/core';
|
|
86
|
+
import { PlayerBody, PlayerJoined, PlayerResource, Transform2d, PhysicsRefs } from '../schema/code-gen/index.js';
|
|
87
|
+
import { PhysicsWorldManager2d } from '@lagless/physics2d';
|
|
88
|
+
import { BodyType } from '@lagless/physics-shared';
|
|
89
|
+
import { <%= projectName %>Arena } from '../arena.js';
|
|
90
|
+
|
|
91
|
+
@ECSSystem()
|
|
92
|
+
export class PlayerConnectionSystem implements IECSSystem {
|
|
93
|
+
private readonly _playerPrefab = Prefab.create()
|
|
94
|
+
.with(Transform2d)
|
|
95
|
+
.with(PhysicsRefs)
|
|
96
|
+
.with(PlayerBody, { radius: <%= projectName %>Arena.playerRadius });
|
|
97
|
+
|
|
98
|
+
constructor(
|
|
99
|
+
private readonly _ECSConfig: ECSConfig,
|
|
100
|
+
private readonly _InputProvider: InputProvider,
|
|
101
|
+
private readonly _Transform2d: Transform2d,
|
|
102
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
103
|
+
private readonly _PlayerBody: PlayerBody,
|
|
104
|
+
private readonly _EntitiesManager: EntitiesManager,
|
|
105
|
+
private readonly _PlayerResources: PlayerResources,
|
|
106
|
+
private readonly _WorldManager: PhysicsWorldManager2d,
|
|
107
|
+
) {}
|
|
108
|
+
|
|
109
|
+
public update(tick: number): void {
|
|
110
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, PlayerJoined);
|
|
111
|
+
const maxPlayers = this._ECSConfig.maxPlayers;
|
|
112
|
+
const spacing = <%= projectName %>Arena.width * 0.5 / Math.max(maxPlayers, 1);
|
|
113
|
+
|
|
114
|
+
for (const rpc of rpcs) {
|
|
115
|
+
const slot = rpc.data.slot;
|
|
116
|
+
const entity = this._EntitiesManager.createEntity(this._playerPrefab);
|
|
117
|
+
|
|
118
|
+
this._PlayerBody.unsafe.playerSlot[entity] = slot;
|
|
119
|
+
|
|
120
|
+
const spawnX = <%= projectName %>Arena.width * 0.25 + slot * spacing;
|
|
121
|
+
const spawnY = <%= projectName %>Arena.height * 0.5;
|
|
122
|
+
|
|
123
|
+
const t = this._Transform2d.unsafe;
|
|
124
|
+
t.positionX[entity] = spawnX;
|
|
125
|
+
t.positionY[entity] = spawnY;
|
|
126
|
+
t.rotation[entity] = 0;
|
|
127
|
+
t.prevPositionX[entity] = spawnX;
|
|
128
|
+
t.prevPositionY[entity] = spawnY;
|
|
129
|
+
t.prevRotation[entity] = 0;
|
|
130
|
+
|
|
131
|
+
// Create physics body
|
|
132
|
+
const body = this._WorldManager.createDynamicBody();
|
|
133
|
+
body.setTranslation({ x: spawnX, y: spawnY }, true);
|
|
134
|
+
body.setLinearDamping(5.0);
|
|
135
|
+
|
|
136
|
+
// Create ball collider
|
|
137
|
+
const collider = this._WorldManager.createBallCollider(<%= projectName %>Arena.playerRadius, body);
|
|
138
|
+
|
|
139
|
+
// Store handles in PhysicsRefs
|
|
140
|
+
const pr = this._PhysicsRefs.unsafe;
|
|
141
|
+
pr.bodyHandle[entity] = body.handle;
|
|
142
|
+
pr.colliderHandle[entity] = collider.handle;
|
|
143
|
+
pr.bodyType[entity] = BodyType.DYNAMIC;
|
|
144
|
+
|
|
145
|
+
// Register in collider-entity map
|
|
146
|
+
this._WorldManager.registerCollider(collider.handle, entity);
|
|
147
|
+
|
|
148
|
+
const playerResource = this._PlayerResources.get(PlayerResource, slot);
|
|
149
|
+
playerResource.safe.entity = entity;
|
|
150
|
+
playerResource.safe.connected = 1;
|
|
151
|
+
for (let i = 0; i < rpc.data.playerId.length; i++) {
|
|
152
|
+
playerResource.unsafe.id[i] = rpc.data.playerId[i];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
<% } else { -%>
|
|
1
158
|
import { ECSConfig, ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources, Prefab } from '@lagless/core';
|
|
2
159
|
import { PlayerBody, PlayerJoined, PlayerResource, Transform2d, Velocity2d } from '../schema/code-gen/index.js';
|
|
3
160
|
import { <%= projectName %>Arena } from '../arena.js';
|
|
@@ -45,3 +202,4 @@ export class PlayerConnectionSystem implements IECSSystem {
|
|
|
45
202
|
}
|
|
46
203
|
}
|
|
47
204
|
}
|
|
205
|
+
<% } -%>
|
package/templates/pixi-react/__packageName__-simulation/src/lib/systems/player-leave.system.ts
CHANGED
|
@@ -1,3 +1,72 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
import { ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
3
|
+
import { PlayerLeft, PlayerResource, PhysicsRefs } from '../schema/code-gen/index.js';
|
|
4
|
+
import { PhysicsWorldManager3d } from '@lagless/physics3d';
|
|
5
|
+
|
|
6
|
+
@ECSSystem()
|
|
7
|
+
export class PlayerLeaveSystem implements IECSSystem {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly _InputProvider: InputProvider,
|
|
10
|
+
private readonly _EntitiesManager: EntitiesManager,
|
|
11
|
+
private readonly _PlayerResources: PlayerResources,
|
|
12
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
13
|
+
private readonly _WorldManager: PhysicsWorldManager3d,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
public update(tick: number): void {
|
|
17
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, PlayerLeft);
|
|
18
|
+
|
|
19
|
+
for (const rpc of rpcs) {
|
|
20
|
+
const player = this._PlayerResources.get(PlayerResource, rpc.data.slot);
|
|
21
|
+
const entity = player.safe.entity;
|
|
22
|
+
|
|
23
|
+
// Remove physics objects
|
|
24
|
+
const colliderHandle = this._PhysicsRefs.unsafe.colliderHandle[entity];
|
|
25
|
+
const bodyHandle = this._PhysicsRefs.unsafe.bodyHandle[entity];
|
|
26
|
+
this._WorldManager.unregisterCollider(colliderHandle);
|
|
27
|
+
this._WorldManager.removeCollider(colliderHandle);
|
|
28
|
+
this._WorldManager.removeBody(bodyHandle);
|
|
29
|
+
|
|
30
|
+
player.safe.connected = 0;
|
|
31
|
+
this._EntitiesManager.removeEntity(entity);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
36
|
+
import { ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
37
|
+
import { PlayerLeft, PlayerResource, PhysicsRefs } from '../schema/code-gen/index.js';
|
|
38
|
+
import { PhysicsWorldManager2d } from '@lagless/physics2d';
|
|
39
|
+
|
|
40
|
+
@ECSSystem()
|
|
41
|
+
export class PlayerLeaveSystem implements IECSSystem {
|
|
42
|
+
constructor(
|
|
43
|
+
private readonly _InputProvider: InputProvider,
|
|
44
|
+
private readonly _EntitiesManager: EntitiesManager,
|
|
45
|
+
private readonly _PlayerResources: PlayerResources,
|
|
46
|
+
private readonly _PhysicsRefs: PhysicsRefs,
|
|
47
|
+
private readonly _WorldManager: PhysicsWorldManager2d,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
public update(tick: number): void {
|
|
51
|
+
const rpcs = this._InputProvider.collectTickRPCs(tick, PlayerLeft);
|
|
52
|
+
|
|
53
|
+
for (const rpc of rpcs) {
|
|
54
|
+
const player = this._PlayerResources.get(PlayerResource, rpc.data.slot);
|
|
55
|
+
const entity = player.safe.entity;
|
|
56
|
+
|
|
57
|
+
// Remove physics objects
|
|
58
|
+
const colliderHandle = this._PhysicsRefs.unsafe.colliderHandle[entity];
|
|
59
|
+
const bodyHandle = this._PhysicsRefs.unsafe.bodyHandle[entity];
|
|
60
|
+
this._WorldManager.unregisterCollider(colliderHandle);
|
|
61
|
+
this._WorldManager.removeCollider(colliderHandle);
|
|
62
|
+
this._WorldManager.removeBody(bodyHandle);
|
|
63
|
+
|
|
64
|
+
player.safe.connected = 0;
|
|
65
|
+
this._EntitiesManager.removeEntity(entity);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
<% } else { -%>
|
|
1
70
|
import { ECSSystem, EntitiesManager, IECSSystem, InputProvider, PlayerResources } from '@lagless/core';
|
|
2
71
|
import { PlayerLeft, PlayerResource } from '../schema/code-gen/index.js';
|
|
3
72
|
|
|
@@ -19,3 +88,4 @@ export class PlayerLeaveSystem implements IECSSystem {
|
|
|
19
88
|
}
|
|
20
89
|
}
|
|
21
90
|
}
|
|
91
|
+
<% } -%>
|
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
<% if (simulationType === 'physics3d') { -%>
|
|
2
|
+
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
3
|
+
import { Transform3d, PlayerFilter } from '../schema/code-gen/index.js';
|
|
4
|
+
|
|
5
|
+
@ECSSystem()
|
|
6
|
+
export class SavePrevTransformSystem implements IECSSystem {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly _PlayerFilter: PlayerFilter,
|
|
9
|
+
private readonly _Transform3d: Transform3d,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
public update(): void {
|
|
13
|
+
const t = this._Transform3d.unsafe;
|
|
14
|
+
for (const entity of this._PlayerFilter) {
|
|
15
|
+
t.prevPositionX[entity] = t.positionX[entity];
|
|
16
|
+
t.prevPositionY[entity] = t.positionY[entity];
|
|
17
|
+
t.prevPositionZ[entity] = t.positionZ[entity];
|
|
18
|
+
t.prevRotationX[entity] = t.rotationX[entity];
|
|
19
|
+
t.prevRotationY[entity] = t.rotationY[entity];
|
|
20
|
+
t.prevRotationZ[entity] = t.rotationZ[entity];
|
|
21
|
+
t.prevRotationW[entity] = t.rotationW[entity];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
<% } else if (simulationType === 'physics2d') { -%>
|
|
26
|
+
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
27
|
+
import { Transform2d, PlayerFilter } from '../schema/code-gen/index.js';
|
|
28
|
+
|
|
29
|
+
@ECSSystem()
|
|
30
|
+
export class SavePrevTransformSystem implements IECSSystem {
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly _PlayerFilter: PlayerFilter,
|
|
33
|
+
private readonly _Transform2d: Transform2d,
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
public update(): void {
|
|
37
|
+
const t = this._Transform2d.unsafe;
|
|
38
|
+
for (const entity of this._PlayerFilter) {
|
|
39
|
+
t.prevPositionX[entity] = t.positionX[entity];
|
|
40
|
+
t.prevPositionY[entity] = t.positionY[entity];
|
|
41
|
+
t.prevRotation[entity] = t.rotation[entity];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
<% } else { -%>
|
|
1
46
|
import { ECSSystem, IECSSystem } from '@lagless/core';
|
|
2
47
|
import { Transform2d, PlayerFilter } from '../schema/code-gen/index.js';
|
|
3
48
|
|
|
@@ -15,3 +60,4 @@ export class SavePrevTransformSystem implements IECSSystem {
|
|
|
15
60
|
}
|
|
16
61
|
}
|
|
17
62
|
}
|
|
63
|
+
<% } -%>
|