@flamework-experimental/networking 2.0.0-alpha.3 → 2.0.0-alpha.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/README.md +13 -8
- package/flamework.build +1 -1
- package/out/events/createGenericHandler.luau +1 -1
- package/out/events/createServerMethod.d.ts +2 -0
- package/out/events/types.d.ts +32 -20
- package/out/functions/createClientMethod.d.ts +2 -1
- package/out/functions/createGenericHandler.luau +1 -1
- package/out/functions/createServerMethod.d.ts +2 -1
- package/out/functions/types.d.ts +58 -24
- package/out/index.d.ts +22 -1
- package/out/init.luau +25 -0
- package/out/middleware/createGuards.d.ts +1 -1
- package/out/middleware/createGuards.luau +2 -1
- package/out/middleware/processor.d.ts +3 -0
- package/out/middleware/processor.luau +37 -2
- package/out/types.d.ts +50 -9
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -5,15 +5,20 @@ isolated and easy to test.
|
|
|
5
5
|
|
|
6
6
|
## Documentation
|
|
7
7
|
|
|
8
|
-
Start with **[docs/](docs/README.md)**. It holds a twelve-part guide, which starts from a working
|
|
8
|
+
Start with **[docs/](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/docs/README.md)**. It holds a twelve-part guide, which starts from a working
|
|
9
9
|
entry point and builds up to plugins, project layout, scopes and testing. It also holds reference
|
|
10
10
|
material:
|
|
11
11
|
|
|
12
12
|
| | |
|
|
13
13
|
|---|---|
|
|
14
|
-
| [Guide](docs/README.md#guide) | Getting started, modules, providers, lifecycle events, components, networking, macros, plugins, project structure, migrating from v1, scopes, testing in the place. |
|
|
15
|
-
| [Internals](docs/reference/internals.md) | What the transformer does to your code and what the runtime does with the result. |
|
|
16
|
-
| [Transformer plugins](docs/reference/transformer-plugins.md) | Adding macro types of your own. |
|
|
14
|
+
| [Guide](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/docs/README.md#guide) | Getting started, modules, providers, lifecycle events, components, networking, macros, plugins, project structure, migrating from v1, scopes, testing in the place. |
|
|
15
|
+
| [Internals](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/docs/reference/internals.md) | What the transformer does to your code and what the runtime does with the result. |
|
|
16
|
+
| [Transformer plugins](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/docs/reference/transformer-plugins.md) | Adding macro types of your own. |
|
|
17
|
+
|
|
18
|
+
The guide also ships inside the core package, for the version you installed:
|
|
19
|
+
`node_modules/@flamework-experimental/core/docs/README.md` is its index, and the pages are in
|
|
20
|
+
`node_modules/@flamework-experimental/core/docs/guide/`. Instructions for a coding assistant can
|
|
21
|
+
point there, so that it reads the docs for your version.
|
|
17
22
|
|
|
18
23
|
The Flamework website documents v1, most of which no longer applies:
|
|
19
24
|
|
|
@@ -51,7 +56,7 @@ bun run test:place # the in-place suite in Roblox Studio (tests/place); need
|
|
|
51
56
|
- **Transformer tests** (`bun run test:unit`) build a fixture project with the real `rbxtsc` and
|
|
52
57
|
check the Luau it emits: guard generation, identifiers, nested macros and the plugin system.
|
|
53
58
|
- **Runtime specs** (`bun run test:runtime`) run the built `@flamework-experimental/core`,
|
|
54
|
-
`components` and `networking` packages under Lune. The harness in [`tests/runtime`](tests/runtime)
|
|
59
|
+
`components` and `networking` packages under Lune. The harness in [`tests/runtime`](https://github.com/Velover/ExperimentalFlameworkV2/tree/HEAD/tests/runtime)
|
|
55
60
|
models roblox-ts's `TS.import` tree over the filesystem. It also stubs the parts of the Roblox API
|
|
56
61
|
that Flamework uses: Instances, attributes, CollectionService, RemoteEvents, Players, signals,
|
|
57
62
|
`task`, `Enum`, and a `Heartbeat` pump so that `Promise.delay` runs (and with it, request
|
|
@@ -65,10 +70,10 @@ bun run test:place # the in-place suite in Roblox Studio (tests/place); need
|
|
|
65
70
|
example, from the server a function receives on `$name` and sends on `@name`, and from the client
|
|
66
71
|
it does the reverse, so the two runs pin down the wire format from both ends.
|
|
67
72
|
|
|
68
|
-
Specs live in [`packages/specs`](packages/specs). `rbxtsc` builds them like any other project that
|
|
73
|
+
Specs live in [`packages/specs`](https://github.com/Velover/ExperimentalFlameworkV2/tree/HEAD/packages/specs). `rbxtsc` builds them like any other project that
|
|
69
74
|
uses Flamework, so they test the transformer and the runtime together.
|
|
70
75
|
|
|
71
76
|
A third suite runs against the real engine, and `bun run test` leaves it out. It lives in the
|
|
72
|
-
[test place](tests/place/README.md), a small game linked to the packages' builds.
|
|
77
|
+
[test place](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/tests/place/README.md), a small game linked to the packages' builds.
|
|
73
78
|
`bun run test:place` runs its `@flamework-experimental/testing` sections in Roblox Studio, on both
|
|
74
|
-
realms, under four Rojo projects (see [Testing in Roblox Studio](docs/testing/studio.md)).
|
|
79
|
+
realms, under four Rojo projects (see [Testing in Roblox Studio](https://github.com/Velover/ExperimentalFlameworkV2/blob/HEAD/docs/testing/studio.md)).
|
package/flamework.build
CHANGED
|
@@ -50,7 +50,7 @@ local function createGenericHandler(globalName, namespaceName, metadata, config,
|
|
|
50
50
|
if not config.disableIncomingGuards and isIncoming then
|
|
51
51
|
local guards = metadata.incoming[name]
|
|
52
52
|
assert(guards)
|
|
53
|
-
incomingGuards = createGuards(name, guards[1], guards[2], networkInfo, config.warnOnInvalidGuards, signals)
|
|
53
|
+
incomingGuards = createGuards(name, guards[1], guards[2], guards[3], networkInfo, config.warnOnInvalidGuards, signals)
|
|
54
54
|
end
|
|
55
55
|
-- A malformed serialized payload is reported like a failed guard, with no argument index.
|
|
56
56
|
local onMalformed = function(player, message)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { EventInterface } from "../event/createEvent";
|
|
2
2
|
export declare function createServerMethod(receiver: EventInterface, sender: EventInterface): {
|
|
3
3
|
readonly _flamework_send?: unknown[] | undefined;
|
|
4
|
+
readonly _flamework_fn?: unknown;
|
|
4
5
|
_fire: (players: Player | Player[], payload?: buffer, blobs?: Array<defined>) => void;
|
|
5
6
|
_except: (players: Player | Player[], payload?: buffer, blobs?: Array<defined>) => void;
|
|
6
7
|
_broadcast: (payload?: buffer, blobs?: Array<defined>) => void;
|
|
8
|
+
readonly _flamework_packing?: "plain" | undefined;
|
|
7
9
|
fire: (players: Player | Player[], ...args: unknown[]) => void;
|
|
8
10
|
except: (players: Player | Player[], ...args: unknown[]) => void;
|
|
9
11
|
broadcast: (...args: unknown[]) => void;
|
package/out/events/types.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
FunctionParameters,
|
|
3
3
|
IntrinsicTupleGuards,
|
|
4
4
|
IntrinsicNetworkDecoder,
|
|
5
|
+
IntrinsicNetworkUnreliable,
|
|
5
6
|
IntrinsicObfuscate,
|
|
7
|
+
IsRawMember,
|
|
6
8
|
NetworkingObfuscationMarker,
|
|
7
|
-
|
|
8
|
-
NetworkUnreliable,
|
|
9
|
+
NetworkPacking,
|
|
9
10
|
ObfuscateNames,
|
|
10
11
|
} from "../types";
|
|
11
12
|
import { EventNetworkingEvents } from "../handlers";
|
|
@@ -17,9 +18,12 @@ import { Modding } from "@flamework-experimental/core";
|
|
|
17
18
|
* A sender declared `Networking.RawReliable` / `RawUnreliable`: its arguments travel as they are.
|
|
18
19
|
* Without the hidden marker below, no call site packs them and the peer runs no decoder.
|
|
19
20
|
*/
|
|
20
|
-
export interface RawServerSender<I extends unknown[]> {
|
|
21
|
+
export interface RawServerSender<I extends unknown[], M = "raw"> {
|
|
21
22
|
(player: Player | Player[], ...args: I): void;
|
|
22
23
|
|
|
24
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
25
|
+
readonly _flamework_packing?: M;
|
|
26
|
+
|
|
23
27
|
/**
|
|
24
28
|
* Sends this request to the specified player(s).
|
|
25
29
|
* @param players The player(s) that will receive this event
|
|
@@ -38,10 +42,13 @@ export interface RawServerSender<I extends unknown[]> {
|
|
|
38
42
|
broadcast(...args: I): void;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
export interface ServerSender<I extends unknown[]
|
|
45
|
+
export interface ServerSender<I extends unknown[], F = unknown, M = NetworkPacking<F>> extends RawServerSender<I, M> {
|
|
42
46
|
/** @hidden Marks a sender for the transformer, which packs its arguments at each call site. */
|
|
43
47
|
readonly _flamework_send?: I;
|
|
44
48
|
|
|
49
|
+
/** @hidden The declared member, whose markers (`Serialized`) say whether its call sites pack. */
|
|
50
|
+
readonly _flamework_fn?: F;
|
|
51
|
+
|
|
45
52
|
/** @hidden Sends an argument list the transformer already packed; nothing when the list carries nothing. */
|
|
46
53
|
_fire(players: Player | Player[], payload?: buffer, blobs?: Array<defined>): void;
|
|
47
54
|
|
|
@@ -70,19 +77,25 @@ export interface ServerReceiver<I extends unknown[]> extends RawServerReceiver<I
|
|
|
70
77
|
readonly _flamework_receive?: I;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
export interface RawClientSender<I extends unknown[]> {
|
|
80
|
+
export interface RawClientSender<I extends unknown[], M = "raw"> {
|
|
74
81
|
(...args: I): void;
|
|
75
82
|
|
|
83
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
84
|
+
readonly _flamework_packing?: M;
|
|
85
|
+
|
|
76
86
|
/**
|
|
77
87
|
* Sends this request to the server.
|
|
78
88
|
*/
|
|
79
89
|
fire(...args: I): void;
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
export interface ClientSender<I extends unknown[]
|
|
92
|
+
export interface ClientSender<I extends unknown[], F = unknown, M = NetworkPacking<F>> extends RawClientSender<I, M> {
|
|
83
93
|
/** @hidden Marks a sender for the transformer, which packs its arguments at each call site. */
|
|
84
94
|
readonly _flamework_send?: I;
|
|
85
95
|
|
|
96
|
+
/** @hidden The declared member, whose markers (`Serialized`) say whether its call sites pack. */
|
|
97
|
+
readonly _flamework_fn?: F;
|
|
98
|
+
|
|
86
99
|
/** @hidden Sends an argument list the transformer already packed; nothing when the list carries nothing. */
|
|
87
100
|
_fire(payload?: buffer, blobs?: Array<defined>): void;
|
|
88
101
|
}
|
|
@@ -106,11 +119,11 @@ export interface ClientReceiver<I extends unknown[]> extends RawClientReceiver<I
|
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
export type ServerHandler<E, R> = NetworkingObfuscationMarker & {
|
|
109
|
-
[k in keyof Events<E>]: E[k] extends
|
|
122
|
+
[k in keyof Events<E>]: IsRawMember<E[k]> extends true
|
|
110
123
|
? RawServerSender<FunctionParameters<E[k]>>
|
|
111
|
-
: ServerSender<FunctionParameters<E[k]
|
|
124
|
+
: ServerSender<FunctionParameters<E[k]>, E[k]>;
|
|
112
125
|
} & {
|
|
113
|
-
[k in keyof Events<R>]: R[k] extends
|
|
126
|
+
[k in keyof Events<R>]: IsRawMember<R[k]> extends true
|
|
114
127
|
? RawServerReceiver<FunctionParameters<R[k]>>
|
|
115
128
|
: ServerReceiver<FunctionParameters<R[k]>>;
|
|
116
129
|
} & {
|
|
@@ -120,11 +133,11 @@ export type ServerHandler<E, R> = NetworkingObfuscationMarker & {
|
|
|
120
133
|
};
|
|
121
134
|
|
|
122
135
|
export type ClientHandler<E, R> = NetworkingObfuscationMarker & {
|
|
123
|
-
[k in keyof Events<E>]: E[k] extends
|
|
136
|
+
[k in keyof Events<E>]: IsRawMember<E[k]> extends true
|
|
124
137
|
? RawClientSender<FunctionParameters<E[k]>>
|
|
125
|
-
: ClientSender<FunctionParameters<E[k]
|
|
138
|
+
: ClientSender<FunctionParameters<E[k]>, E[k]>;
|
|
126
139
|
} & {
|
|
127
|
-
[k in keyof Events<R>]: R[k] extends
|
|
140
|
+
[k in keyof Events<R>]: IsRawMember<R[k]> extends true
|
|
128
141
|
? RawClientReceiver<FunctionParameters<R[k]>>
|
|
129
142
|
: ClientReceiver<FunctionParameters<R[k]>>;
|
|
130
143
|
} & {
|
|
@@ -185,23 +198,22 @@ export type NamespaceMetadata<R, S> = Modding.Emit<{
|
|
|
185
198
|
incomingIds: ObfuscateNames<keyof Events<R>>;
|
|
186
199
|
incoming: IntrinsicObfuscate<{ [k in keyof Events<R>]: IntrinsicTupleGuards<Parameters<Events<R>[k]>> }>;
|
|
187
200
|
incomingUnreliable: IntrinsicObfuscate<{
|
|
188
|
-
[k in keyof Events<R>]: R[k]
|
|
201
|
+
[k in keyof Events<R>]: IntrinsicNetworkUnreliable<R[k], k>;
|
|
189
202
|
}>;
|
|
190
203
|
|
|
191
204
|
outgoingIds: ObfuscateNames<keyof Events<S>>;
|
|
192
205
|
outgoingUnreliable: IntrinsicObfuscate<{
|
|
193
|
-
[k in keyof Events<S>]: S[k]
|
|
206
|
+
[k in keyof Events<S>]: IntrinsicNetworkUnreliable<S[k], k>;
|
|
194
207
|
}>;
|
|
195
208
|
|
|
196
209
|
/**
|
|
197
|
-
* Decoders for each incoming event's argument list, present
|
|
198
|
-
* on, and absent for raw events and
|
|
199
|
-
* inline where they are fired; nothing here
|
|
210
|
+
* Decoders for each incoming event's argument list, present for the events that are packed (all of
|
|
211
|
+
* them with `networking.serialization` on, else the serialized ones) and absent for raw events and
|
|
212
|
+
* for lists that carry nothing. Outgoing lists are packed inline where they are fired; nothing here
|
|
213
|
+
* can encode.
|
|
200
214
|
*/
|
|
201
215
|
incomingSerializers: IntrinsicObfuscate<{
|
|
202
|
-
[k in keyof Events<R>]: R[k]
|
|
203
|
-
? undefined
|
|
204
|
-
: IntrinsicNetworkDecoder<Parameters<Events<R>[k]>>;
|
|
216
|
+
[k in keyof Events<R>]: IntrinsicNetworkDecoder<Parameters<Events<R>[k]>, R[k], k>;
|
|
205
217
|
}>;
|
|
206
218
|
|
|
207
219
|
namespaceIds: ObfuscateNames<keyof EventNamespaces<R> | keyof EventNamespaces<S>>;
|
|
@@ -3,12 +3,13 @@ import { FunctionSenderInterface } from "../function/createFunctionSender";
|
|
|
3
3
|
import { FunctionCreateConfiguration } from "./types";
|
|
4
4
|
export declare function createClientMethod(config: FunctionCreateConfiguration<unknown>, receiver?: FunctionReceiverInterface, sender?: FunctionSenderInterface): {
|
|
5
5
|
readonly _flamework_send?: unknown[] | undefined;
|
|
6
|
+
readonly _flamework_fn?: unknown;
|
|
6
7
|
_invoke: (payload?: buffer, blobs?: Array<defined>) => Promise<unknown>;
|
|
7
8
|
_invokeWithTimeout: (timeout: number, payload?: buffer, blobs?: Array<defined>) => Promise<unknown>;
|
|
9
|
+
readonly _flamework_packing?: "plain" | undefined;
|
|
8
10
|
invoke: (...args: unknown[]) => Promise<unknown>;
|
|
9
11
|
invokeWithTimeout: (timeout: number, ...args: unknown[]) => Promise<unknown>;
|
|
10
12
|
readonly _flamework_receive?: unknown[] | undefined;
|
|
11
|
-
readonly _flamework_fn?: unknown;
|
|
12
13
|
_setCallback: (callback: (...args: never[]) => unknown, pack: (value: unknown) => unknown) => void;
|
|
13
14
|
setCallback: (callback: (...args: unknown[]) => unknown | Promise<unknown>) => void;
|
|
14
15
|
predict: (...args: unknown[]) => Promise<unknown>;
|
|
@@ -47,7 +47,7 @@ local function createGenericHandler(globalName, namespaceName, receiverPrefix, s
|
|
|
47
47
|
if not config.disableIncomingGuards and isReceiver then
|
|
48
48
|
local guards = metadata.incoming[name]
|
|
49
49
|
assert(guards)
|
|
50
|
-
incomingGuards = createGuards(name, guards[1], guards[2], networkInfo, config.warnOnInvalidGuards, signals)
|
|
50
|
+
incomingGuards = createGuards(name, guards[1], guards[2], guards[3], networkInfo, config.warnOnInvalidGuards, signals)
|
|
51
51
|
end
|
|
52
52
|
-- A malformed serialized payload is reported like a failed guard, with no argument index.
|
|
53
53
|
local onMalformed = function(player, message)
|
|
@@ -3,12 +3,13 @@ import { FunctionSenderInterface } from "../function/createFunctionSender";
|
|
|
3
3
|
import { FunctionCreateConfiguration } from "./types";
|
|
4
4
|
export declare function createServerMethod(config: FunctionCreateConfiguration<unknown>, receiver?: FunctionReceiverInterface, sender?: FunctionSenderInterface): {
|
|
5
5
|
readonly _flamework_send?: unknown[] | undefined;
|
|
6
|
+
readonly _flamework_fn?: unknown;
|
|
6
7
|
_invoke: (player: Player, payload?: buffer, blobs?: Array<defined>) => Promise<unknown>;
|
|
7
8
|
_invokeWithTimeout: (player: Player, timeout: number, payload?: buffer, blobs?: Array<defined>) => Promise<unknown>;
|
|
9
|
+
readonly _flamework_packing?: "plain" | undefined;
|
|
8
10
|
invoke: (player: Player, ...args: unknown[]) => Promise<unknown>;
|
|
9
11
|
invokeWithTimeout: (player: Player, timeout: number, ...args: unknown[]) => Promise<unknown>;
|
|
10
12
|
readonly _flamework_receive?: unknown[] | undefined;
|
|
11
|
-
readonly _flamework_fn?: unknown;
|
|
12
13
|
_setCallback: (callback: (player: Player, ...args: never[]) => unknown, pack: (value: unknown) => unknown) => void;
|
|
13
14
|
setCallback: (callback: (player: Player, ...args: unknown[]) => unknown | Promise<unknown>) => void;
|
|
14
15
|
predict: (player: Player, ...args: unknown[]) => Promise<unknown>;
|
package/out/functions/types.d.ts
CHANGED
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
IntrinsicNetworkDecoder,
|
|
7
7
|
IntrinsicNetworkResultDecoder,
|
|
8
8
|
IntrinsicObfuscate,
|
|
9
|
+
IsRawMember,
|
|
9
10
|
NetworkingObfuscationMarker,
|
|
10
|
-
|
|
11
|
+
NetworkPacking,
|
|
11
12
|
ObfuscateNames,
|
|
12
13
|
} from "../types";
|
|
13
14
|
import { FunctionNetworkingEvents } from "../handlers";
|
|
@@ -19,9 +20,12 @@ import { Modding } from "@flamework-experimental/core";
|
|
|
19
20
|
* A sender declared `Networking.Raw`: its arguments and the result travel as they are. Without the
|
|
20
21
|
* hidden marker below, no call site packs them and the peer runs no decoder.
|
|
21
22
|
*/
|
|
22
|
-
export interface RawServerSender<I extends unknown[], O> {
|
|
23
|
+
export interface RawServerSender<I extends unknown[], O, M = "raw"> {
|
|
23
24
|
(player: Player, ...args: I): Promise<O>;
|
|
24
25
|
|
|
26
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
27
|
+
readonly _flamework_packing?: M;
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* Sends this request to the specified player.
|
|
27
31
|
* @param player The player that will receive this event
|
|
@@ -36,10 +40,17 @@ export interface RawServerSender<I extends unknown[], O> {
|
|
|
36
40
|
invokeWithTimeout(player: Player, timeout: number, ...args: I): Promise<O>;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
export interface ServerSender<I extends unknown[], O
|
|
43
|
+
export interface ServerSender<I extends unknown[], O, F = unknown, M = NetworkPacking<F>> extends RawServerSender<
|
|
44
|
+
I,
|
|
45
|
+
O,
|
|
46
|
+
M
|
|
47
|
+
> {
|
|
40
48
|
/** @hidden Marks a sender for the transformer, which packs its arguments at each call site. */
|
|
41
49
|
readonly _flamework_send?: I;
|
|
42
50
|
|
|
51
|
+
/** @hidden The declared function type, whose markers (`Serialized`) say whether its call sites pack. */
|
|
52
|
+
readonly _flamework_fn?: F;
|
|
53
|
+
|
|
43
54
|
/** @hidden Sends an argument list the transformer already packed; nothing when the list carries nothing. */
|
|
44
55
|
_invoke(player: Player, payload?: buffer, blobs?: Array<defined>): Promise<O>;
|
|
45
56
|
|
|
@@ -47,7 +58,10 @@ export interface ServerSender<I extends unknown[], O> extends RawServerSender<I,
|
|
|
47
58
|
_invokeWithTimeout(player: Player, timeout: number, payload?: buffer, blobs?: Array<defined>): Promise<O>;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
export interface RawServerReceiver<I extends unknown[], O> {
|
|
61
|
+
export interface RawServerReceiver<I extends unknown[], O, M = "raw"> {
|
|
62
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
63
|
+
readonly _flamework_packing?: M;
|
|
64
|
+
|
|
51
65
|
/**
|
|
52
66
|
* Connect to a networking event.
|
|
53
67
|
* @param event The event to connect to
|
|
@@ -62,20 +76,27 @@ export interface RawServerReceiver<I extends unknown[], O> {
|
|
|
62
76
|
predict(player: Player, ...args: I): Promise<O>;
|
|
63
77
|
}
|
|
64
78
|
|
|
65
|
-
export interface ServerReceiver<I extends unknown[], O, F = unknown
|
|
79
|
+
export interface ServerReceiver<I extends unknown[], O, F = unknown, M = NetworkPacking<F>> extends RawServerReceiver<
|
|
80
|
+
I,
|
|
81
|
+
O,
|
|
82
|
+
M
|
|
83
|
+
> {
|
|
66
84
|
/** @hidden Marks a receiver for the transformer, which packs the callback's result at the call site. */
|
|
67
85
|
readonly _flamework_receive?: I;
|
|
68
86
|
|
|
69
|
-
/** @hidden The declared function type
|
|
87
|
+
/** @hidden The declared function type: its return type is what the transformer packs, its markers say how. */
|
|
70
88
|
readonly _flamework_fn?: F;
|
|
71
89
|
|
|
72
90
|
/** @hidden Registers a callback with `pack`, which turns a successful result into `[payload, blobs?]`. */
|
|
73
91
|
_setCallback(callback: (player: Player, ...args: never[]) => unknown, pack: (value: unknown) => unknown): void;
|
|
74
92
|
}
|
|
75
93
|
|
|
76
|
-
export interface RawClientSender<I extends unknown[], O> {
|
|
94
|
+
export interface RawClientSender<I extends unknown[], O, M = "raw"> {
|
|
77
95
|
(...args: I): Promise<O>;
|
|
78
96
|
|
|
97
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
98
|
+
readonly _flamework_packing?: M;
|
|
99
|
+
|
|
79
100
|
/**
|
|
80
101
|
* Sends this request to the server.
|
|
81
102
|
*/
|
|
@@ -88,10 +109,17 @@ export interface RawClientSender<I extends unknown[], O> {
|
|
|
88
109
|
invokeWithTimeout(timeout: number, ...args: I): Promise<O>;
|
|
89
110
|
}
|
|
90
111
|
|
|
91
|
-
export interface ClientSender<I extends unknown[], O
|
|
112
|
+
export interface ClientSender<I extends unknown[], O, F = unknown, M = NetworkPacking<F>> extends RawClientSender<
|
|
113
|
+
I,
|
|
114
|
+
O,
|
|
115
|
+
M
|
|
116
|
+
> {
|
|
92
117
|
/** @hidden Marks a sender for the transformer, which packs its arguments at each call site. */
|
|
93
118
|
readonly _flamework_send?: I;
|
|
94
119
|
|
|
120
|
+
/** @hidden The declared function type, whose markers (`Serialized`) say whether its call sites pack. */
|
|
121
|
+
readonly _flamework_fn?: F;
|
|
122
|
+
|
|
95
123
|
/** @hidden Sends an argument list the transformer already packed; nothing when the list carries nothing. */
|
|
96
124
|
_invoke(payload?: buffer, blobs?: Array<defined>): Promise<O>;
|
|
97
125
|
|
|
@@ -99,7 +127,10 @@ export interface ClientSender<I extends unknown[], O> extends RawClientSender<I,
|
|
|
99
127
|
_invokeWithTimeout(timeout: number, payload?: buffer, blobs?: Array<defined>): Promise<O>;
|
|
100
128
|
}
|
|
101
129
|
|
|
102
|
-
export interface RawClientReceiver<I extends unknown[], O> {
|
|
130
|
+
export interface RawClientReceiver<I extends unknown[], O, M = "raw"> {
|
|
131
|
+
/** @hidden How the member is packed (see `NetworkPacking`), which keeps differently packed members apart in a union. */
|
|
132
|
+
readonly _flamework_packing?: M;
|
|
133
|
+
|
|
103
134
|
/**
|
|
104
135
|
* Connect to a networking function.
|
|
105
136
|
* @param event The function to connect to
|
|
@@ -113,11 +144,15 @@ export interface RawClientReceiver<I extends unknown[], O> {
|
|
|
113
144
|
predict(...args: I): Promise<O>;
|
|
114
145
|
}
|
|
115
146
|
|
|
116
|
-
export interface ClientReceiver<I extends unknown[], O, F = unknown
|
|
147
|
+
export interface ClientReceiver<I extends unknown[], O, F = unknown, M = NetworkPacking<F>> extends RawClientReceiver<
|
|
148
|
+
I,
|
|
149
|
+
O,
|
|
150
|
+
M
|
|
151
|
+
> {
|
|
117
152
|
/** @hidden Marks a receiver for the transformer, which packs the callback's result at the call site. */
|
|
118
153
|
readonly _flamework_receive?: I;
|
|
119
154
|
|
|
120
|
-
/** @hidden The declared function type
|
|
155
|
+
/** @hidden The declared function type: its return type is what the transformer packs, its markers say how. */
|
|
121
156
|
readonly _flamework_fn?: F;
|
|
122
157
|
|
|
123
158
|
/** @hidden Registers a callback with `pack`, which turns a successful result into `[payload, blobs?]`. */
|
|
@@ -125,11 +160,11 @@ export interface ClientReceiver<I extends unknown[], O, F = unknown> extends Raw
|
|
|
125
160
|
}
|
|
126
161
|
|
|
127
162
|
export type ServerHandler<E, R> = NetworkingObfuscationMarker & {
|
|
128
|
-
[k in keyof Functions<E>]: E[k] extends
|
|
163
|
+
[k in keyof Functions<E>]: IsRawMember<E[k]> extends true
|
|
129
164
|
? RawServerSender<FunctionParameters<E[k]>, FunctionReturn<E[k]>>
|
|
130
|
-
: ServerSender<FunctionParameters<E[k]>, FunctionReturn<E[k]
|
|
165
|
+
: ServerSender<FunctionParameters<E[k]>, FunctionReturn<E[k]>, E[k]>;
|
|
131
166
|
} & {
|
|
132
|
-
[k in keyof Functions<R>]: R[k] extends
|
|
167
|
+
[k in keyof Functions<R>]: IsRawMember<R[k]> extends true
|
|
133
168
|
? RawServerReceiver<FunctionParameters<R[k]>, FunctionReturn<R[k]>>
|
|
134
169
|
: ServerReceiver<FunctionParameters<R[k]>, FunctionReturn<R[k]>, R[k]>;
|
|
135
170
|
} & {
|
|
@@ -139,11 +174,11 @@ export type ServerHandler<E, R> = NetworkingObfuscationMarker & {
|
|
|
139
174
|
};
|
|
140
175
|
|
|
141
176
|
export type ClientHandler<E, R> = NetworkingObfuscationMarker & {
|
|
142
|
-
[k in keyof Functions<E>]: E[k] extends
|
|
177
|
+
[k in keyof Functions<E>]: IsRawMember<E[k]> extends true
|
|
143
178
|
? RawClientSender<FunctionParameters<E[k]>, FunctionReturn<E[k]>>
|
|
144
|
-
: ClientSender<FunctionParameters<E[k]>, FunctionReturn<E[k]
|
|
179
|
+
: ClientSender<FunctionParameters<E[k]>, FunctionReturn<E[k]>, E[k]>;
|
|
145
180
|
} & {
|
|
146
|
-
[k in keyof Functions<R>]: R[k] extends
|
|
181
|
+
[k in keyof Functions<R>]: IsRawMember<R[k]> extends true
|
|
147
182
|
? RawClientReceiver<FunctionParameters<R[k]>, FunctionReturn<R[k]>>
|
|
148
183
|
: ClientReceiver<FunctionParameters<R[k]>, FunctionReturn<R[k]>, R[k]>;
|
|
149
184
|
} & {
|
|
@@ -255,17 +290,16 @@ export type NamespaceMetadata<R, S> = Modding.Emit<{
|
|
|
255
290
|
outgoing: IntrinsicObfuscate<{ [k in keyof Functions<S>]: Modding.Target.Guard<Awaited<ReturnType<S[k]>>> }>;
|
|
256
291
|
|
|
257
292
|
/**
|
|
258
|
-
* Decoders
|
|
259
|
-
*
|
|
260
|
-
* and results are packed inline where they are
|
|
293
|
+
* Decoders for the functions that are packed (all of them with `networking.serialization` on, else
|
|
294
|
+
* the serialized ones), absent for raw functions: the argument lists of requests this realm receives
|
|
295
|
+
* and the responses to requests it sends. Requests and results are packed inline where they are
|
|
296
|
+
* produced.
|
|
261
297
|
*/
|
|
262
298
|
incomingSerializers: IntrinsicObfuscate<{
|
|
263
|
-
[k in keyof Functions<R>]: R[k]
|
|
264
|
-
? undefined
|
|
265
|
-
: IntrinsicNetworkDecoder<Parameters<R[k]>>;
|
|
299
|
+
[k in keyof Functions<R>]: IntrinsicNetworkDecoder<Parameters<R[k]>, R[k], k>;
|
|
266
300
|
}>;
|
|
267
301
|
outgoingResults: IntrinsicObfuscate<{
|
|
268
|
-
[k in keyof Functions<S>]: S[k]
|
|
302
|
+
[k in keyof Functions<S>]: IntrinsicNetworkResultDecoder<S[k], k>;
|
|
269
303
|
}>;
|
|
270
304
|
|
|
271
305
|
namespaceIds: ObfuscateNames<keyof FunctionNamespaces<R> | keyof FunctionNamespaces<S>>;
|
package/out/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Skip as NetworkingSkip } from "./middleware/skip";
|
|
|
4
4
|
import { NetworkingFunctionError } from "./function/errors";
|
|
5
5
|
import { EventMiddleware as _EventMiddleware, FunctionMiddleware as _FunctionMiddleware } from "./middleware/types";
|
|
6
6
|
import { SignalConnection as _SignalConnection } from "./util/signal";
|
|
7
|
-
import { NetworkRaw, NetworkUnreliable } from "./types";
|
|
7
|
+
import { NetworkInfo as _NetworkInfo, NetworkRaw, NetworkSerialized, NetworkUnreliable } from "./types";
|
|
8
8
|
import type { Modding } from "@flamework-experimental/core";
|
|
9
9
|
export declare namespace Networking {
|
|
10
10
|
/**
|
|
@@ -44,6 +44,27 @@ export declare namespace Networking {
|
|
|
44
44
|
* A function whose requests and results bypass serialization; see {@link RawReliable}.
|
|
45
45
|
*/
|
|
46
46
|
type Raw<T> = NetworkRaw<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Packs this event's arguments into a buffer even when `networking.serialization` is off, exactly
|
|
49
|
+
* as the switch would. With the switch on, it changes nothing.
|
|
50
|
+
*
|
|
51
|
+
* `Unreliable<Serialized<T>>` and `Serialized<Unreliable<T>>` are the same as `SerializedUnreliable<T>`.
|
|
52
|
+
* It cannot be combined with `Raw`.
|
|
53
|
+
*/
|
|
54
|
+
type SerializedReliable<T> = NetworkSerialized<T>;
|
|
55
|
+
/**
|
|
56
|
+
* An unreliable event whose arguments are packed into a buffer; see {@link SerializedReliable}.
|
|
57
|
+
*/
|
|
58
|
+
type SerializedUnreliable<T> = NetworkUnreliable<NetworkSerialized<T>>;
|
|
59
|
+
/**
|
|
60
|
+
* A function whose requests and results are packed into a buffer; see {@link SerializedReliable}.
|
|
61
|
+
*/
|
|
62
|
+
type Serialized<T> = NetworkSerialized<T>;
|
|
63
|
+
/**
|
|
64
|
+
* What a middleware factory receives as its second argument: the event or function's `name`,
|
|
65
|
+
* `globalName` and `eventType`.
|
|
66
|
+
*/
|
|
67
|
+
type NetworkInfo = _NetworkInfo;
|
|
47
68
|
/**
|
|
48
69
|
* A function that generates an event middleware.
|
|
49
70
|
*/
|
package/out/init.luau
CHANGED
|
@@ -60,6 +60,31 @@ do
|
|
|
60
60
|
*
|
|
61
61
|
* A function whose requests and results bypass serialization; see {@link RawReliable}.
|
|
62
62
|
|
|
63
|
+
]]
|
|
64
|
+
--[[
|
|
65
|
+
*
|
|
66
|
+
* Packs this event's arguments into a buffer even when `networking.serialization` is off, exactly
|
|
67
|
+
* as the switch would. With the switch on, it changes nothing.
|
|
68
|
+
*
|
|
69
|
+
* `Unreliable<Serialized<T>>` and `Serialized<Unreliable<T>>` are the same as `SerializedUnreliable<T>`.
|
|
70
|
+
* It cannot be combined with `Raw`.
|
|
71
|
+
|
|
72
|
+
]]
|
|
73
|
+
--[[
|
|
74
|
+
*
|
|
75
|
+
* An unreliable event whose arguments are packed into a buffer; see {@link SerializedReliable}.
|
|
76
|
+
|
|
77
|
+
]]
|
|
78
|
+
--[[
|
|
79
|
+
*
|
|
80
|
+
* A function whose requests and results are packed into a buffer; see {@link SerializedReliable}.
|
|
81
|
+
|
|
82
|
+
]]
|
|
83
|
+
--[[
|
|
84
|
+
*
|
|
85
|
+
* What a middleware factory receives as its second argument: the event or function's `name`,
|
|
86
|
+
* `globalName` and `eventType`.
|
|
87
|
+
|
|
63
88
|
]]
|
|
64
89
|
--[[
|
|
65
90
|
*
|
|
@@ -7,4 +7,4 @@ import { Guards } from "./processor";
|
|
|
7
7
|
* The generated argument checks of one event or function, for the receive pipeline, which runs them
|
|
8
8
|
* ahead of all user middleware. A failure warns (with `warnOnInvalid`) and fires `onBadRequest`.
|
|
9
9
|
*/
|
|
10
|
-
export declare function createGuards(name: string, fixedParameters: t.check<unknown>[], restParameter: t.check<unknown> | undefined, networkInfo: NetworkInfo, warnOnInvalid: boolean, signals: SignalContainer<EventNetworkingEvents>): Guards;
|
|
10
|
+
export declare function createGuards(name: string, fixedParameters: t.check<unknown>[], restParameter: t.check<unknown> | undefined, parametersAfterRest: t.check<unknown>[] | undefined, networkInfo: NetworkInfo, warnOnInvalid: boolean, signals: SignalContainer<EventNetworkingEvents>): Guards;
|
|
@@ -7,10 +7,11 @@ local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Pl
|
|
|
7
7
|
* ahead of all user middleware. A failure warns (with `warnOnInvalid`) and fires `onBadRequest`.
|
|
8
8
|
|
|
9
9
|
]]
|
|
10
|
-
local function createGuards(name, fixedParameters, restParameter, networkInfo, warnOnInvalid, signals)
|
|
10
|
+
local function createGuards(name, fixedParameters, restParameter, parametersAfterRest, networkInfo, warnOnInvalid, signals)
|
|
11
11
|
return {
|
|
12
12
|
fixed = fixedParameters,
|
|
13
13
|
rest = restParameter,
|
|
14
|
+
after = parametersAfterRest,
|
|
14
15
|
reject = function(player, index, value)
|
|
15
16
|
if warnOnInvalid then
|
|
16
17
|
if player then
|
|
@@ -15,6 +15,9 @@ export interface Guards {
|
|
|
15
15
|
/** Checks every argument past the fixed ones, for a rest parameter. */
|
|
16
16
|
rest: t.check<unknown> | undefined;
|
|
17
17
|
|
|
18
|
+
/** With a rest parameter, `after[i]` checks the argument `i` places after the rest (`(...args: [...B[], C])`). */
|
|
19
|
+
after?: ReadonlyArray<t.check<unknown>>;
|
|
20
|
+
|
|
18
21
|
/** Called with the first argument that failed, by its 0-based index. */
|
|
19
22
|
reject: (player: Player | undefined, index: number, value: unknown) => void;
|
|
20
23
|
}
|
|
@@ -74,8 +74,9 @@ end
|
|
|
74
74
|
that does not call it drops the message there. `final` ends the chain.
|
|
75
75
|
|
|
76
76
|
`guards`, when given, check the arguments before any middleware runs: `fixed[i]` checks argument
|
|
77
|
-
`i`, `rest` every one past them
|
|
78
|
-
|
|
77
|
+
`i`, `rest` every one past them, and `after` (for a rest parameter with parameters after it) the
|
|
78
|
+
last ones. The first one that fails is reported through `reject` with its 0-based index, and the
|
|
79
|
+
processor returns `rejected` without calling anything else.
|
|
79
80
|
|
|
80
81
|
`cancelled` is what a Promise that was cancelled reads as (`Networking.Skip` for a function).
|
|
81
82
|
]]
|
|
@@ -105,9 +106,43 @@ local function createProcessor(
|
|
|
105
106
|
|
|
106
107
|
local fixed = guards.fixed
|
|
107
108
|
local rest = guards.rest
|
|
109
|
+
local after = guards.after
|
|
108
110
|
local reject = guards.reject
|
|
109
111
|
local fixedCount = #fixed
|
|
110
112
|
|
|
113
|
+
if after ~= nil and #after > 0 then
|
|
114
|
+
local afterCount = #after
|
|
115
|
+
|
|
116
|
+
-- `(...args: [A, ...B[], C])`: A first, C last, and every argument between them a B.
|
|
117
|
+
return function(player, ...)
|
|
118
|
+
local count = select("#", ...)
|
|
119
|
+
while count > 0 and select(count, ...) == nil do
|
|
120
|
+
count -= 1
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
local total = math.max(fixedCount + afterCount, count)
|
|
124
|
+
local restEnd = total - afterCount
|
|
125
|
+
for index = 1, total do
|
|
126
|
+
local guard
|
|
127
|
+
if index <= fixedCount then
|
|
128
|
+
guard = fixed[index]
|
|
129
|
+
elseif index > restEnd then
|
|
130
|
+
guard = after[index - restEnd]
|
|
131
|
+
else
|
|
132
|
+
guard = rest
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
local value = select(index, ...)
|
|
136
|
+
if guard ~= nil and not guard(value) then
|
|
137
|
+
reject(player, index - 1, value)
|
|
138
|
+
return rejected
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
return follow(callTrimmed(head, player, ...), cancelled)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
111
146
|
return function(player, ...)
|
|
112
147
|
-- Checked up to the last value: a trailing nil is an argument that was not passed.
|
|
113
148
|
local count = select("#", ...)
|
package/out/types.d.ts
CHANGED
|
@@ -26,6 +26,31 @@ export type NetworkUnreliable<T> = T & { _flamework_unreliable: never };
|
|
|
26
26
|
*/
|
|
27
27
|
export type NetworkRaw<T> = T & { _flamework_raw: never };
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Marks an event or function whose values are packed into a buffer whether or not the project turns
|
|
31
|
+
* `networking.serialization` on: the call sites encode and the metadata decodes, exactly as the switch
|
|
32
|
+
* would have them do.
|
|
33
|
+
*/
|
|
34
|
+
export type NetworkSerialized<T> = T & { _flamework_serialized: never };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A member declared raw and nothing else. One that is also serialized gets the marked handler
|
|
38
|
+
* instead, so that the transformer sees it where it is used and reports the conflict.
|
|
39
|
+
*/
|
|
40
|
+
export type IsRawMember<T> =
|
|
41
|
+
T extends NetworkRaw<unknown> ? (T extends NetworkSerialized<unknown> ? false : true) : false;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* How the member `F` is packed. Each sender and function receiver takes it as a type argument of its
|
|
45
|
+
* own and carries it as the hidden `_flamework_packing`, so members that are packed differently have
|
|
46
|
+
* unrelated handler types. (Worked out from `F` inside the interface, it would be compared through
|
|
47
|
+
* `F`, which a `Serialized` member's type extends.) A union of them, from a conditional or a helper
|
|
48
|
+
* that returns one of several members, then keeps every one of them rather than reducing to the one
|
|
49
|
+
* the others extend, and the transformer refuses a call that cannot pack for all of them.
|
|
50
|
+
*/
|
|
51
|
+
export type NetworkPacking<F> =
|
|
52
|
+
IsRawMember<F> extends true ? "raw" : F extends NetworkSerialized<unknown> ? "serialized" : "plain";
|
|
53
|
+
|
|
29
54
|
export interface NetworkingObfuscationMarker {
|
|
30
55
|
/**
|
|
31
56
|
* An internal marker type used to signify to Flamework to obfuscate access expressions.
|
|
@@ -53,26 +78,42 @@ export type IntrinsicObfuscateArray<T, V = T> = Modding.Intrinsic<"shuffle-array
|
|
|
53
78
|
export type IntrinsicTupleGuards<T> = Modding.Intrinsic<"tuple-guards", [T], GuardType>;
|
|
54
79
|
|
|
55
80
|
/**
|
|
56
|
-
* Decode code for the argument list `T`, generated
|
|
57
|
-
*
|
|
58
|
-
*
|
|
81
|
+
* Decode code for the argument list `T` of the member `F`, generated when the member is packed: with
|
|
82
|
+
* the project's `networking.serialization` on (unless `F` is raw), or when `F` is serialized.
|
|
83
|
+
* `undefined` otherwise, which passes values through as they are. `K` is the member's name, for the
|
|
84
|
+
* transformer's messages. The matching encoding is generated inline at every call site, so no encoder
|
|
85
|
+
* exists at runtime.
|
|
59
86
|
* @hidden Intrinsic feature not intended for users
|
|
60
87
|
*/
|
|
61
|
-
export type IntrinsicNetworkDecoder<T extends Array<unknown
|
|
88
|
+
export type IntrinsicNetworkDecoder<T extends Array<unknown>, F = unknown, K = unknown> = Modding.Intrinsic<
|
|
62
89
|
"network-decoder",
|
|
63
|
-
[T],
|
|
90
|
+
[T, F, K],
|
|
64
91
|
Serialization.Decoder<T> | undefined
|
|
65
92
|
>;
|
|
66
93
|
|
|
67
94
|
/**
|
|
68
95
|
* Decode code for the result of the function type `F`, carried as a one-element list. Takes the
|
|
69
|
-
* function type rather than its return type so that the return type as declared is known
|
|
96
|
+
* function type rather than its return type so that the return type as declared is known, and so
|
|
97
|
+
* that its markers are; see {@link IntrinsicNetworkDecoder} for `K`.
|
|
70
98
|
* @hidden Intrinsic feature not intended for users
|
|
71
99
|
*/
|
|
72
|
-
export type IntrinsicNetworkResultDecoder<F> = Modding.Intrinsic<
|
|
100
|
+
export type IntrinsicNetworkResultDecoder<F, K = unknown> = Modding.Intrinsic<
|
|
73
101
|
"network-result-decoder",
|
|
74
|
-
[F],
|
|
102
|
+
[F, K],
|
|
75
103
|
Serialization.Decoder | undefined
|
|
76
104
|
>;
|
|
77
105
|
|
|
78
|
-
|
|
106
|
+
/**
|
|
107
|
+
* `true` for a member declared unreliable, `undefined` otherwise. An intrinsic rather than a
|
|
108
|
+
* conditional type so that every member of an event network, in both directions, passes through the
|
|
109
|
+
* transformer's check of its markers wherever a handler is created.
|
|
110
|
+
* @hidden Intrinsic feature not intended for users
|
|
111
|
+
*/
|
|
112
|
+
export type IntrinsicNetworkUnreliable<F, K = unknown> = Modding.Intrinsic<
|
|
113
|
+
"network-unreliable",
|
|
114
|
+
[F, K],
|
|
115
|
+
true | undefined
|
|
116
|
+
>;
|
|
117
|
+
|
|
118
|
+
/** The guards of the arguments before a rest parameter, of the rest, and of any after it (`[A, ...B[], C]`). */
|
|
119
|
+
type GuardType = [t.check<unknown>[], t.check<unknown> | undefined, t.check<unknown>[]?];
|
package/package.json
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamework-experimental/networking",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.4",
|
|
4
4
|
"main": "out/init.luau",
|
|
5
5
|
"types": "out/index.d.ts",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Velover/ExperimentalFlameworkV2.git",
|
|
9
|
+
"directory": "packages/networking"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Velover/ExperimentalFlameworkV2#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Velover/ExperimentalFlameworkV2/issues"
|
|
14
|
+
},
|
|
6
15
|
"scripts": {
|
|
7
16
|
"build": "rbxtsc",
|
|
8
17
|
"watch": "rbxtsc -w"
|