@trioforge/crucible 0.1.0 → 0.2.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/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 +107 -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 +154 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +403 -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 +393 -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/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 +16 -0
- package/out/server/init.luau +49 -3
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +13 -2
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/index.js +37 -11
- package/transformer/network.cjs +961 -0
- package/vide/package.json +4 -0
package/out/client/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import type { BootOptions } from "../index";
|
|
2
|
+
import type { AskResult, AskServer, PayloadArgs, ToServer } from "../network";
|
|
2
3
|
export declare const BOOTED_ATTRIBUTE = "CrucibleBooted";
|
|
3
4
|
export declare function boot(options: BootOptions): void;
|
|
5
|
+
export declare namespace Network {
|
|
6
|
+
/** True when accepted locally: fired now, or queued until this client is ready. Never delivery. */
|
|
7
|
+
function send<P>(message: ToServer<P>, ...payload: PayloadArgs<NoInfer<P>>): boolean;
|
|
8
|
+
/** Yields until the reply, the declared timeout or the session ending. */
|
|
9
|
+
function ask<Request, Response>(message: AskServer<Request, Response>, ...request: PayloadArgs<NoInfer<Request>>): AskResult<Response>;
|
|
10
|
+
}
|
package/out/client/init.luau
CHANGED
|
@@ -1,20 +1,55 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local bootSide = TS.import(script, script.Parent, "internal", "boot").boot
|
|
4
|
+
local ClientSession = TS.import(script, script.Parent, "internal", "network", "client-session").ClientSession
|
|
5
|
+
local networkPort = TS.import(script, script.Parent, "internal", "network", "port").networkPort
|
|
6
|
+
local installClientSync = TS.import(script, script.Parent, "internal", "sync", "client-sync").installClientSync
|
|
7
|
+
local SyncFaultMessage = TS.import(script, script.Parent, "internal", "sync", "frame").SyncFaultMessage
|
|
4
8
|
if not game:GetService("RunService"):IsClient() then
|
|
5
9
|
error("@trioforge/crucible/client loaded on the server")
|
|
6
10
|
end
|
|
7
11
|
local BOOTED_ATTRIBUTE = "CrucibleBooted"
|
|
12
|
+
local session
|
|
13
|
+
local function active()
|
|
14
|
+
return session or error("Crucible networking is not ready; send from a constructor, a hook, or later", 0)
|
|
15
|
+
end
|
|
8
16
|
local function boot(options)
|
|
17
|
+
local network = ClientSession.new(networkPort())
|
|
18
|
+
session = network
|
|
19
|
+
installClientSync(function(revision)
|
|
20
|
+
return active():send(SyncFaultMessage, revision)
|
|
21
|
+
end)
|
|
9
22
|
bootSide({
|
|
10
23
|
name = "client",
|
|
11
24
|
renders = true,
|
|
12
25
|
booted = function()
|
|
13
|
-
|
|
26
|
+
local player = game:GetService("Players").LocalPlayer
|
|
27
|
+
player:SetAttribute(BOOTED_ATTRIBUTE, true)
|
|
28
|
+
return function()
|
|
29
|
+
return player:SetAttribute(BOOTED_ATTRIBUTE, nil)
|
|
30
|
+
end
|
|
14
31
|
end,
|
|
32
|
+
network = network,
|
|
15
33
|
}, options)
|
|
16
34
|
end
|
|
35
|
+
local Network = {}
|
|
36
|
+
do
|
|
37
|
+
local _container = Network
|
|
38
|
+
--* True when accepted locally: fired now, or queued until this client is ready. Never delivery.
|
|
39
|
+
local function send(message, ...)
|
|
40
|
+
local payload = { ... }
|
|
41
|
+
return active():send(message, payload[1])
|
|
42
|
+
end
|
|
43
|
+
_container.send = send
|
|
44
|
+
--* Yields until the reply, the declared timeout or the session ending.
|
|
45
|
+
local function ask(message, ...)
|
|
46
|
+
local request = { ... }
|
|
47
|
+
return active():ask(message, request[1])
|
|
48
|
+
end
|
|
49
|
+
_container.ask = ask
|
|
50
|
+
end
|
|
17
51
|
return {
|
|
18
52
|
boot = boot,
|
|
19
53
|
BOOTED_ATTRIBUTE = BOOTED_ATTRIBUTE,
|
|
54
|
+
Network = Network,
|
|
20
55
|
}
|
package/out/configs.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare const configKind: unique symbol;
|
|
2
|
+
/** An experience-wide config. Calling it reads the global snapshot's value; it never yields or throws. */
|
|
3
|
+
export interface Config<T> {
|
|
4
|
+
(): T;
|
|
5
|
+
readonly [configKind]: {
|
|
6
|
+
readonly scope: "global";
|
|
7
|
+
readonly value: (value: T) => T;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** A targeted config. Calling it reads that player's value; it never yields or throws. */
|
|
11
|
+
export interface PlayerConfig<T> {
|
|
12
|
+
(player: Player): T;
|
|
13
|
+
readonly [configKind]: {
|
|
14
|
+
readonly scope: "player";
|
|
15
|
+
readonly value: (value: T) => T;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Declares an experience config by its dashboard key, read from the global snapshot. It must initialize an exported
|
|
20
|
+
* top-level const in a server module, with `T` explicit. The fallback is the value before the first fetch and for a
|
|
21
|
+
* missing key.
|
|
22
|
+
*/
|
|
23
|
+
export declare function config<T = never>(_name: string, _fallback: NoInfer<T>): Config<T>;
|
|
24
|
+
/** Declares a targeted config by its dashboard key, read from each player's snapshot. Same form as `config`. */
|
|
25
|
+
export declare function playerConfig<T = never>(_name: string, _fallback: NoInfer<T>): PlayerConfig<T>;
|
|
26
|
+
/**
|
|
27
|
+
* With no argument, true once the global snapshot has been applied. With a player, true once that player's snapshot
|
|
28
|
+
* has been applied, and false before and after they leave.
|
|
29
|
+
*/
|
|
30
|
+
export declare function configReady(player?: Player): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Refreshes every snapshot with newer values, fetches every snapshot that has not loaded, and applies them all in
|
|
33
|
+
* one pass. Yields, at most 10 seconds. True when every snapshot is current; false keeps the last good values.
|
|
34
|
+
* Concurrent calls share one refresh. Crucible never calls it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function refreshConfigs(): boolean;
|
|
37
|
+
export {};
|
package/out/configs.luau
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _runtime = TS.import(script, script.Parent, "internal", "configs", "runtime")
|
|
4
|
+
local ready = _runtime.ready
|
|
5
|
+
local refresh = _runtime.refresh
|
|
6
|
+
--* An experience-wide config. Calling it reads the global snapshot's value; it never yields or throws.
|
|
7
|
+
--* A targeted config. Calling it reads that player's value; it never yields or throws.
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* Declares an experience config by its dashboard key, read from the global snapshot. It must initialize an exported
|
|
11
|
+
* top-level const in a server module, with `T` explicit. The fallback is the value before the first fetch and for a
|
|
12
|
+
* missing key.
|
|
13
|
+
|
|
14
|
+
]]
|
|
15
|
+
local function config(_name, _fallback)
|
|
16
|
+
error("Crucible configs need the @trioforge/crucible/transformer plugin")
|
|
17
|
+
end
|
|
18
|
+
--* Declares a targeted config by its dashboard key, read from each player's snapshot. Same form as `config`.
|
|
19
|
+
local function playerConfig(_name, _fallback)
|
|
20
|
+
error("Crucible configs need the @trioforge/crucible/transformer plugin")
|
|
21
|
+
end
|
|
22
|
+
--[[
|
|
23
|
+
*
|
|
24
|
+
* With no argument, true once the global snapshot has been applied. With a player, true once that player's snapshot
|
|
25
|
+
* has been applied, and false before and after they leave.
|
|
26
|
+
|
|
27
|
+
]]
|
|
28
|
+
local function configReady(player)
|
|
29
|
+
return ready(player)
|
|
30
|
+
end
|
|
31
|
+
--[[
|
|
32
|
+
*
|
|
33
|
+
* Refreshes every snapshot with newer values, fetches every snapshot that has not loaded, and applies them all in
|
|
34
|
+
* one pass. Yields, at most 10 seconds. True when every snapshot is current; false keeps the last good values.
|
|
35
|
+
* Concurrent calls share one refresh. Crucible never calls it.
|
|
36
|
+
|
|
37
|
+
]]
|
|
38
|
+
local function refreshConfigs()
|
|
39
|
+
return refresh()
|
|
40
|
+
end
|
|
41
|
+
return {
|
|
42
|
+
config = config,
|
|
43
|
+
playerConfig = playerConfig,
|
|
44
|
+
configReady = configReady,
|
|
45
|
+
refreshConfigs = refreshConfigs,
|
|
46
|
+
}
|
package/out/data.d.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import type { Readable } from "./reactive";
|
|
2
|
+
declare const operationKind: unique symbol;
|
|
3
|
+
declare const storeKind: unique symbol;
|
|
4
|
+
declare const messageKind: unique symbol;
|
|
5
|
+
declare const claimKind: unique symbol;
|
|
6
|
+
declare const serverOnlyKind: unique symbol;
|
|
7
|
+
/** A value a save can hold: JSON, with absent keys for values that admit `undefined`. */
|
|
8
|
+
export type Json = boolean | number | string | readonly Json[] | JsonRecord;
|
|
9
|
+
/** A saved record: string keys to JSON. */
|
|
10
|
+
export interface JsonRecord {
|
|
11
|
+
readonly [key: string]: Json | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One schema step. It gets a private copy of the stored record, unknown keys included, as plain JSON, and returns a
|
|
15
|
+
* patch: an object merges into the stored object, any other value replaces the stored one, and an omitted key is kept.
|
|
16
|
+
* A step cannot delete a key. Steps are append-only and must be pure.
|
|
17
|
+
*/
|
|
18
|
+
export type Migration = (old: JsonRecord) => JsonRecord;
|
|
19
|
+
/**
|
|
20
|
+
* The identity of one logical operation, kept across its retries. Only `operationId()` makes one; a plain string does
|
|
21
|
+
* not type-check. It carries its creation time, which bounds how long it deduplicates.
|
|
22
|
+
*/
|
|
23
|
+
export type OperationId = string & {
|
|
24
|
+
readonly [operationKind]: true;
|
|
25
|
+
};
|
|
26
|
+
/** Why a store call did not complete. */
|
|
27
|
+
export type StoreFailure = "unavailable" | "timeout" | "uncertain" | "overloaded" | "full" | "expired" | "reused" | "closing";
|
|
28
|
+
/** A store call's result: narrow `ok` before reading `value`. */
|
|
29
|
+
export type StoreResult<T> = {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
readonly value: T;
|
|
32
|
+
} | {
|
|
33
|
+
readonly ok: false;
|
|
34
|
+
readonly reason: StoreFailure;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A template field the owner's client never receives. The brand is phantom; `serverOnly(value)` also marks the value at
|
|
38
|
+
* runtime, and the declaration checks that the two agree.
|
|
39
|
+
*/
|
|
40
|
+
export interface ServerOnly<V> {
|
|
41
|
+
readonly [serverOnlyKind]: V;
|
|
42
|
+
}
|
|
43
|
+
type Scalar = string | number | boolean;
|
|
44
|
+
/** What the server reads and writes: the template's type with every `ServerOnly` unwrapped, deeply readonly. */
|
|
45
|
+
export type ProfileData<T> = T extends ServerOnly<infer V> ? ProfileData<V> : T extends Scalar | undefined ? T : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<K, ProfileData<V>> : {
|
|
46
|
+
readonly [K in keyof T]: ProfileData<T[K]>;
|
|
47
|
+
};
|
|
48
|
+
type IsUnion<V, C = V> = V extends unknown ? ([C] extends [V] ? false : true) : never;
|
|
49
|
+
/** A `ServerOnly` field, or a plain object field whose every field is server-only in turn. */
|
|
50
|
+
type ServerOnlyField<V> = [V] extends [ServerOnly<unknown>] ? true : [V] extends [Scalar | undefined] ? false : [V] extends [ReadonlyMap<unknown, unknown> | readonly unknown[]] ? false : true extends IsUnion<V> ? false : string extends keyof V ? false : [keyof V] extends [never] ? false : false extends {
|
|
51
|
+
[K in keyof V]-?: ServerOnlyField<NonNullable<V[K]>>;
|
|
52
|
+
}[keyof V] ? false : true;
|
|
53
|
+
/**
|
|
54
|
+
* What the owner's client reads: every `ServerOnly` field removed, and every plain object left with no field, at any
|
|
55
|
+
* depth. Deeply readonly.
|
|
56
|
+
*/
|
|
57
|
+
export type ClientData<T> = T extends Scalar | undefined ? T : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<K, ClientData<V>> : T extends readonly unknown[] ? {
|
|
58
|
+
readonly [K in keyof T]: ClientData<T[K]>;
|
|
59
|
+
} : {
|
|
60
|
+
readonly [K in keyof T as ServerOnlyField<NonNullable<T[K]>> extends true ? never : K]: ClientData<T[K]>;
|
|
61
|
+
};
|
|
62
|
+
/** A store's server data type, for helper signatures: `export type PlayerData = Profile<typeof PlayerData>`. */
|
|
63
|
+
export type Profile<S> = S extends ProfileStore<infer T> ? ProfileData<T> : never;
|
|
64
|
+
/** A store's client data type. */
|
|
65
|
+
export type ClientProfile<S> = S extends ProfileStore<infer T> ? ClientData<T> : never;
|
|
66
|
+
/**
|
|
67
|
+
* A server handle's own members. `h()` reads, tracked and frozen; while the profile is not writable it returns the
|
|
68
|
+
* template's value at the path. `set` and `update` are one write each through the store's gate and return its result.
|
|
69
|
+
*/
|
|
70
|
+
export interface WriteHandle<V, A> extends Readable<V | A> {
|
|
71
|
+
set(value: V): boolean;
|
|
72
|
+
update(change: (current: V) => V): boolean;
|
|
73
|
+
}
|
|
74
|
+
/** An array handle's writes. Indices are TypeScript's, from 0. */
|
|
75
|
+
export interface ArrayWrites<E> {
|
|
76
|
+
/** Inserts at `index`, from 0 to the length, or appends without one. */
|
|
77
|
+
insert(value: E, index?: number): boolean;
|
|
78
|
+
/** Removes the element at `index`, from 0 to length - 1. */
|
|
79
|
+
remove(index: number): boolean;
|
|
80
|
+
}
|
|
81
|
+
type DataChildren<V, A> = [V] extends [Scalar] ? unknown : [V] extends [readonly (infer E)[]] ? ArrayWrites<E> & {
|
|
82
|
+
readonly [index: number]: DataHandle<E, undefined>;
|
|
83
|
+
} : true extends IsUnion<V> ? unknown : [V] extends [ReadonlyMap<string, infer E>] ? {
|
|
84
|
+
readonly [key: string]: DataHandle<E | undefined, A>;
|
|
85
|
+
} : string extends keyof V ? {
|
|
86
|
+
readonly [key: string]: DataHandle<V[string & keyof V] | undefined, A>;
|
|
87
|
+
} : {
|
|
88
|
+
readonly [K in keyof V]-?: DataHandle<V[K], A>;
|
|
89
|
+
};
|
|
90
|
+
/** A server handle at a position of type `V`: reads `V | A`, writes `V`, and reaches its children by key. */
|
|
91
|
+
export type DataHandle<V, A = never> = WriteHandle<V, A> & DataChildren<NonNullable<V>, A | Extract<V, undefined>>;
|
|
92
|
+
type ReadChildren<V, A> = [V] extends [Scalar] ? unknown : [V] extends [readonly (infer E)[]] ? {
|
|
93
|
+
readonly [index: number]: ReadHandle<E, undefined>;
|
|
94
|
+
} : true extends IsUnion<V> ? unknown : [V] extends [ReadonlyMap<string, infer E>] ? {
|
|
95
|
+
readonly [key: string]: ReadHandle<E | undefined, A>;
|
|
96
|
+
} : string extends keyof V ? {
|
|
97
|
+
readonly [key: string]: ReadHandle<V[string & keyof V] | undefined, A>;
|
|
98
|
+
} : {
|
|
99
|
+
readonly [K in keyof V]-?: ReadHandle<V[K], A>;
|
|
100
|
+
};
|
|
101
|
+
/** A client handle: the same paths and reads as the server's, and no writes. */
|
|
102
|
+
export type ReadHandle<V, A = never> = Readable<V | A> & ReadChildren<NonNullable<V>, A | Extract<V, undefined>>;
|
|
103
|
+
/** Why a profile loaded. */
|
|
104
|
+
export type LoadReason = "join" | "resume";
|
|
105
|
+
/** Why a profile is releasing. */
|
|
106
|
+
export type ReleaseReason = "leave" | "handoff" | "shutdown";
|
|
107
|
+
/** A developer product purchase, read from Roblox's receipt. */
|
|
108
|
+
export interface Purchase {
|
|
109
|
+
readonly id: string;
|
|
110
|
+
readonly productId: number;
|
|
111
|
+
readonly currencySpent: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* A grant callback's fourth argument. It stages an effect that runs once, on a fresh thread, after the grant is durably
|
|
115
|
+
* saved and before the purchase is acknowledged; never after a refused grant.
|
|
116
|
+
*/
|
|
117
|
+
export type AfterGrant = (effect: () => void) => void;
|
|
118
|
+
/** A field holding it is the claim. Not callable. */
|
|
119
|
+
export interface ProfileClaim {
|
|
120
|
+
readonly [claimKind]: true;
|
|
121
|
+
}
|
|
122
|
+
/** A durable message to one player's profile, declared in a server module. Invariant in `P`. */
|
|
123
|
+
export interface ProfileMessage<P> {
|
|
124
|
+
readonly [messageKind]: {
|
|
125
|
+
readonly payload: (value: P) => P;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** One quarantined message, for recovery tooling. The payload is the stored JSON, unknown keys included. */
|
|
129
|
+
export interface QuarantinedMessage {
|
|
130
|
+
readonly id: OperationId;
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly reason: "payload" | "attempts";
|
|
133
|
+
readonly attempts: number;
|
|
134
|
+
/** The id's creation time, in wall-clock seconds. */
|
|
135
|
+
readonly sentAt: number;
|
|
136
|
+
readonly payload: Json;
|
|
137
|
+
}
|
|
138
|
+
/** Why a save or handoff did not confirm. */
|
|
139
|
+
export type SaveFailure = "unavailable" | "timeout" | "uncertain";
|
|
140
|
+
/** A save or handoff result: narrow `ok` first. */
|
|
141
|
+
export type SaveResult = {
|
|
142
|
+
readonly ok: true;
|
|
143
|
+
} | {
|
|
144
|
+
readonly ok: false;
|
|
145
|
+
readonly reason: SaveFailure;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* A profile store: one shared declaration, callable on both sides. The server calls it with a player for that player's
|
|
149
|
+
* writable handle; the client calls it with nothing for the local player's read-only handle.
|
|
150
|
+
*/
|
|
151
|
+
export interface ProfileStore<T> {
|
|
152
|
+
readonly [storeKind]: {
|
|
153
|
+
readonly kind: "profiles";
|
|
154
|
+
readonly value: (value: T) => T;
|
|
155
|
+
};
|
|
156
|
+
/** Server: the player's root handle, the same one while they are present. */
|
|
157
|
+
(player: Player): DataHandle<ProfileData<T>>;
|
|
158
|
+
/** Client: the local player's read-only root handle. */
|
|
159
|
+
(): ReadHandle<ClientData<T>>;
|
|
160
|
+
/** Server. Tracked: whether a write to the player's profile would be accepted now. */
|
|
161
|
+
loaded(player: Player): boolean;
|
|
162
|
+
/** Client. Tracked: whether the server has published this player's profile and the client holds all of it. */
|
|
163
|
+
loaded(): boolean;
|
|
164
|
+
/** Server. Tracked: the players whose profiles are writable; it changes only when one starts or stops being. */
|
|
165
|
+
players(): readonly Player[];
|
|
166
|
+
/** Server. Yields until the profile is writable (true), or its load fails, the player leaves or the server closes. */
|
|
167
|
+
await(player: Player): boolean;
|
|
168
|
+
/** Server. Yields until the current revision is durably saved, for a rare critical point. */
|
|
169
|
+
save(player: Player, timeoutSeconds?: number): SaveResult;
|
|
170
|
+
/** Server. Seals and durably releases the profile while the player stays, before a teleport. */
|
|
171
|
+
handoff(player: Player): SaveResult;
|
|
172
|
+
/** Server. After a failed teleport: waits for the handoff's release to settle, then loads again. */
|
|
173
|
+
resume(player: Player): boolean;
|
|
174
|
+
/** Server. Reads any user's saved data, with no lock and no write. */
|
|
175
|
+
inspect(userId: number): StoreResult<ProfileData<T> | undefined>;
|
|
176
|
+
/** Server. Appends a message to a user's inbox, online or not, and waits until it is durable. */
|
|
177
|
+
send<P>(userId: number, message: ProfileMessage<P>, payload: NoInfer<P>, id?: OperationId): StoreResult<undefined>;
|
|
178
|
+
/** Server. A user's quarantined messages, read with no lock. */
|
|
179
|
+
quarantined(userId: number): StoreResult<readonly QuarantinedMessage[]>;
|
|
180
|
+
/** Server. Moves listed quarantined messages back to the inbox ("retry") or drops them for good ("discard"). */
|
|
181
|
+
clearQuarantined(userId: number, ids: readonly OperationId[], action: "retry" | "discard"): StoreResult<number>;
|
|
182
|
+
/** Server claim, a Subsystem field initializer: changes a profile as it loads, before anything reads it. */
|
|
183
|
+
onLoad(transform: (player: Player, data: ProfileData<T>, reason: LoadReason) => ProfileData<T>): ProfileClaim;
|
|
184
|
+
/** Server claim: changes a profile as it releases. A claim that throws is skipped. */
|
|
185
|
+
onRelease(transform: (player: Player, data: ProfileData<T>, reason: ReleaseReason) => ProfileData<T>): ProfileClaim;
|
|
186
|
+
/** Server claim: applies a message to its target's profile, once per message id. */
|
|
187
|
+
receive<P>(message: ProfileMessage<P>, apply: (player: Player, data: ProfileData<T>, payload: P) => ProfileData<T>): ProfileClaim;
|
|
188
|
+
/** Server claim: grants a developer product. The purchase is acknowledged only after the change is saved. */
|
|
189
|
+
grant(productId: number, apply: (player: Player, data: ProfileData<T>, purchase: Purchase, afterGrant: AfterGrant) => ProfileData<T>): ProfileClaim;
|
|
190
|
+
}
|
|
191
|
+
/** A profile store's options. */
|
|
192
|
+
export interface ProfileOptions<T> {
|
|
193
|
+
/**
|
|
194
|
+
* The store's type, by inference, and a new profile's value. A missing required key on an object path from the root
|
|
195
|
+
* takes its value from here. Admitted and frozen once, with every `serverOnly` mark unwrapped.
|
|
196
|
+
*/
|
|
197
|
+
readonly template: T;
|
|
198
|
+
/** Append-only. Removing or reordering a step changes what every older record becomes. */
|
|
199
|
+
readonly migrations?: readonly Migration[];
|
|
200
|
+
}
|
|
201
|
+
/** The handle member names. A template key with one of these names, at any depth, does not compile. */
|
|
202
|
+
export type ReservedName = "set" | "update" | "insert" | "remove";
|
|
203
|
+
type ReservedKeys<T> = T extends ServerOnly<infer V> ? ReservedKeys<V> : T extends Scalar | undefined ? never : T extends ReadonlyMap<unknown, infer V> ? ReservedKeys<V> : T extends readonly (infer E)[] ? ReservedKeys<E> : string extends keyof T ? ReservedKeys<T[string & keyof T]> : (keyof T & ReservedName) | {
|
|
204
|
+
[K in keyof T]-?: ReservedKeys<T[K]>;
|
|
205
|
+
}[keyof T];
|
|
206
|
+
type ReservedKeyCheck<T> = [ReservedKeys<T>] extends [never] ? unknown : {
|
|
207
|
+
readonly template: {
|
|
208
|
+
readonly [K in ReservedKeys<T> as `"${K & string}" is a handle method; rename it`]: never;
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Declares a profile store by its DataStore name. It must initialize an exported top-level const in a shared module.
|
|
213
|
+
* The type comes from `template`; annotate empty collections and optional fields with `as`.
|
|
214
|
+
*/
|
|
215
|
+
export declare function profileStore<T extends object>(_name: string, _options: ProfileOptions<T> & ReservedKeyCheck<T>): ProfileStore<T>;
|
|
216
|
+
/** Marks a template field server-only: the owner's client never receives it. */
|
|
217
|
+
export declare function serverOnly<V>(value: V): ServerOnly<V>;
|
|
218
|
+
/**
|
|
219
|
+
* The exact field paths the owner's client receives, each an array of segments: an object field's key, "[]" for an
|
|
220
|
+
* array element and "*" for a map value. A key that is "[]" or "*", or starts with a backslash, gets one more backslash.
|
|
221
|
+
* Sorted segment by segment, by bytes.
|
|
222
|
+
*/
|
|
223
|
+
export declare function replicatedPaths<T>(store: ProfileStore<T>): readonly (readonly string[])[];
|
|
224
|
+
export {};
|
package/out/data.luau
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _declarations = TS.import(script, script.Parent, "internal", "data", "declarations")
|
|
4
|
+
local markServerOnly = _declarations.markServerOnly
|
|
5
|
+
local replicatedPathsOf = _declarations.replicatedPathsOf
|
|
6
|
+
--* A value a save can hold: JSON, with absent keys for values that admit `undefined`.
|
|
7
|
+
--* A saved record: string keys to JSON.
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* One schema step. It gets a private copy of the stored record, unknown keys included, as plain JSON, and returns a
|
|
11
|
+
* patch: an object merges into the stored object, any other value replaces the stored one, and an omitted key is kept.
|
|
12
|
+
* A step cannot delete a key. Steps are append-only and must be pure.
|
|
13
|
+
|
|
14
|
+
]]
|
|
15
|
+
--[[
|
|
16
|
+
*
|
|
17
|
+
* The identity of one logical operation, kept across its retries. Only `operationId()` makes one; a plain string does
|
|
18
|
+
* not type-check. It carries its creation time, which bounds how long it deduplicates.
|
|
19
|
+
|
|
20
|
+
]]
|
|
21
|
+
--* Why a store call did not complete.
|
|
22
|
+
--* A store call's result: narrow `ok` before reading `value`.
|
|
23
|
+
--[[
|
|
24
|
+
*
|
|
25
|
+
* A template field the owner's client never receives. The brand is phantom; `serverOnly(value)` also marks the value at
|
|
26
|
+
* runtime, and the declaration checks that the two agree.
|
|
27
|
+
|
|
28
|
+
]]
|
|
29
|
+
--* What the server reads and writes: the template's type with every `ServerOnly` unwrapped, deeply readonly.
|
|
30
|
+
--* A `ServerOnly` field, or a plain object field whose every field is server-only in turn.
|
|
31
|
+
--[[
|
|
32
|
+
*
|
|
33
|
+
* What the owner's client reads: every `ServerOnly` field removed, and every plain object left with no field, at any
|
|
34
|
+
* depth. Deeply readonly.
|
|
35
|
+
|
|
36
|
+
]]
|
|
37
|
+
--* A store's server data type, for helper signatures: `export type PlayerData = Profile<typeof PlayerData>`.
|
|
38
|
+
--* A store's client data type.
|
|
39
|
+
--[[
|
|
40
|
+
*
|
|
41
|
+
* A server handle's own members. `h()` reads, tracked and frozen; while the profile is not writable it returns the
|
|
42
|
+
* template's value at the path. `set` and `update` are one write each through the store's gate and return its result.
|
|
43
|
+
|
|
44
|
+
]]
|
|
45
|
+
--* An array handle's writes. Indices are TypeScript's, from 0.
|
|
46
|
+
--* A server handle at a position of type `V`: reads `V | A`, writes `V`, and reaches its children by key.
|
|
47
|
+
--* A client handle: the same paths and reads as the server's, and no writes.
|
|
48
|
+
--* Why a profile loaded.
|
|
49
|
+
--* Why a profile is releasing.
|
|
50
|
+
--* A developer product purchase, read from Roblox's receipt.
|
|
51
|
+
--[[
|
|
52
|
+
*
|
|
53
|
+
* A grant callback's fourth argument. It stages an effect that runs once, on a fresh thread, after the grant is durably
|
|
54
|
+
* saved and before the purchase is acknowledged; never after a refused grant.
|
|
55
|
+
|
|
56
|
+
]]
|
|
57
|
+
--* A field holding it is the claim. Not callable.
|
|
58
|
+
--* A durable message to one player's profile, declared in a server module. Invariant in `P`.
|
|
59
|
+
--* One quarantined message, for recovery tooling. The payload is the stored JSON, unknown keys included.
|
|
60
|
+
--* Why a save or handoff did not confirm.
|
|
61
|
+
--* A save or handoff result: narrow `ok` first.
|
|
62
|
+
--[[
|
|
63
|
+
*
|
|
64
|
+
* A profile store: one shared declaration, callable on both sides. The server calls it with a player for that player's
|
|
65
|
+
* writable handle; the client calls it with nothing for the local player's read-only handle.
|
|
66
|
+
|
|
67
|
+
]]
|
|
68
|
+
--* A profile store's options.
|
|
69
|
+
--* The handle member names. A template key with one of these names, at any depth, does not compile.
|
|
70
|
+
--[[
|
|
71
|
+
*
|
|
72
|
+
* Declares a profile store by its DataStore name. It must initialize an exported top-level const in a shared module.
|
|
73
|
+
* The type comes from `template`; annotate empty collections and optional fields with `as`.
|
|
74
|
+
|
|
75
|
+
]]
|
|
76
|
+
local function profileStore(_name, _options)
|
|
77
|
+
error("Crucible data needs the @trioforge/crucible/transformer plugin")
|
|
78
|
+
end
|
|
79
|
+
--* Marks a template field server-only: the owner's client never receives it.
|
|
80
|
+
local function serverOnly(value)
|
|
81
|
+
return markServerOnly(value)
|
|
82
|
+
end
|
|
83
|
+
--[[
|
|
84
|
+
*
|
|
85
|
+
* The exact field paths the owner's client receives, each an array of segments: an object field's key, "[]" for an
|
|
86
|
+
* array element and "*" for a map value. A key that is "[]" or "*", or starts with a backslash, gets one more backslash.
|
|
87
|
+
* Sorted segment by segment, by bytes.
|
|
88
|
+
|
|
89
|
+
]]
|
|
90
|
+
local function replicatedPaths(store)
|
|
91
|
+
return replicatedPathsOf(store)
|
|
92
|
+
end
|
|
93
|
+
return {
|
|
94
|
+
profileStore = profileStore,
|
|
95
|
+
serverOnly = serverOnly,
|
|
96
|
+
replicatedPaths = replicatedPaths,
|
|
97
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Config, PlayerConfig } from "../configs";
|
|
2
|
+
import type { Node } from "../internal/network/schema";
|
|
3
|
+
export declare function config<T>(name: string, fallback: T, value: Node): Config<T>;
|
|
4
|
+
export declare function playerConfig<T>(name: string, fallback: T, value: Node): PlayerConfig<T>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _runtime = TS.import(script, script.Parent.Parent, "internal", "configs", "runtime")
|
|
4
|
+
local declare = _runtime.declare
|
|
5
|
+
local readGlobal = _runtime.readGlobal
|
|
6
|
+
local readPlayer = _runtime.readPlayer
|
|
7
|
+
local function config(name, fallback, value)
|
|
8
|
+
local declaration = declare(name, "global", fallback, value)
|
|
9
|
+
return function()
|
|
10
|
+
return readGlobal(declaration)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
local function playerConfig(name, fallback, value)
|
|
14
|
+
local declaration = declare(name, "player", fallback, value)
|
|
15
|
+
return function(player)
|
|
16
|
+
return readPlayer(declaration, player)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
return {
|
|
20
|
+
config = config,
|
|
21
|
+
playerConfig = playerConfig,
|
|
22
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProfileMessage, ProfileOptions, ProfileStore } from "../data";
|
|
2
|
+
import type { Node } from "../internal/network/schema";
|
|
3
|
+
import type { DataStore, StoreOptions } from "../server/data";
|
|
4
|
+
export declare function dataStore<T extends object>(name: string, options: StoreOptions<NoInfer<T>>, value: Node): DataStore<T>;
|
|
5
|
+
export declare function profileStore<T extends object>(name: string, options: ProfileOptions<T>, persist: Node, replica: Node, serverOnlyPaths: readonly (readonly string[])[]): ProfileStore<T>;
|
|
6
|
+
export declare function profileMessage<P>(name: string, payload: Node): ProfileMessage<P>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local declareProfileStore = TS.import(script, script.Parent.Parent, "internal", "data", "declarations").declareProfileStore
|
|
4
|
+
local declareProfileMessage = TS.import(script, script.Parent.Parent, "internal", "data", "messages").declareProfileMessage
|
|
5
|
+
local declareRecords = TS.import(script, script.Parent.Parent, "internal", "data", "record-declarations").declareRecords
|
|
6
|
+
local function dataStore(name, options, value)
|
|
7
|
+
return declareRecords(name, options.defaults, options.migrations, value)
|
|
8
|
+
end
|
|
9
|
+
local function profileStore(name, options, persist, replica, serverOnlyPaths)
|
|
10
|
+
return declareProfileStore(name, options.template, options.migrations, persist, replica, serverOnlyPaths)
|
|
11
|
+
end
|
|
12
|
+
local function profileMessage(name, payload)
|
|
13
|
+
return declareProfileMessage(name, payload)
|
|
14
|
+
end
|
|
15
|
+
return {
|
|
16
|
+
dataStore = dataStore,
|
|
17
|
+
profileStore = profileStore,
|
|
18
|
+
profileMessage = profileMessage,
|
|
19
|
+
}
|
package/out/emit/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export { n } from "../internal/network/schema";
|
|
2
|
+
export { config, playerConfig } from "./configs";
|
|
3
|
+
export { toServer, toClient, askServer, receive } from "./network";
|
|
4
|
+
export { replicated, replicatedPerPlayer } from "./replicated";
|
|
5
|
+
export { dataStore, profileStore, profileMessage } from "./data";
|
|
1
6
|
import type { All } from "../all";
|
|
2
7
|
import type { CrucibleObject } from "../crucible-object";
|
|
3
8
|
export type EmittedDep = string | {
|
|
@@ -38,4 +43,3 @@ export interface EmittedAttribute {
|
|
|
38
43
|
readonly required: boolean;
|
|
39
44
|
}
|
|
40
45
|
export declare function attribute<T>(object: object, field: string, fallback?: T): T;
|
|
41
|
-
export {};
|
package/out/emit/init.luau
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
3
4
|
local readClaim = TS.import(script, script.Parent, "internal", "construction-claim").readClaim
|
|
5
|
+
exports.n = TS.import(script, script.Parent, "internal", "network", "schema").n
|
|
6
|
+
local _configs = TS.import(script, script, "configs")
|
|
7
|
+
exports.config = _configs.config
|
|
8
|
+
exports.playerConfig = _configs.playerConfig
|
|
9
|
+
local _network = TS.import(script, script, "network")
|
|
10
|
+
exports.toServer = _network.toServer
|
|
11
|
+
exports.toClient = _network.toClient
|
|
12
|
+
exports.askServer = _network.askServer
|
|
13
|
+
exports.receive = _network.receive
|
|
14
|
+
local _replicated = TS.import(script, script, "replicated")
|
|
15
|
+
exports.replicated = _replicated.replicated
|
|
16
|
+
exports.replicatedPerPlayer = _replicated.replicatedPerPlayer
|
|
17
|
+
local _data = TS.import(script, script, "data")
|
|
18
|
+
exports.dataStore = _data.dataStore
|
|
19
|
+
exports.profileStore = _data.profileStore
|
|
20
|
+
exports.profileMessage = _data.profileMessage
|
|
4
21
|
local _registry = TS.import(script, script.Parent, "internal", "registry")
|
|
5
22
|
local define = _registry.define
|
|
6
23
|
local resolveOne = _registry.resolveOne
|
|
@@ -76,10 +93,9 @@ local function attribute(object, field, fallback)
|
|
|
76
93
|
local value = _value
|
|
77
94
|
return if value == nil then fallback else value
|
|
78
95
|
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
96
|
+
exports.defineClass = defineClass
|
|
97
|
+
exports.resolve = resolve
|
|
98
|
+
exports.resolveAll = resolveAll
|
|
99
|
+
exports.attribute = attribute
|
|
100
|
+
exports.t = t
|
|
101
|
+
return exports
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AskServer, ReceiveCallback, Receiver, ReceiveTarget, ToClient, ToServer } from "../network";
|
|
2
|
+
import type { Node } from "../internal/network/schema";
|
|
3
|
+
export declare function toServer<P>(name: string, payload: Node): ToServer<P>;
|
|
4
|
+
export declare function toClient<P>(name: string, payload: Node): ToClient<P>;
|
|
5
|
+
export declare function askServer<Request, Response>(name: string, request: Node, response: Node, timeout: number | undefined): AskServer<Request, Response>;
|
|
6
|
+
export declare function receive<T extends ReceiveTarget>(owner: object, field: string, target: T, callback: ReceiveCallback<T>): Receiver;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local define = TS.import(script, script.Parent.Parent, "internal", "network", "catalog").define
|
|
4
|
+
local claim = TS.import(script, script.Parent.Parent, "internal", "network", "claims").claim
|
|
5
|
+
local function toServer(name, payload)
|
|
6
|
+
return define({
|
|
7
|
+
kind = "to-server",
|
|
8
|
+
name = name,
|
|
9
|
+
input = payload,
|
|
10
|
+
})
|
|
11
|
+
end
|
|
12
|
+
local function toClient(name, payload)
|
|
13
|
+
return define({
|
|
14
|
+
kind = "to-client",
|
|
15
|
+
name = name,
|
|
16
|
+
input = payload,
|
|
17
|
+
})
|
|
18
|
+
end
|
|
19
|
+
local function askServer(name, request, response, timeout)
|
|
20
|
+
return define({
|
|
21
|
+
kind = "ask-server",
|
|
22
|
+
name = name,
|
|
23
|
+
input = request,
|
|
24
|
+
output = response,
|
|
25
|
+
timeout = timeout,
|
|
26
|
+
})
|
|
27
|
+
end
|
|
28
|
+
local function receive(owner, field, target, callback)
|
|
29
|
+
return claim(owner, field, target, callback)
|
|
30
|
+
end
|
|
31
|
+
return {
|
|
32
|
+
toServer = toServer,
|
|
33
|
+
toClient = toClient,
|
|
34
|
+
askServer = askServer,
|
|
35
|
+
receive = receive,
|
|
36
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Node } from "../internal/network/schema";
|
|
2
|
+
import type { Replicated, ReplicatedPerPlayer } from "../replicated";
|
|
3
|
+
export declare function replicated<T>(name: string, initial: NoInfer<T>, value: Node): Replicated<T>;
|
|
4
|
+
export declare function replicatedPerPlayer<T>(name: string, initial: NoInfer<T>, value: Node): ReplicatedPerPlayer<T>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local declareReplicated = TS.import(script, script.Parent.Parent, "internal", "sync", "declarations").declareReplicated
|
|
4
|
+
local function replicated(name, initial, value)
|
|
5
|
+
return declareReplicated("all", name, initial, value)
|
|
6
|
+
end
|
|
7
|
+
local function replicatedPerPlayer(name, initial, value)
|
|
8
|
+
return declareReplicated("player", name, initial, value)
|
|
9
|
+
end
|
|
10
|
+
return {
|
|
11
|
+
replicated = replicated,
|
|
12
|
+
replicatedPerPlayer = replicatedPerPlayer,
|
|
13
|
+
}
|