@driftengine/core 4.7.4 → 4.8.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/CHANGELOG.json +57 -0
- package/README.md +42 -1
- package/package.json +6 -6
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,61 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.8.1",
|
|
4
|
+
"date": "2026-10-03",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "fixed",
|
|
8
|
+
"short": "A character controller is stopped by a beam at head height, and a capsule meets a box that only its middle or its top would touch, where it passed through.",
|
|
9
|
+
"text": "Two defects in the narrow phase, which together let a standing 1.8 m capsule walk through a beam from 1.4 to 1.7 m. A capsule against a box was measured from the two balls at the ends of its segment only, so a slab across its middle, which neither ball reaches, was not there at all; the closest point inside the segment is now found wherever the ends are not the answer, which costs nothing for a capsule standing on a floor or lying on a face. And `shapecast` advanced by the first contact's separation, which for a capsule is its lower end, so a box that only the upper end would meet was never reached. It now advances by the nearest contact. `overlap` and the solver's contacts gain the interior point too, so a capsule body lying across a rail rests on it."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "fixed",
|
|
13
|
+
"short": "A character climbs any step up to its step height, where a rise nearly as tall as its radius stopped it at the edge.",
|
|
14
|
+
"text": "The forward part of a step is what is left of the tick's move when the body meets the step, and that can be millimetres. With a 0.32 m radius and a 0.35 m step height, a 0.25 m rise climbed and a 0.28 m one did not: the foot came down on the step's edge, a contact too steep to stand on, slid back off, and never tried again. A step that lands on an edge now carries the body over it, so the foot comes down on the top, which moves it up to one radius further in that tick than it was going to. `shapecast` also closes on a surface it approaches at a shallow angle, where its fixed number of advances ran out first and it reported nothing there: it now advances by how fast the two close along the normal that separates them, not by the whole travel."
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"version": "4.8.0",
|
|
20
|
+
"date": "2026-10-03",
|
|
21
|
+
"entries": [
|
|
22
|
+
{
|
|
23
|
+
"kind": "changed",
|
|
24
|
+
"short": "A drift/* answer that is one of a few things is an enum a script matches on: a contact, a phase of matter, a chemistry event, a tree's or a save's status, the editor's modes, a hand and its joints.",
|
|
25
|
+
"text": "Twelve enums are declared by the engine for DriftScript 1.15.0's host enums: `TreeStatus`, `MatterPhase`, `ChemistryEvent`, `ContactKind`, `SaveStatus`, `GizmoMode`, `GizmoSpace`, `GizmoHandle`, `EditorMode`, `FieldKind`, `Hand` and `HandJoint`. `behavior.tick`, `step` and `status`, `chemistry.phase` and `eventKind`, `physics.contactKind`, `persistence.saveStatus` and the editor's `gizmoMode`, `space`, `hovered`, `mode` and `fieldKind` answer a variant where they answered a number or a string, and `drift/xr` takes a `Hand` and a `HandJoint` where it took `\"right\"` and `\"index-finger-tip\"`. A script that compared one of these against a number or a string no longer compiles, which is the point: those comparisons were checked by nothing, and a misspelt joint read zero. Each variant list is tested against the engine constants it translates. `contactKind`, `eventKind` and `fieldKind` refuse an index past what exists, where they answered a stale entry or nothing."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"kind": "fixed",
|
|
29
|
+
"short": "An optional component field reads and writes as an option from a script, in a query loop and through a handle: a cleared one reads none, and writing one no longer stores NaN.",
|
|
30
|
+
"text": "**`driftscript` moves to 1.16.0.** A loop read only an optional field's value column and an assignment put the option object into it, which stored `NaN` marked present; through a handle the read answered a bare number. The compiler now reads and writes the presence column beside the value, and `drift/ecs`'s `read` answers `undefined` for an absent optional field where it answered zero, which the compiler turns into `none`. The entities example's frogs keep their target as an `Entity?`, where they kept a flag beside a plain `Entity` because of this."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"kind": "fixed",
|
|
34
|
+
"short": "A lockstep session whose player holds a control on the first tick stays in step: the ticks before anyone can publish an input are neutral on every peer, where each peer guessed its own.",
|
|
35
|
+
"text": "No peer can publish an input for the ticks before its first tick plus the input delay, so every peer guessed them and nothing ever corrected the guess. A guess repeats the newest input the log holds, and for a peer's own participant that was the input it had just submitted for a later tick, which the other peer cannot know. So a key held when the match began split the two worlds, and the first fingerprint halted the session. `LockstepSession` settles those ticks as neutral for every participant on its first `submit`."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"kind": "fixed",
|
|
39
|
+
"short": "WebSocketTransport takes a browser WebSocket as it is typed: the socket shape asked for any buffer, which a browser's send does not accept, so the package's own example did not compile.",
|
|
40
|
+
"text": "`SocketLike.send` and `DataChannelLike.send` take a view of an `ArrayBuffer`, which is what a browser's `WebSocket` and `RTCDataChannel` accept, and both transports copy a message on shared memory off it before sending, since a browser refuses to send one."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"kind": "added",
|
|
44
|
+
"short": "runSchedule takes a failure reporter: every system failure, every tick, with the system's name, the tick and the error, where it logged once per system and nothing else could see it.",
|
|
45
|
+
"text": "`runSchedule(world, schedule, tick, report)`. Without a reporter a failing system is still skipped and logged once per system per world, as before. With one, nothing is logged and the reporter decides: count failures, show them in an overlay, send them to telemetry, or throw to make a failure fatal while developing, which stops the schedule at that system."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"kind": "changed",
|
|
49
|
+
"short": "A system's component access follows its calls into other files, so DS0291 stops calling a correct declaration unused and an undeclared write is a compile error, not a refusal at runtime.",
|
|
50
|
+
"text": "From DriftScript 1.16.0. Access was inferred from a module's own functions, so a helper imported from another file touched nothing as far as a system was concerned: a declaration of what it wrote drew `DS0291`, and leaving it out drew no `DS0288` and left the write out of the metadata the engine builds its schedule from. And from 1.15.0, `==` compares what two values hold, so an enum variant still equals itself after a hot reload; it compared objects by identity, and a rule written `if round.phase != Phase.Playing` returned forever after the first save."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"kind": "added",
|
|
54
|
+
"short": "The manual gains a DriftScript section and a networking chapter: what a script is for, setting one up, patterns, everything it can reach, testing, and two peers in one match over a lossy link.",
|
|
55
|
+
"text": "**DriftScript in a game** says what belongs in a script and what stays in TypeScript, **Setting up scripts** covers the build, binding, services and hot reload, **Patterns** names the shapes the examples share, **What a script can reach** lists every module, function and enum from the capability file, and **Determinism, testing and shipping** covers `@deterministic`, testing a script in Node and what reaches the bundle. The starter and the first game keep their rules in `.drs` files, the starter carries the Vite config and declaration a copied project needs and builds without a warning, and the first game's round is tested in Node. **Networking and rollback** is built on the new netplay example: two peers in one page over an impaired loopback, with the link, the redundancy and the input delay as live switches."
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
2
59
|
{
|
|
3
60
|
"version": "4.7.4",
|
|
4
61
|
"date": "2026-10-02",
|
package/README.md
CHANGED
|
@@ -36,6 +36,9 @@ import {
|
|
|
36
36
|
createRenderer,
|
|
37
37
|
startLoop,
|
|
38
38
|
} from '@driftengine/core';
|
|
39
|
+
import { bindModule } from '@driftengine/script';
|
|
40
|
+
import { loadModule, patchModule } from 'driftscript';
|
|
41
|
+
import * as spinScript from './spin.drs';
|
|
39
42
|
|
|
40
43
|
const canvas = document.querySelector<HTMLCanvasElement>('#stage');
|
|
41
44
|
if (canvas === null) throw new Error('the page must carry <canvas id="stage">');
|
|
@@ -94,6 +97,22 @@ camera.position[1] = 4.5;
|
|
|
94
97
|
camera.position[2] = 8;
|
|
95
98
|
camera.lookAt(0, 1, 0);
|
|
96
99
|
|
|
100
|
+
/*
|
|
101
|
+
* How fast the cube turns is a rule, and rules live in DriftScript: `spin.drs`. The bundler compiles
|
|
102
|
+
* the file, `loadModule` makes a module of it, and `bindModule` gives it the engine capabilities it
|
|
103
|
+
* imports. Saving the file while the page is open replaces its function in place.
|
|
104
|
+
*/
|
|
105
|
+
const spinModule = loadModule(spinScript as Record<string, unknown>);
|
|
106
|
+
const bound = bindModule(spinModule, {});
|
|
107
|
+
if (!bound.bound) throw new Error(bound.reason);
|
|
108
|
+
const rules = spinModule.exports as unknown as { spinRate(time: number): number };
|
|
109
|
+
|
|
110
|
+
if (import.meta.hot) {
|
|
111
|
+
import.meta.hot.accept('./spin.drs', (next) => {
|
|
112
|
+
if (next !== undefined) patchModule(spinModule, next as Record<string, unknown>);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
97
116
|
/*
|
|
98
117
|
* Two angles, because that is what `alpha` is for. The simulation advances in fixed steps and
|
|
99
118
|
* the display does not, so a frame almost never lands on a step boundary: drawing `spin`
|
|
@@ -102,11 +121,13 @@ camera.lookAt(0, 1, 0);
|
|
|
102
121
|
*/
|
|
103
122
|
let spin = 0;
|
|
104
123
|
let previousSpin = 0;
|
|
124
|
+
let time = 0;
|
|
105
125
|
|
|
106
126
|
startLoop({
|
|
107
127
|
simulate(dt) {
|
|
108
128
|
previousSpin = spin;
|
|
109
|
-
|
|
129
|
+
time += dt;
|
|
130
|
+
spin += dt * rules.spinRate(time);
|
|
110
131
|
},
|
|
111
132
|
render(alpha) {
|
|
112
133
|
spinner.setRotationAxisAngle(0, 1, 0, previousSpin + (spin - previousSpin) * alpha);
|
|
@@ -123,6 +144,26 @@ startLoop({
|
|
|
123
144
|
});
|
|
124
145
|
```
|
|
125
146
|
|
|
147
|
+
How fast the cube turns is a rule, so it lives in DriftScript, in `spin.drs` beside the page:
|
|
148
|
+
|
|
149
|
+
```drs
|
|
150
|
+
// How the cube turns. The page owns the cube; this file only says how fast it goes.
|
|
151
|
+
//
|
|
152
|
+
// Save it while the page is open and the cube takes the new rate without the page reloading. Try a
|
|
153
|
+
// steady 2, a cube that stops every few seconds, or one that turns the other way.
|
|
154
|
+
|
|
155
|
+
import { sin } from "std/math"
|
|
156
|
+
|
|
157
|
+
// Radians a second: a steady turn that quickens and eases every few seconds.
|
|
158
|
+
fn spinRate(time: f32) -> f32 {
|
|
159
|
+
return 0.8 + 0.4 * math.sin(time * 0.5)
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The bundler compiles it with DriftScript's Vite plugin, set up as in the starter's `vite.config.ts`;
|
|
164
|
+
[Installation](https://driftengine.dev/docs/start/installation#driftscript) has the two packages and
|
|
165
|
+
the config.
|
|
166
|
+
|
|
126
167
|
This is the engine's `examples/starter`, and a test keeps the two the same.
|
|
127
168
|
[Hello world](https://driftengine.dev/docs/start/hello-world) walks through it, and
|
|
128
169
|
[Your first game](https://driftengine.dev/docs/start/first-game) builds a complete one.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.1",
|
|
4
4
|
"description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.8.1",
|
|
33
|
+
"@driftengine/physics": "4.8.1",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@driftengine/nav": "4.
|
|
71
|
-
"@driftengine/network": "4.
|
|
72
|
-
"@driftengine/texture": "4.
|
|
70
|
+
"@driftengine/nav": "4.8.1",
|
|
71
|
+
"@driftengine/network": "4.8.1",
|
|
72
|
+
"@driftengine/texture": "4.8.1"
|
|
73
73
|
}
|
|
74
74
|
}
|