@trioforge/crucible 0.1.0 → 0.3.0
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/maths/spline/package.json +4 -0
- package/out/client/index.d.ts +7 -0
- package/out/client/init.luau +36 -1
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/data.d.ts +224 -0
- package/out/data.luau +97 -0
- package/out/emit/configs.d.ts +4 -0
- package/out/emit/configs.luau +22 -0
- package/out/emit/data.d.ts +6 -0
- package/out/emit/data.luau +19 -0
- package/out/emit/index.d.ts +5 -1
- package/out/emit/init.luau +23 -7
- package/out/emit/network.d.ts +6 -0
- package/out/emit/network.luau +36 -0
- package/out/emit/replicated.d.ts +4 -0
- package/out/emit/replicated.luau +13 -0
- package/out/index.d.ts +8 -0
- package/out/init.luau +23 -0
- package/out/internal/binding.luau +5 -0
- package/out/internal/boot.luau +112 -8
- package/out/internal/configs/limits.d.ts +10 -0
- package/out/internal/configs/limits.luau +25 -0
- package/out/internal/configs/port.d.ts +29 -0
- package/out/internal/configs/port.luau +99 -0
- package/out/internal/configs/runtime.d.ts +21 -0
- package/out/internal/configs/runtime.luau +981 -0
- package/out/internal/data/admission.d.ts +1 -0
- package/out/internal/data/admission.luau +673 -0
- package/out/internal/data/declarations.d.ts +1 -0
- package/out/internal/data/declarations.luau +384 -0
- package/out/internal/data/envelope.d.ts +1 -0
- package/out/internal/data/envelope.luau +318 -0
- package/out/internal/data/handles.d.ts +1 -0
- package/out/internal/data/handles.luau +255 -0
- package/out/internal/data/inbox-transforms.d.ts +1 -0
- package/out/internal/data/inbox-transforms.luau +458 -0
- package/out/internal/data/inbox.d.ts +1 -0
- package/out/internal/data/inbox.luau +437 -0
- package/out/internal/data/lease.d.ts +1 -0
- package/out/internal/data/lease.luau +304 -0
- package/out/internal/data/limits.d.ts +1 -0
- package/out/internal/data/limits.luau +234 -0
- package/out/internal/data/messages.d.ts +1 -0
- package/out/internal/data/messages.luau +69 -0
- package/out/internal/data/operation-id.d.ts +1 -0
- package/out/internal/data/operation-id.luau +61 -0
- package/out/internal/data/paths.d.ts +2 -0
- package/out/internal/data/paths.luau +323 -0
- package/out/internal/data/port.d.ts +1 -0
- package/out/internal/data/port.luau +290 -0
- package/out/internal/data/profiles.d.ts +1 -0
- package/out/internal/data/profiles.luau +862 -0
- package/out/internal/data/receipts.d.ts +1 -0
- package/out/internal/data/receipts.luau +190 -0
- package/out/internal/data/record-bindings.d.ts +1 -0
- package/out/internal/data/record-bindings.luau +92 -0
- package/out/internal/data/record-declarations.d.ts +1 -0
- package/out/internal/data/record-declarations.luau +140 -0
- package/out/internal/data/records.d.ts +1 -0
- package/out/internal/data/records.luau +400 -0
- package/out/internal/data/runtime.d.ts +1 -0
- package/out/internal/data/runtime.luau +918 -0
- package/out/internal/data/scheduler.d.ts +1 -0
- package/out/internal/data/scheduler.luau +507 -0
- package/out/internal/data/session.d.ts +1 -0
- package/out/internal/data/session.luau +234 -0
- package/out/internal/data/shutdown.d.ts +1 -0
- package/out/internal/data/shutdown.luau +91 -0
- package/out/internal/data/waiters.d.ts +1 -0
- package/out/internal/data/waiters.luau +65 -0
- package/out/internal/lifecycle.luau +45 -0
- package/out/internal/network/catalog.d.ts +26 -0
- package/out/internal/network/catalog.luau +182 -0
- package/out/internal/network/claims.d.ts +1 -0
- package/out/internal/network/claims.luau +146 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +405 -0
- package/out/internal/network/codec.d.ts +63 -0
- package/out/internal/network/codec.luau +1183 -0
- package/out/internal/network/envelope.d.ts +1 -0
- package/out/internal/network/envelope.luau +247 -0
- package/out/internal/network/limits.d.ts +26 -0
- package/out/internal/network/limits.luau +55 -0
- package/out/internal/network/port.d.ts +1 -0
- package/out/internal/network/port.luau +19 -0
- package/out/internal/network/roblox-network-port.d.ts +1 -0
- package/out/internal/network/roblox-network-port.luau +105 -0
- package/out/internal/network/schema.d.ts +72 -0
- package/out/internal/network/schema.luau +386 -0
- package/out/internal/network/server-session.d.ts +1 -0
- package/out/internal/network/server-session.luau +395 -0
- package/out/internal/network/session.d.ts +1 -0
- package/out/internal/network/session.luau +266 -0
- package/out/internal/reactive/context.d.ts +1 -0
- package/out/internal/reactive/context.luau +149 -0
- package/out/internal/reactive/flush.d.ts +1 -0
- package/out/internal/reactive/flush.luau +355 -0
- package/out/internal/reactive/graph.d.ts +1 -0
- package/out/internal/reactive/graph.luau +626 -0
- package/out/internal/reactive/limits.d.ts +1 -0
- package/out/internal/reactive/limits.luau +54 -0
- package/out/internal/reactive/ownership.d.ts +1 -0
- package/out/internal/reactive/ownership.luau +626 -0
- package/out/internal/registry.luau +10 -1
- package/out/internal/sync/client-sync.d.ts +1 -0
- package/out/internal/sync/client-sync.luau +157 -0
- package/out/internal/sync/declarations.d.ts +1 -0
- package/out/internal/sync/declarations.luau +276 -0
- package/out/internal/sync/frame.d.ts +1 -0
- package/out/internal/sync/frame.luau +91 -0
- package/out/internal/sync/server-sync.d.ts +1 -0
- package/out/internal/sync/server-sync.luau +502 -0
- package/out/internal/sync/values.d.ts +1 -0
- package/out/internal/sync/values.luau +930 -0
- package/out/maths/spline/curve.d.ts +35 -0
- package/out/maths/spline/curve.luau +1122 -0
- package/out/maths/spline/index.d.ts +2 -0
- package/out/maths/spline/init.luau +8 -0
- package/out/network.d.ts +66 -0
- package/out/network.luau +24 -0
- package/out/reactive.d.ts +76 -0
- package/out/reactive.luau +136 -0
- package/out/replicated.d.ts +38 -0
- package/out/replicated.luau +45 -0
- package/out/server/data.d.ts +54 -0
- package/out/server/data.luau +44 -0
- package/out/server/index.d.ts +17 -0
- package/out/server/init.luau +58 -4
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/testing/index.d.ts +38 -0
- package/out/testing/init.luau +189 -0
- package/out/util/random/index.d.ts +24 -0
- package/out/util/random/init.luau +301 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +28 -2
- package/testing/package.json +4 -0
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/index.js +87 -23
- package/transformer/network.cjs +961 -0
- package/util/random/package.json +4 -0
- package/vide/package.json +4 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local _curve = TS.import(script, script, "curve")
|
|
5
|
+
exports.catmullRom = _curve.catmullRom
|
|
6
|
+
exports.cubicBezier = _curve.cubicBezier
|
|
7
|
+
exports.uniformBSpline = _curve.uniformBSpline
|
|
8
|
+
return exports
|
package/out/network.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
declare const message: unique symbol;
|
|
2
|
+
declare const width: unique symbol;
|
|
3
|
+
export type f64 = number & {
|
|
4
|
+
readonly [width]?: "f64";
|
|
5
|
+
};
|
|
6
|
+
export type f32 = number & {
|
|
7
|
+
readonly [width]?: "f32";
|
|
8
|
+
};
|
|
9
|
+
export type u8 = number & {
|
|
10
|
+
readonly [width]?: "u8";
|
|
11
|
+
};
|
|
12
|
+
export type u16 = number & {
|
|
13
|
+
readonly [width]?: "u16";
|
|
14
|
+
};
|
|
15
|
+
export type u32 = number & {
|
|
16
|
+
readonly [width]?: "u32";
|
|
17
|
+
};
|
|
18
|
+
export interface ToServer<P> {
|
|
19
|
+
readonly [message]: {
|
|
20
|
+
readonly kind: "to-server";
|
|
21
|
+
readonly payload: (value: P) => P;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface ToClient<P> {
|
|
25
|
+
readonly [message]: {
|
|
26
|
+
readonly kind: "to-client";
|
|
27
|
+
readonly payload: (value: P) => P;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface AskServer<Request, Response> {
|
|
31
|
+
readonly [message]: {
|
|
32
|
+
readonly kind: "ask-server";
|
|
33
|
+
readonly request: (value: Request) => Request;
|
|
34
|
+
readonly response: (value: Response) => Response;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ClientReady {
|
|
38
|
+
readonly [message]: {
|
|
39
|
+
readonly kind: "client-ready";
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export declare const ClientReady: ClientReady;
|
|
43
|
+
export interface Receiver {
|
|
44
|
+
readonly [message]: {
|
|
45
|
+
readonly kind: "receiver";
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export type PayloadArgs<P> = [P] extends [undefined] ? [] : [payload: P];
|
|
49
|
+
export type AskFailure = "timeout" | "disconnected" | "unavailable" | "handler-error" | "overloaded" | "invalid";
|
|
50
|
+
export type AskResult<T> = {
|
|
51
|
+
readonly ok: true;
|
|
52
|
+
readonly value: T;
|
|
53
|
+
} | {
|
|
54
|
+
readonly ok: false;
|
|
55
|
+
readonly reason: AskFailure;
|
|
56
|
+
};
|
|
57
|
+
export interface AskOptions {
|
|
58
|
+
readonly timeout?: number;
|
|
59
|
+
}
|
|
60
|
+
export type ReceiveTarget = ToServer<any> | ToClient<any> | AskServer<any, any> | ClientReady;
|
|
61
|
+
export type ReceiveCallback<T extends ReceiveTarget> = T extends ToServer<infer P> ? (player: Player, ...payload: PayloadArgs<P>) => void : T extends AskServer<infer Request, infer Response> ? (player: Player, ...request: PayloadArgs<Request>) => Response : T extends ToClient<infer P> ? (...payload: PayloadArgs<P>) => void : (player: Player) => (() => void) | void;
|
|
62
|
+
export declare function toServer<P>(_name: string): ToServer<P>;
|
|
63
|
+
export declare function toClient<P>(_name: string): ToClient<P>;
|
|
64
|
+
export declare function askServer<Request, Response>(_name: string, _options?: AskOptions): AskServer<Request, Response>;
|
|
65
|
+
export declare function receive<T extends ReceiveTarget>(_target: T, _callback: ReceiveCallback<T>): Receiver;
|
|
66
|
+
export {};
|
package/out/network.luau
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
-- Function-valued phantom positions keep every payload type invariant under strictFunctionTypes.
|
|
3
|
+
local ClientReady = table.freeze({})
|
|
4
|
+
-- `any` is the only argument every invariant token is assignable to.
|
|
5
|
+
local UNTRANSFORMED = "Crucible networking needs the @trioforge/crucible/transformer plugin"
|
|
6
|
+
local function toServer(_name)
|
|
7
|
+
error(UNTRANSFORMED)
|
|
8
|
+
end
|
|
9
|
+
local function toClient(_name)
|
|
10
|
+
error(UNTRANSFORMED)
|
|
11
|
+
end
|
|
12
|
+
local function askServer(_name, _options)
|
|
13
|
+
error(UNTRANSFORMED)
|
|
14
|
+
end
|
|
15
|
+
local function receive(_target, _callback)
|
|
16
|
+
error(UNTRANSFORMED)
|
|
17
|
+
end
|
|
18
|
+
return {
|
|
19
|
+
toServer = toServer,
|
|
20
|
+
toClient = toClient,
|
|
21
|
+
askServer = askServer,
|
|
22
|
+
receive = receive,
|
|
23
|
+
ClientReady = ClientReady,
|
|
24
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
declare const stateKind: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* A value read by calling it. Any function of no arguments is one, so a getter that reads state, such as
|
|
4
|
+
* `() => this.open() && !this.locked()`, tracks like a state or a computed.
|
|
5
|
+
*/
|
|
6
|
+
export interface Readable<T> {
|
|
7
|
+
(): T;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A writable local value. `set` stores its argument as is, and `update` stores what its callback returns for the
|
|
11
|
+
* current value. A write equal (`rawequal`) to the current value does nothing, so replace tables instead of
|
|
12
|
+
* mutating them. At runtime a State is a callable table, not a function: pass `() => s()` to code that tests
|
|
13
|
+
* `type(x) == "function"`.
|
|
14
|
+
*/
|
|
15
|
+
export interface State<T> extends Readable<T> {
|
|
16
|
+
readonly [stateKind]: {
|
|
17
|
+
readonly value: (value: T) => T;
|
|
18
|
+
};
|
|
19
|
+
set(value: T): void;
|
|
20
|
+
update(change: (current: T) => T): void;
|
|
21
|
+
}
|
|
22
|
+
/** A local source of truth. It needs no owner: a field initializer, a local or a module constant. */
|
|
23
|
+
export declare function state<T>(initial: T): State<T>;
|
|
24
|
+
/**
|
|
25
|
+
* A cached value derived from other readables. It evaluates on its first read and again only when read after a
|
|
26
|
+
* dependency changed. A result equal to the cached one stops propagation. A throw or a yield is cached and rethrown
|
|
27
|
+
* to every reader until a dependency changes. Writing state or creating an effect, watch, scope or cleanup inside
|
|
28
|
+
* `read` throws. It needs no owner.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computed<T>(read: () => T): Readable<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Keeps something outside the graph in step with state. The body runs on the next reactive flush, never inside
|
|
33
|
+
* the call that created it, and again after anything it read changed. It may return a cleanup, which runs before
|
|
34
|
+
* the next run and when the effect ends. It belongs to the code that created it: inside `onEnable` it ends at
|
|
35
|
+
* disable; otherwise it ends with its object, its parent effect run or its scope. A throw, a yield or another
|
|
36
|
+
* return value stops it for good, after what its run created is released. With no owner it throws.
|
|
37
|
+
*/
|
|
38
|
+
export declare function effect(body: () => void | (() => void)): void;
|
|
39
|
+
/**
|
|
40
|
+
* Calls `onChange(value, previous)` on the flushes where `source()` differs from the last value seen. The source is
|
|
41
|
+
* read now as the baseline, so a change before the first flush is seen. It observes the value at each flush, not
|
|
42
|
+
* each write. Owned and stopped like `effect`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function watch<T>(source: Readable<T>, onChange: (value: T, previous: T) => void): void;
|
|
45
|
+
/** Calls `read` without tracking what it reads. Ownership is unchanged. */
|
|
46
|
+
export declare function untracked<T>(read: () => T): T;
|
|
47
|
+
/**
|
|
48
|
+
* Runs `setup` now as a new lifetime that owns every effect, watch, scope and cleanup created inside it. Returns
|
|
49
|
+
* an idempotent dispose. Inside another owner it also ends when that owner ends; outside every owner it is a root
|
|
50
|
+
* that only its dispose ends. If `setup` throws or yields, what it created ends and the error is rethrown.
|
|
51
|
+
*/
|
|
52
|
+
export declare function scope(setup: () => void): () => void;
|
|
53
|
+
/**
|
|
54
|
+
* Registers `release` to run once when the current owner ends: after the owner's effects and scopes ended, newest
|
|
55
|
+
* release first. Under an owner that already ended it runs at once. With no owner it throws.
|
|
56
|
+
*/
|
|
57
|
+
export declare function onCleanup(release: () => void): void;
|
|
58
|
+
/**
|
|
59
|
+
* What `own` releases: a function, called with no arguments; a connection, disconnected; an Instance, destroyed; a
|
|
60
|
+
* thread, cancelled with `task.cancel`; or an object, through its `destroy()` or else its `Destroy()`. A `State` is a
|
|
61
|
+
* callable table, not a function, so it is not one.
|
|
62
|
+
*/
|
|
63
|
+
export type Ownable = ((() => void) & {
|
|
64
|
+
readonly [stateKind]?: never;
|
|
65
|
+
}) | RBXScriptConnection | Instance | thread | {
|
|
66
|
+
destroy(): void;
|
|
67
|
+
} | {
|
|
68
|
+
Destroy(): void;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Releases `value` once when the current owner ends, exactly as `onCleanup` would release a function that does it,
|
|
72
|
+
* and returns `value`. How it is released is decided now, from its type. Under an owner that already ended it is
|
|
73
|
+
* released at once. With no owner it throws, and so does a value that is not `Ownable`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function own<T extends Ownable>(value: T): T;
|
|
76
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _flush = TS.import(script, script.Parent, "internal", "reactive", "flush")
|
|
4
|
+
local createEffect = _flush.createEffect
|
|
5
|
+
local createWatch = _flush.createWatch
|
|
6
|
+
local _graph = TS.import(script, script.Parent, "internal", "reactive", "graph")
|
|
7
|
+
local createComputedNode = _graph.createComputedNode
|
|
8
|
+
local createStateNode = _graph.createStateNode
|
|
9
|
+
local readComputed = _graph.readComputed
|
|
10
|
+
local readState = _graph.readState
|
|
11
|
+
local readUntracked = _graph.readUntracked
|
|
12
|
+
local update = _graph.update
|
|
13
|
+
local write = _graph.write
|
|
14
|
+
local _ownership = TS.import(script, script.Parent, "internal", "reactive", "ownership")
|
|
15
|
+
local createScope = _ownership.createScope
|
|
16
|
+
local registerCleanup = _ownership.registerCleanup
|
|
17
|
+
local registerOwned = _ownership.registerOwned
|
|
18
|
+
--[[
|
|
19
|
+
*
|
|
20
|
+
* A value read by calling it. Any function of no arguments is one, so a getter that reads state, such as
|
|
21
|
+
* `() => this.open() && !this.locked()`, tracks like a state or a computed.
|
|
22
|
+
|
|
23
|
+
]]
|
|
24
|
+
--[[
|
|
25
|
+
*
|
|
26
|
+
* A writable local value. `set` stores its argument as is, and `update` stores what its callback returns for the
|
|
27
|
+
* current value. A write equal (`rawequal`) to the current value does nothing, so replace tables instead of
|
|
28
|
+
* mutating them. At runtime a State is a callable table, not a function: pass `() => s()` to code that tests
|
|
29
|
+
* `type(x) == "function"`.
|
|
30
|
+
|
|
31
|
+
]]
|
|
32
|
+
local stateMethods = {
|
|
33
|
+
set = function(self, value)
|
|
34
|
+
write(self, value)
|
|
35
|
+
end,
|
|
36
|
+
update = function(self, change)
|
|
37
|
+
update(self, change)
|
|
38
|
+
end,
|
|
39
|
+
}
|
|
40
|
+
local stateMeta = {
|
|
41
|
+
__index = stateMethods,
|
|
42
|
+
__call = function(node)
|
|
43
|
+
return readState(node)
|
|
44
|
+
end,
|
|
45
|
+
}
|
|
46
|
+
--* A local source of truth. It needs no owner: a field initializer, a local or a module constant.
|
|
47
|
+
local function state(initial)
|
|
48
|
+
return setmetatable(createStateNode(initial), stateMeta)
|
|
49
|
+
end
|
|
50
|
+
--[[
|
|
51
|
+
*
|
|
52
|
+
* A cached value derived from other readables. It evaluates on its first read and again only when read after a
|
|
53
|
+
* dependency changed. A result equal to the cached one stops propagation. A throw or a yield is cached and rethrown
|
|
54
|
+
* to every reader until a dependency changes. Writing state or creating an effect, watch, scope or cleanup inside
|
|
55
|
+
* `read` throws. It needs no owner.
|
|
56
|
+
|
|
57
|
+
]]
|
|
58
|
+
local function computed(read)
|
|
59
|
+
local node = createComputedNode(read)
|
|
60
|
+
return function()
|
|
61
|
+
return readComputed(node)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
--[[
|
|
65
|
+
*
|
|
66
|
+
* Keeps something outside the graph in step with state. The body runs on the next reactive flush, never inside
|
|
67
|
+
* the call that created it, and again after anything it read changed. It may return a cleanup, which runs before
|
|
68
|
+
* the next run and when the effect ends. It belongs to the code that created it: inside `onEnable` it ends at
|
|
69
|
+
* disable; otherwise it ends with its object, its parent effect run or its scope. A throw, a yield or another
|
|
70
|
+
* return value stops it for good, after what its run created is released. With no owner it throws.
|
|
71
|
+
|
|
72
|
+
]]
|
|
73
|
+
local function effect(body)
|
|
74
|
+
createEffect(body)
|
|
75
|
+
end
|
|
76
|
+
--[[
|
|
77
|
+
*
|
|
78
|
+
* Calls `onChange(value, previous)` on the flushes where `source()` differs from the last value seen. The source is
|
|
79
|
+
* read now as the baseline, so a change before the first flush is seen. It observes the value at each flush, not
|
|
80
|
+
* each write. Owned and stopped like `effect`.
|
|
81
|
+
|
|
82
|
+
]]
|
|
83
|
+
local function watch(source, onChange)
|
|
84
|
+
createWatch(source, onChange)
|
|
85
|
+
end
|
|
86
|
+
--* Calls `read` without tracking what it reads. Ownership is unchanged.
|
|
87
|
+
local function untracked(read)
|
|
88
|
+
return readUntracked(read)
|
|
89
|
+
end
|
|
90
|
+
--[[
|
|
91
|
+
*
|
|
92
|
+
* Runs `setup` now as a new lifetime that owns every effect, watch, scope and cleanup created inside it. Returns
|
|
93
|
+
* an idempotent dispose. Inside another owner it also ends when that owner ends; outside every owner it is a root
|
|
94
|
+
* that only its dispose ends. If `setup` throws or yields, what it created ends and the error is rethrown.
|
|
95
|
+
|
|
96
|
+
]]
|
|
97
|
+
local function scope(setup)
|
|
98
|
+
return createScope(setup)
|
|
99
|
+
end
|
|
100
|
+
--[[
|
|
101
|
+
*
|
|
102
|
+
* Registers `release` to run once when the current owner ends: after the owner's effects and scopes ended, newest
|
|
103
|
+
* release first. Under an owner that already ended it runs at once. With no owner it throws.
|
|
104
|
+
|
|
105
|
+
]]
|
|
106
|
+
local function onCleanup(release)
|
|
107
|
+
registerCleanup(release)
|
|
108
|
+
end
|
|
109
|
+
--[[
|
|
110
|
+
*
|
|
111
|
+
* What `own` releases: a function, called with no arguments; a connection, disconnected; an Instance, destroyed; a
|
|
112
|
+
* thread, cancelled with `task.cancel`; or an object, through its `destroy()` or else its `Destroy()`. A `State` is a
|
|
113
|
+
* callable table, not a function, so it is not one.
|
|
114
|
+
|
|
115
|
+
]]
|
|
116
|
+
--[[
|
|
117
|
+
*
|
|
118
|
+
* Releases `value` once when the current owner ends, exactly as `onCleanup` would release a function that does it,
|
|
119
|
+
* and returns `value`. How it is released is decided now, from its type. Under an owner that already ended it is
|
|
120
|
+
* released at once. With no owner it throws, and so does a value that is not `Ownable`.
|
|
121
|
+
|
|
122
|
+
]]
|
|
123
|
+
local function own(value)
|
|
124
|
+
registerOwned(value)
|
|
125
|
+
return value
|
|
126
|
+
end
|
|
127
|
+
return {
|
|
128
|
+
state = state,
|
|
129
|
+
computed = computed,
|
|
130
|
+
effect = effect,
|
|
131
|
+
watch = watch,
|
|
132
|
+
untracked = untracked,
|
|
133
|
+
scope = scope,
|
|
134
|
+
onCleanup = onCleanup,
|
|
135
|
+
own = own,
|
|
136
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Readable } from "./reactive";
|
|
2
|
+
declare const replicatedKind: unique symbol;
|
|
3
|
+
/** Whether a replicated value holds what the server published: before the first frame, after it, or failed. */
|
|
4
|
+
export type SyncStatus = "pending" | "ready" | "failed";
|
|
5
|
+
/**
|
|
6
|
+
* A value every client receives. On the server it reads the authoritative value; on a client, the last value the
|
|
7
|
+
* server sent, or `initial` before the first frame. At runtime it is a plain function, so it composes like any getter.
|
|
8
|
+
*/
|
|
9
|
+
export interface Replicated<T> extends Readable<T> {
|
|
10
|
+
readonly [replicatedKind]: {
|
|
11
|
+
readonly audience: "all";
|
|
12
|
+
readonly value: (value: T) => T;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A value each player receives privately. On a client it reads the local player's value, or `initial` before the
|
|
17
|
+
* first frame. On the server there is no single value, so calling it throws.
|
|
18
|
+
*/
|
|
19
|
+
export interface ReplicatedPerPlayer<T> extends Readable<T> {
|
|
20
|
+
readonly [replicatedKind]: {
|
|
21
|
+
readonly audience: "player";
|
|
22
|
+
readonly value: (value: T) => T;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Declares a value every client receives. Write it as the initializer of an exported top-level const in a shared
|
|
27
|
+
* module, with an explicit type argument and a literal name. The server writes it through `publish`; fields the type
|
|
28
|
+
* does not declare are never sent.
|
|
29
|
+
*/
|
|
30
|
+
export declare function replicated<T = never>(_name: string, _initial: NoInfer<T>): Replicated<T>;
|
|
31
|
+
/** Declares a value each player receives privately. The server supplies it with `publish(declaration, project)`. */
|
|
32
|
+
export declare function replicatedPerPlayer<T = never>(_name: string, _initial: NoInfer<T>): ReplicatedPerPlayer<T>;
|
|
33
|
+
/**
|
|
34
|
+
* The declaration's sync status. Inside an effect or a computed it tracks like a value. On the server, a per-player
|
|
35
|
+
* declaration throws.
|
|
36
|
+
*/
|
|
37
|
+
export declare function syncStatus<T>(declaration: Replicated<T> | ReplicatedPerPlayer<T>): SyncStatus;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local statusOf = TS.import(script, script.Parent, "internal", "sync", "declarations").statusOf
|
|
4
|
+
--* Whether a replicated value holds what the server published: before the first frame, after it, or failed.
|
|
5
|
+
--[[
|
|
6
|
+
*
|
|
7
|
+
* A value every client receives. On the server it reads the authoritative value; on a client, the last value the
|
|
8
|
+
* server sent, or `initial` before the first frame. At runtime it is a plain function, so it composes like any getter.
|
|
9
|
+
|
|
10
|
+
]]
|
|
11
|
+
--[[
|
|
12
|
+
*
|
|
13
|
+
* A value each player receives privately. On a client it reads the local player's value, or `initial` before the
|
|
14
|
+
* first frame. On the server there is no single value, so calling it throws.
|
|
15
|
+
|
|
16
|
+
]]
|
|
17
|
+
local UNTRANSFORMED = "Crucible replicated state needs the @trioforge/crucible/transformer plugin"
|
|
18
|
+
--[[
|
|
19
|
+
*
|
|
20
|
+
* Declares a value every client receives. Write it as the initializer of an exported top-level const in a shared
|
|
21
|
+
* module, with an explicit type argument and a literal name. The server writes it through `publish`; fields the type
|
|
22
|
+
* does not declare are never sent.
|
|
23
|
+
|
|
24
|
+
]]
|
|
25
|
+
local function replicated(_name, _initial)
|
|
26
|
+
error(UNTRANSFORMED)
|
|
27
|
+
end
|
|
28
|
+
--* Declares a value each player receives privately. The server supplies it with `publish(declaration, project)`.
|
|
29
|
+
local function replicatedPerPlayer(_name, _initial)
|
|
30
|
+
error(UNTRANSFORMED)
|
|
31
|
+
end
|
|
32
|
+
--[[
|
|
33
|
+
*
|
|
34
|
+
* The declaration's sync status. Inside an effect or a computed it tracks like a value. On the server, a per-player
|
|
35
|
+
* declaration throws.
|
|
36
|
+
|
|
37
|
+
]]
|
|
38
|
+
local function syncStatus(declaration)
|
|
39
|
+
return statusOf(declaration)
|
|
40
|
+
end
|
|
41
|
+
return {
|
|
42
|
+
replicated = replicated,
|
|
43
|
+
replicatedPerPlayer = replicatedPerPlayer,
|
|
44
|
+
syncStatus = syncStatus,
|
|
45
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Migration, OperationId, ProfileMessage, StoreFailure, StoreResult } from "../data";
|
|
2
|
+
declare const recordKind: unique symbol;
|
|
3
|
+
export interface StoreOptions<T> {
|
|
4
|
+
/**
|
|
5
|
+
* A new record's value, and the value of a missing required key on an object path from the root. It never fills an
|
|
6
|
+
* array element, a map value or an optional key of an existing record. Admitted and frozen once.
|
|
7
|
+
*/
|
|
8
|
+
readonly defaults: T;
|
|
9
|
+
/** Append-only. Removing or reordering a step changes what every older record becomes. */
|
|
10
|
+
readonly migrations?: readonly Migration[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A generic update's result. `replayed` is true when the operation id was already recorded for the key: `change` did
|
|
14
|
+
* not run, and `value` is the current stored record (the defaults when the key holds no payload), which may include
|
|
15
|
+
* later writers' changes. A marker stores no result, so the original one cannot be returned.
|
|
16
|
+
*/
|
|
17
|
+
export type UpdateResult<T> = {
|
|
18
|
+
readonly ok: true;
|
|
19
|
+
readonly value: T;
|
|
20
|
+
readonly replayed: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
readonly ok: false;
|
|
23
|
+
readonly reason: StoreFailure;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Keyed records with no session. Each call is one DataStore request that yields and may fail, so the surface is two
|
|
27
|
+
* calls with result unions, not live handles.
|
|
28
|
+
*/
|
|
29
|
+
export interface DataStore<T> {
|
|
30
|
+
readonly [recordKind]: {
|
|
31
|
+
readonly kind: "records";
|
|
32
|
+
readonly value: (value: T) => T;
|
|
33
|
+
};
|
|
34
|
+
/** Yields. The stored record, migrated and admitted, or undefined when the key has none. */
|
|
35
|
+
read(key: string): StoreResult<T | undefined>;
|
|
36
|
+
/**
|
|
37
|
+
* Yields. One atomic change; `change` may run more than once, so it must be pure. A replayed id returns the current
|
|
38
|
+
* record with `replayed: true`. A throw in `change` cancels the write and is rethrown here.
|
|
39
|
+
*/
|
|
40
|
+
update(key: string, change: (current: T) => T, id?: OperationId): UpdateResult<T>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Declares keyed records by their DataStore name. It must initialize an exported top-level const in a server module,
|
|
44
|
+
* with `T` explicit. `T` is an object or a string-keyed record at its root.
|
|
45
|
+
*/
|
|
46
|
+
export declare function dataStore<T extends object>(_name: string, _options: StoreOptions<NoInfer<T>>): DataStore<T>;
|
|
47
|
+
/** A fresh operation identity. Create it once per logical operation and reuse it for that operation's retries. */
|
|
48
|
+
export declare function operationId(): OperationId;
|
|
49
|
+
/**
|
|
50
|
+
* Declares a durable message to a player's profile. It must initialize an exported top-level const in a server module,
|
|
51
|
+
* with the payload type explicit. A payload type changes only by adding optional fields.
|
|
52
|
+
*/
|
|
53
|
+
export declare function profileMessage<P>(_name: string): ProfileMessage<P>;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local newOperationId = TS.import(script, script.Parent.Parent, "internal", "data", "operation-id").newOperationId
|
|
4
|
+
local dataPort = TS.import(script, script.Parent.Parent, "internal", "data", "port").dataPort
|
|
5
|
+
--[[
|
|
6
|
+
*
|
|
7
|
+
* A generic update's result. `replayed` is true when the operation id was already recorded for the key: `change` did
|
|
8
|
+
* not run, and `value` is the current stored record (the defaults when the key holds no payload), which may include
|
|
9
|
+
* later writers' changes. A marker stores no result, so the original one cannot be returned.
|
|
10
|
+
|
|
11
|
+
]]
|
|
12
|
+
--[[
|
|
13
|
+
*
|
|
14
|
+
* Keyed records with no session. Each call is one DataStore request that yields and may fail, so the surface is two
|
|
15
|
+
* calls with result unions, not live handles.
|
|
16
|
+
|
|
17
|
+
]]
|
|
18
|
+
--[[
|
|
19
|
+
*
|
|
20
|
+
* Declares keyed records by their DataStore name. It must initialize an exported top-level const in a server module,
|
|
21
|
+
* with `T` explicit. `T` is an object or a string-keyed record at its root.
|
|
22
|
+
|
|
23
|
+
]]
|
|
24
|
+
local function dataStore(_name, _options)
|
|
25
|
+
error("Crucible data needs the @trioforge/crucible/transformer plugin")
|
|
26
|
+
end
|
|
27
|
+
--* A fresh operation identity. Create it once per logical operation and reuse it for that operation's retries.
|
|
28
|
+
local function operationId()
|
|
29
|
+
return newOperationId(dataPort())
|
|
30
|
+
end
|
|
31
|
+
--[[
|
|
32
|
+
*
|
|
33
|
+
* Declares a durable message to a player's profile. It must initialize an exported top-level const in a server module,
|
|
34
|
+
* with the payload type explicit. A payload type changes only by adding optional fields.
|
|
35
|
+
|
|
36
|
+
]]
|
|
37
|
+
local function profileMessage(_name)
|
|
38
|
+
error("Crucible data needs the @trioforge/crucible/transformer plugin")
|
|
39
|
+
end
|
|
40
|
+
return {
|
|
41
|
+
dataStore = dataStore,
|
|
42
|
+
operationId = operationId,
|
|
43
|
+
profileMessage = profileMessage,
|
|
44
|
+
}
|
package/out/server/index.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import type { BootOptions } from "../index";
|
|
2
|
+
import type { PayloadArgs, ToClient } from "../network";
|
|
3
|
+
import "../internal/data/record-bindings";
|
|
4
|
+
export declare const BOOTED_ATTRIBUTE = "CrucibleBooted";
|
|
5
|
+
export { config, playerConfig, configReady, refreshConfigs } from "../configs";
|
|
6
|
+
export type { Config, PlayerConfig } from "../configs";
|
|
7
|
+
export { publish } from "./publish";
|
|
8
|
+
export type { Publication } from "./publish";
|
|
9
|
+
export { dataStore, operationId, profileMessage } from "./data";
|
|
10
|
+
export type { DataStore, StoreOptions, UpdateResult } from "./data";
|
|
11
|
+
export type { OperationId, StoreFailure, StoreResult } from "../data";
|
|
12
|
+
export type { ProfileMessage, ProfileClaim, LoadReason, ReleaseReason, Purchase, AfterGrant, QuarantinedMessage, SaveResult, SaveFailure, } from "../data";
|
|
2
13
|
export declare function boot(options: BootOptions): void;
|
|
14
|
+
export declare namespace Network {
|
|
15
|
+
/** True when accepted locally: fired now, or queued before the player's client is ready. Never delivery. */
|
|
16
|
+
function send<P>(player: Player, message: ToClient<P>, ...payload: PayloadArgs<NoInfer<P>>): boolean;
|
|
17
|
+
/** Encodes once and delivers the same packet to every player in the game now. */
|
|
18
|
+
function broadcast<P>(message: ToClient<P>, ...payload: PayloadArgs<NoInfer<P>>): void;
|
|
19
|
+
}
|
package/out/server/init.luau
CHANGED
|
@@ -1,16 +1,70 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
3
4
|
local bootSide = TS.import(script, script.Parent, "internal", "boot").boot
|
|
5
|
+
local installConfigRuntime = TS.import(script, script.Parent, "internal", "configs", "runtime").installConfigRuntime
|
|
6
|
+
local networkPort = TS.import(script, script.Parent, "internal", "network", "port").networkPort
|
|
7
|
+
local ServerSession = TS.import(script, script.Parent, "internal", "network", "server-session").ServerSession
|
|
8
|
+
local SyncFrameMessage = TS.import(script, script.Parent, "internal", "sync", "frame").SyncFrameMessage
|
|
9
|
+
local installServerSync = TS.import(script, script.Parent, "internal", "sync", "server-sync").installServerSync
|
|
10
|
+
TS.import(script, script.Parent, "internal", "data", "record-bindings")
|
|
11
|
+
local installDataRuntime = TS.import(script, script.Parent, "internal", "data", "runtime").installDataRuntime
|
|
4
12
|
if not game:GetService("RunService"):IsServer() then
|
|
5
13
|
error("@trioforge/crucible/server loaded on the client")
|
|
6
14
|
end
|
|
15
|
+
local session
|
|
16
|
+
local BOOTED_ATTRIBUTE = "CrucibleBooted"
|
|
17
|
+
local _configs = TS.import(script, script.Parent, "configs")
|
|
18
|
+
exports.config = _configs.config
|
|
19
|
+
exports.playerConfig = _configs.playerConfig
|
|
20
|
+
exports.configReady = _configs.configReady
|
|
21
|
+
exports.refreshConfigs = _configs.refreshConfigs
|
|
22
|
+
exports.publish = TS.import(script, script, "publish").publish
|
|
23
|
+
local _data = TS.import(script, script, "data")
|
|
24
|
+
exports.dataStore = _data.dataStore
|
|
25
|
+
exports.operationId = _data.operationId
|
|
26
|
+
exports.profileMessage = _data.profileMessage
|
|
27
|
+
local function active()
|
|
28
|
+
return session or error("Crucible networking is not ready; send from a constructor, a hook, or later", 0)
|
|
29
|
+
end
|
|
30
|
+
installConfigRuntime()
|
|
31
|
+
installDataRuntime()
|
|
7
32
|
local function boot(options)
|
|
33
|
+
local network = ServerSession.new(networkPort())
|
|
34
|
+
session = network
|
|
35
|
+
installServerSync(function(player, frame)
|
|
36
|
+
return active():send(player, SyncFrameMessage, frame)
|
|
37
|
+
end)
|
|
8
38
|
bootSide({
|
|
9
39
|
name = "server",
|
|
10
40
|
renders = false,
|
|
11
|
-
booted = function()
|
|
41
|
+
booted = function()
|
|
42
|
+
local service = game:GetService("ServerScriptService")
|
|
43
|
+
service:SetAttribute(BOOTED_ATTRIBUTE, true)
|
|
44
|
+
return function()
|
|
45
|
+
return service:SetAttribute(BOOTED_ATTRIBUTE, nil)
|
|
46
|
+
end
|
|
47
|
+
end,
|
|
48
|
+
network = network,
|
|
12
49
|
}, options)
|
|
13
50
|
end
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
51
|
+
local Network = {}
|
|
52
|
+
do
|
|
53
|
+
local _container = Network
|
|
54
|
+
--* True when accepted locally: fired now, or queued before the player's client is ready. Never delivery.
|
|
55
|
+
local function send(player, message, ...)
|
|
56
|
+
local payload = { ... }
|
|
57
|
+
return active():send(player, message, payload[1])
|
|
58
|
+
end
|
|
59
|
+
_container.send = send
|
|
60
|
+
--* Encodes once and delivers the same packet to every player in the game now.
|
|
61
|
+
local function broadcast(message, ...)
|
|
62
|
+
local payload = { ... }
|
|
63
|
+
active():broadcast(message, payload[1])
|
|
64
|
+
end
|
|
65
|
+
_container.broadcast = broadcast
|
|
66
|
+
end
|
|
67
|
+
exports.boot = boot
|
|
68
|
+
exports.BOOTED_ATTRIBUTE = BOOTED_ATTRIBUTE
|
|
69
|
+
exports.Network = Network
|
|
70
|
+
return exports
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { State } from "../reactive";
|
|
2
|
+
import type { Replicated, ReplicatedPerPlayer } from "../replicated";
|
|
3
|
+
declare const publication: unique symbol;
|
|
4
|
+
/** What a per-player `publish` returns: an opaque claim that holds nothing callable. */
|
|
5
|
+
export interface Publication {
|
|
6
|
+
readonly [publication]: true;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Takes the write side of a value every client sees. Call it while a server Subsystem is constructed, as a field
|
|
10
|
+
* initializer: `private readonly phase = publish(JobPhase)`. `set` and `update` drop fields the type does not
|
|
11
|
+
* declare, check and freeze the rest, and throw at a write that cannot replicate. One Subsystem publishes each value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function publish<T>(target: Replicated<T>): State<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Supplies a per-player value as a projection. For each player whose client is ready, it is evaluated like a
|
|
16
|
+
* `computed`, admitted on every pull and sent to that player alone. It must be pure and must not yield. A throw or a
|
|
17
|
+
* rejected value marks that player's value failed until it next changes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function publish<T>(target: ReplicatedPerPlayer<T>, project: (player: Player) => T): Publication;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local claimPublication = TS.import(script, script.Parent.Parent, "internal", "sync", "declarations").claimPublication
|
|
4
|
+
--* What a per-player `publish` returns: an opaque claim that holds nothing callable.
|
|
5
|
+
--[[
|
|
6
|
+
*
|
|
7
|
+
* Takes the write side of a value every client sees. Call it while a server Subsystem is constructed, as a field
|
|
8
|
+
* initializer: `private readonly phase = publish(JobPhase)`. `set` and `update` drop fields the type does not
|
|
9
|
+
* declare, check and freeze the rest, and throw at a write that cannot replicate. One Subsystem publishes each value.
|
|
10
|
+
|
|
11
|
+
]]
|
|
12
|
+
--[[
|
|
13
|
+
*
|
|
14
|
+
* Supplies a per-player value as a projection. For each player whose client is ready, it is evaluated like a
|
|
15
|
+
* `computed`, admitted on every pull and sent to that player alone. It must be pure and must not yield. A throw or a
|
|
16
|
+
* rejected value marks that player's value failed until it next changes.
|
|
17
|
+
|
|
18
|
+
]]
|
|
19
|
+
local function publish(target, project)
|
|
20
|
+
return claimPublication(target, project)
|
|
21
|
+
end
|
|
22
|
+
return {
|
|
23
|
+
publish = publish,
|
|
24
|
+
}
|