lecodes-sdk 2.0.3 → 2.0.4

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/dist/global.d.ts CHANGED
@@ -390,6 +390,7 @@ declare global {
390
390
  type NavPath = SDK.NavPath
391
391
  type NavQueryOptions = SDK.NavQueryOptions
392
392
  type NavRaycastHit = SDK.NavRaycastHit
393
+ type NetCorrection = SDK.NetCorrection
393
394
  type NetEntity<S extends object = any> = SDK.NetEntity<S>
394
395
  type NetEvents = SDK.NetEvents
395
396
  type NetInput<T extends object = any> = SDK.NetInput<T>
@@ -20,7 +20,7 @@ export { localStorage } from "./runtime/storage";
20
20
  export { device, type HapticStyle, type MotionOptions } from "./runtime/device";
21
21
  export { Input, InputChannel, type InputKeyEvent, type InputGamepadEvent, type InputEventName, type GamepadState, type GamepadAxisName } from "./runtime/input";
22
22
  export { WebSocket } from "./runtime/net";
23
- export { Net, NetPlayer, Replicated, NetEntity, type NetMessage, type NetRole, type NetStatus, type NetEvents, type NetLaunch, type NetInput, type NetKind, type NetTransform } from "./net";
23
+ export { Net, NetPlayer, Replicated, NetEntity, type NetMessage, type NetRole, type NetStatus, type NetEvents, type NetLaunch, type NetInput, type NetKind, type NetTransform, type NetCorrection } from "./net";
24
24
  export { __rpc, __channel, __serverOnly, RpcError, type ClientChannel } from "./runtime/rpc";
25
25
  export { AudioPlayer, VideoPlayer } from "./runtime/media";
26
26
  export { Sound, type SoundOptions } from "./audio/Sound";
@@ -3,6 +3,7 @@ export type Field = {
3
3
  key: string;
4
4
  kind: FieldKind;
5
5
  len: number;
6
+ step: boolean;
6
7
  };
7
8
  export type Layout = {
8
9
  fields: Field[];
@@ -11,7 +12,7 @@ export type Layout = {
11
12
  bytes: number;
12
13
  sig: string;
13
14
  };
14
- export declare const makeLayout: (defaults: Record<string, unknown>, what: string) => Layout;
15
+ export declare const makeLayout: (defaults: Record<string, unknown>, what: string, discrete?: readonly string[]) => Layout;
15
16
  /** A fresh state object shaped like the defaults (arrays copied, never shared). */
16
17
  export declare const cloneDefaults: <T extends object>(defaults: T) => T;
17
18
  /** Copy the layout's fields from `from` into `into` (array elements copied in place; unknown keys
@@ -19,7 +20,7 @@ export declare const cloneDefaults: <T extends object>(defaults: T) => T;
19
20
  export declare const assignState: (layout: Layout, into: any, from: any) => void;
20
21
  export declare const packInto: (view: DataView, offset: number, layout: Layout, s: any) => number;
21
22
  export declare const unpackFrom: (view: DataView, offset: number, layout: Layout, into: any) => number;
22
- /** `out = a + (b − a) · t` for the numeric fields; booleans take `b`'s. */
23
+ /** `out = a + (b − a) · t` for the numeric fields; booleans and the discrete ones take `b`'s. */
23
24
  export declare const lerpState: (layout: Layout, a: any, b: any, t: number, out: any) => void;
24
25
  /** FNV-1a over a string → 8 hex chars. Enough to tell two bundles' schemas apart. */
25
26
  export declare const hashString: (s: string) => string;
@@ -34,7 +34,7 @@ export type NetStats = {
34
34
  };
35
35
  export declare const CH_RELIABLE = 0;
36
36
  export declare const CH_UNRELIABLE = 1;
37
- export declare const PROTO = 2;
37
+ export declare const PROTO = 3;
38
38
  export declare const LOCAL_SLOT = -1;
39
39
  export declare const bridge: () => typeof _creatorNet | null;
40
40
  export declare class NetPlayer {
@@ -45,6 +45,9 @@ export declare class NetPlayer {
45
45
  /** A local bag for game facts (score, team, ready). NOT replicated — anything a client must see
46
46
  * goes through a message or replicated state. */
47
47
  data: any;
48
+ /** Server: what this player's `Net.connect(…, { hello })` carried (the game's own: a build, a
49
+ * token) — the value `Net.listen({ accept })` was asked about. Undefined everywhere else. */
50
+ hello: unknown;
48
51
  private _leave;
49
52
  constructor(id: number, name: string, slot: number);
50
53
  get isMe(): boolean;
@@ -72,6 +75,12 @@ export declare const state: {
72
75
  connectResolve: null | (() => void);
73
76
  connectReject: null | ((e: Error) => void);
74
77
  myName: string;
78
+ /** client: the game's own part of the `$hello` (`Net.connect(…, { hello })`), opaque to the SDK */
79
+ hello: unknown;
80
+ /** server: the game's say in the handshake (`Net.listen({ accept })`) — a reason refuses */
81
+ accept: null | ((hello: unknown, who: {
82
+ name: string;
83
+ }) => string | null | void);
75
84
  };
76
85
  export declare const hooks: {
77
86
  frameEarly: null | ((dt: number) => void);
@@ -1,7 +1,7 @@
1
1
  import { NetPlayer, type NetEvents, type NetLaunch, type NetMessage, type NetRole, type NetStatus } from "./core";
2
- import { NetEntity, Replicated, stats, type NetInput, type NetKind, type NetTransform } from "./replication";
2
+ import { NetEntity, Replicated, stats, type NetCorrection, type NetInput, type NetKind, type NetTransform } from "./replication";
3
3
  export { NetPlayer, NetEntity, Replicated };
4
- export type { NetEvents, NetLaunch, NetMessage, NetRole, NetStatus, NetInput, NetKind, NetTransform };
4
+ export type { NetEvents, NetLaunch, NetMessage, NetRole, NetStatus, NetInput, NetKind, NetTransform, NetCorrection };
5
5
  declare class NetImpl {
6
6
  get role(): NetRole;
7
7
  get status(): NetStatus;
@@ -19,21 +19,33 @@ declare class NetImpl {
19
19
  /** The simulation tick — one per fixed step (`updateFixed`). The server's is THE tick; a client
20
20
  * runs a few ticks ahead so its inputs arrive in time. */
21
21
  get tick(): number;
22
+ /** The server tick this machine is SHOWING the others at: on a client, its estimate of the server's
23
+ * clock less the interpolation delay (fractional); `tick` everywhere else, where nothing is
24
+ * interpolated. What a client names when it tells the server "as I saw it". */
25
+ get viewTick(): number;
22
26
  /** Live counters: snapshots, input lead / `ahead`, corrections, missed inputs, bytes out. */
23
27
  get stats(): typeof stats;
24
28
  /** Start serving on `port`. From offline this makes us the host (a player who is also the
25
- * server); on the dedicated exe (`Net.launch.role === "server"`) there is no local player. */
29
+ * server); on the dedicated exe (`Net.launch.role === "server"`) there is no local player.
30
+ * `accept` is the game's say in the handshake: it is given what the client's `connect` passed as
31
+ * `hello` and returns a reason to refuse (the client's `reject` event carries it), nothing to accept. */
26
32
  listen(opts?: {
27
33
  port?: number;
28
34
  maxClients?: number;
29
35
  name?: string;
30
36
  dedicated?: boolean;
37
+ accept?: (hello: unknown, who: {
38
+ name: string;
39
+ }) => string | null | void;
31
40
  }): boolean;
32
- /** Connect to a host/server (`address` = "ip:port"). Resolves once the server welcomed us. */
41
+ /** Connect to a host/server (`address` = "ip:port"). Resolves once the server welcomed us.
42
+ * `hello` = anything JSON the game wants the server to see before it lets us in (its
43
+ * `Net.listen({ accept })`, then `player.hello`); the SDK only carries it. */
33
44
  connect(target: {
34
45
  address: string;
35
46
  }, opts?: {
36
47
  name?: string;
48
+ hello?: unknown;
37
49
  }): Promise<void>;
38
50
  /** Client → offline. Host/server → everyone is dropped, back to offline. */
39
51
  disconnect(): void;
@@ -62,10 +74,15 @@ declare class NetImpl {
62
74
  setInput<T extends object>(inp: NetInput<T>, values: Partial<T>): void;
63
75
  /** A replicated entity type: the defaults are the snapshot layout (same field rules as `input`),
64
76
  * `transform` tells the SDK which fields mirror the node's pose, `client` births the proxy on a
65
- * client when the server spawns one (`new Node().aspect(Replicated, { kind, owner })`). */
77
+ * client when the server spawns one (`new Node().aspect(Replicated, { kind, owner })`) — it may
78
+ * be set later, `Kind.client = …`, where the client's code lives. `discrete` names the numeric
79
+ * fields that are never interpolated (an index, a count); `correction` tunes how the owner's
80
+ * prediction is brought back to the server's state (all in the game's own units). */
66
81
  kind<S extends object>(name: string, defaults: S, opts?: {
67
82
  transform?: NetTransform;
68
83
  client?: (rep: NetEntity<S>, scene: any) => any;
84
+ discrete?: readonly (keyof S & string)[];
85
+ correction?: Partial<NetCorrection>;
69
86
  }): NetKind<S>;
70
87
  /** Every replicated entity this machine knows. */
71
88
  get entities(): readonly NetEntity[];
@@ -5,6 +5,18 @@ import { Scene } from "../gl/Scene";
5
5
  import { type Layout } from "./codec";
6
6
  import { NetPlayer } from "./core";
7
7
  export type NetTransform = "pos" | "pos+rot" | "none";
8
+ /** How an owner's prediction is brought back to the server's state. The SDK knows nothing of what
9
+ * the entity is, so the defaults are neutral: every disagreement counts, none is a teleport. */
10
+ export type NetCorrection = {
11
+ /** A vertical disagreement below this is ignored (world units). 0 = none ignored. A body that
12
+ * snaps to a floor differs by a little between two machines, and lifting it by that feeds itself. */
13
+ deadbandY: number;
14
+ /** A disagreement of this much or more is a teleport: the body jumps and `correction` stays zero
15
+ * (world units). Infinity = always smoothed. */
16
+ snap: number;
17
+ /** The decay time constant of the visual offset `correction` (seconds). 0 = no smoothing. */
18
+ tau: number;
19
+ };
8
20
  /** The per-tick input layout: `const PlayerInput = Net.input({ moveX: 0, moveY: 0, jump: false })`. */
9
21
  export type NetInput<T extends object = any> = {
10
22
  readonly layout: Layout;
@@ -19,8 +31,10 @@ export type NetKind<S extends object = any> = {
19
31
  /** Which state fields the SDK samples on the server and applies to a client proxy: `pos` (a
20
32
  * `pos: [x,y,z]` field), `pos+rot` (+ a `rot: [x,y,z,w]` quaternion field), or `none`. */
21
33
  readonly transform: NetTransform;
22
- /** Births the client-side proxy on spawn; the SDK attaches `Replicated` to what it returns. */
23
- readonly client?: (rep: NetEntity<S>, scene: Scene | null) => Node | void;
34
+ readonly correction: NetCorrection;
35
+ /** Births the client-side proxy on spawn; the SDK attaches `Replicated` to what it returns.
36
+ * Assignable: a declaration both ends share may leave it to the client's own module. */
37
+ client?: (rep: NetEntity<S>, scene: Scene | null) => Node | void;
24
38
  readonly __t?: S;
25
39
  };
26
40
  /** Live counters (`Net.stats`): how the clocks and the prediction are doing. */
@@ -59,6 +73,9 @@ export declare class NetEntity<S extends object = any> {
59
73
  /** The owner's input for the tick being simulated — the ring entry on the server (the last one
60
74
  * repeated when it is late), the local input on the owner / host / offline. */
61
75
  input<T extends object>(inp: NetInput<T>): T;
76
+ /** `input()` is an input the owner gave — false until the first one exists (the server before
77
+ * the owner's first packet, the owner before its first `Net.setInput`), when it is the defaults. */
78
+ get hasInput(): boolean;
62
79
  /** Client: `state` was refreshed (a snapshot landed / the interpolation moved). */
63
80
  on(event: "state", cb: (s: S) => void): this;
64
81
  off(event: "state", cb: (s: S) => void): this;
@@ -66,10 +83,13 @@ export declare class NetEntity<S extends object = any> {
66
83
  export declare const declareKind: <S extends object>(name: string, defaults: S, opts?: {
67
84
  transform?: NetTransform;
68
85
  client?: NetKind<S>["client"];
86
+ discrete?: readonly string[];
87
+ correction?: Partial<NetCorrection>;
69
88
  }) => NetKind<S>;
70
89
  export declare const declareInput: <T extends object>(defaults: T) => NetInput<T>;
71
90
  export declare const setInput: <T extends object>(inp: NetInput<T>, values: Partial<T>) => void;
72
91
  export declare const currentTick: () => number;
92
+ export declare const viewTick: () => number;
73
93
  export declare const allEntities: () => NetEntity[];
74
94
  export declare const entityById: (netId: number) => NetEntity | undefined;
75
95
  /**
@@ -97,5 +117,6 @@ export declare class Replicated extends Aspect<"replicated", Node> {
97
117
  get correction(): Vec3;
98
118
  /** The owner's input for the tick being simulated (see `NetEntity.input`). */
99
119
  input<T extends object>(inp: NetInput<T>): T;
120
+ get hasInput(): boolean;
100
121
  }
101
122
  export declare const installReplication: () => void;
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "2.0.3";
1
+ export declare const SDK_VERSION = "2.0.4";
2
2
  /** The major of a semver string, or null when it is not one. */
3
3
  export declare const sdkMajor: (version: string | null | undefined) => number | null;
4
4
  /** The `// sdk: <version>` line of a compiled bundle's header (sdk/src/compile/header.ts), read from