@flamework-experimental/networking 2.0.0-alpha.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/README.md +66 -0
- package/flamework.build +7 -0
- package/out/event/createEvent.d.ts +59 -0
- package/out/event/createEvent.luau +114 -0
- package/out/event/createRemoteInstance.d.ts +1 -0
- package/out/event/createRemoteInstance.luau +58 -0
- package/out/events/createClientMethod.d.ts +5 -0
- package/out/events/createClientMethod.luau +30 -0
- package/out/events/createGenericHandler.d.ts +5 -0
- package/out/events/createGenericHandler.luau +102 -0
- package/out/events/createNetworkingEvent.d.ts +2 -0
- package/out/events/createNetworkingEvent.luau +60 -0
- package/out/events/createServerMethod.d.ts +13 -0
- package/out/events/createServerMethod.luau +63 -0
- package/out/events/types.d.ts +220 -0
- package/out/function/createFunctionReceiver.d.ts +52 -0
- package/out/function/createFunctionReceiver.luau +118 -0
- package/out/function/createFunctionSender.d.ts +45 -0
- package/out/function/createFunctionSender.luau +135 -0
- package/out/function/errors.d.ts +10 -0
- package/out/function/errors.luau +25 -0
- package/out/functions/createClientMethod.d.ts +15 -0
- package/out/functions/createClientMethod.luau +52 -0
- package/out/functions/createGenericHandler.d.ts +7 -0
- package/out/functions/createGenericHandler.luau +140 -0
- package/out/functions/createNetworkingFunction.d.ts +2 -0
- package/out/functions/createNetworkingFunction.luau +68 -0
- package/out/functions/createServerMethod.d.ts +15 -0
- package/out/functions/createServerMethod.luau +52 -0
- package/out/functions/types.d.ts +289 -0
- package/out/handlers.d.ts +36 -0
- package/out/index.d.ts +55 -0
- package/out/init.luau +78 -0
- package/out/middleware/createGuardMiddleware.d.ts +6 -0
- package/out/middleware/createGuardMiddleware.luau +44 -0
- package/out/middleware/createMiddlewareProcessor.d.ts +3 -0
- package/out/middleware/createMiddlewareProcessor.luau +24 -0
- package/out/middleware/skip.d.ts +8 -0
- package/out/middleware/skip.luau +22 -0
- package/out/middleware/types.d.ts +15 -0
- package/out/types.d.ts +78 -0
- package/out/util/createSignalContainer.d.ts +5 -0
- package/out/util/createSignalContainer.luau +30 -0
- package/out/util/getNamespaceConfig.d.ts +10 -0
- package/out/util/getNamespaceConfig.luau +15 -0
- package/out/util/timeoutPromise.d.ts +1 -0
- package/out/util/timeoutPromise.luau +10 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Flamework
|
|
2
|
+
|
|
3
|
+
Flamework is an extensible framework for roblox-ts designed around portable, isolated and testable modules.
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
**[docs/](docs/README.md)** -- start there. A ten-part guide that builds up from a working entry
|
|
8
|
+
point to plugins and project layout, plus reference material:
|
|
9
|
+
|
|
10
|
+
| | |
|
|
11
|
+
|---|---|
|
|
12
|
+
| [Guide](docs/README.md#guide) | Getting started, modules, providers, lifecycle events, components, networking, macros, plugins, project structure, migrating from v1. |
|
|
13
|
+
| [Internals](docs/reference/internals.md) | What the transformer does to your code and what the runtime does with the result. |
|
|
14
|
+
| [Transformer plugins](docs/reference/transformer-plugins.md) | Adding macro types of your own. |
|
|
15
|
+
|
|
16
|
+
The Flamework website documents v1, most of which no longer applies:
|
|
17
|
+
|
|
18
|
+
https://flamework.fireboltofdeath.dev/docs/introduction
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
This repository is a [Bun](https://bun.sh) workspace. It also needs
|
|
23
|
+
[Lune](https://lune-org.github.io/docs) on `PATH` to run the runtime specs.
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
bun install
|
|
27
|
+
bun run build # builds every package in dependency order
|
|
28
|
+
bun run test # build + transformer tests + runtime specs
|
|
29
|
+
bun run lint
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Packages
|
|
33
|
+
|
|
34
|
+
| Package | Description |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `packages/core` | Modules, dependency injection, plugins and lifecycle events |
|
|
37
|
+
| `packages/components` | CollectionService components, built on the core plugin system |
|
|
38
|
+
| `packages/networking` | Remote events and functions |
|
|
39
|
+
| `packages/testing` | In-place tests: sections, cleanup, a bindable and a remote to run them, a cloud entry; and `flamework-test`, the CLI that runs them in Roblox Studio on this machine or through Open Cloud (`cli/`) |
|
|
40
|
+
| `packages/transformer` | The roblox-ts transformer |
|
|
41
|
+
| `packages/transformer-plugin` | Public API for writing transformer plugins |
|
|
42
|
+
| `packages/specs` | Runtime specs, compiled by `rbxtsc` and executed under Lune |
|
|
43
|
+
|
|
44
|
+
### Tests
|
|
45
|
+
|
|
46
|
+
Two suites, both run by `bun run test`:
|
|
47
|
+
|
|
48
|
+
- **Transformer tests** (`bun run test:unit`) compile a fixture project with the real `rbxtsc` and
|
|
49
|
+
assert on the emitted Luau — guard generation, identifiers, nested macros and the plugin system.
|
|
50
|
+
- **Runtime specs** (`bun run test:runtime`) execute compiled `@flamework-experimental/core`, `components` and
|
|
51
|
+
`networking` under Lune using the harness in [`tests/runtime`](tests/runtime), which models
|
|
52
|
+
roblox-ts's `TS.import` tree over the filesystem and stubs the Roblox API surface Flamework
|
|
53
|
+
touches (Instances, attributes, CollectionService, RemoteEvents, Players, signals, `task`,
|
|
54
|
+
`Enum`, and a `Heartbeat` pump so `Promise.delay` -- and therefore request timeouts -- runs).
|
|
55
|
+
They cover dependency injection, modules, hooks and the per-frame lifecycle events, component
|
|
56
|
+
construction, dependencies and streaming, and both halves of networking: events, functions,
|
|
57
|
+
middleware and the generated guards.
|
|
58
|
+
|
|
59
|
+
They run twice, once as `Server` and once as `Client`, because realm-dependent code paths --
|
|
60
|
+
`@Provider`'s metadata, component streaming, and the client/server halves of networking -- differ
|
|
61
|
+
between them. Where a spec asserts something realm-specific, running it from both sides is what
|
|
62
|
+
proves the two agree: a function receives on `$name` and sends on `@name` from the server and the
|
|
63
|
+
mirror image from the client, so the pair of runs pins the wire format down from both ends.
|
|
64
|
+
|
|
65
|
+
Specs live in [`packages/specs`](packages/specs) and are compiled by `rbxtsc` like any other
|
|
66
|
+
Flamework consumer, so they exercise the transformer and the runtime together.
|
package/flamework.build
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Serialization } from "@flamework-experimental/core";
|
|
2
|
+
import { MiddlewareFactory, MiddlewareProcessor } from "../middleware/types";
|
|
3
|
+
import { NetworkInfo } from "../types";
|
|
4
|
+
export interface CreateEventOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The namespace this event should be created in.
|
|
7
|
+
*/
|
|
8
|
+
namespace: string;
|
|
9
|
+
/**
|
|
10
|
+
* The name of the remote instance, not necessarily unique and used for debugging purposes.
|
|
11
|
+
*/
|
|
12
|
+
debugName: string;
|
|
13
|
+
/**
|
|
14
|
+
* The remote's ID which must be unique.
|
|
15
|
+
*/
|
|
16
|
+
id: string;
|
|
17
|
+
/**
|
|
18
|
+
* Information about the network, which includes some of the above.
|
|
19
|
+
*
|
|
20
|
+
* Passed to the middleware.
|
|
21
|
+
*/
|
|
22
|
+
networkInfo: NetworkInfo;
|
|
23
|
+
/**
|
|
24
|
+
* The reliability of this remote.
|
|
25
|
+
*
|
|
26
|
+
* Defaults to reliable.
|
|
27
|
+
*/
|
|
28
|
+
reliability?: "reliable" | "unreliable";
|
|
29
|
+
/**
|
|
30
|
+
* A list of middleware that this event uses when it receives an event.
|
|
31
|
+
*/
|
|
32
|
+
incomingMiddleware?: MiddlewareFactory<any[], void>[];
|
|
33
|
+
/**
|
|
34
|
+
* Unpacks the argument list of incoming events. Absent when the project does not enable
|
|
35
|
+
* serialization. Outgoing lists reach `fire*` already packed: the transformer generates the
|
|
36
|
+
* encoding inline at each call site.
|
|
37
|
+
*/
|
|
38
|
+
incomingDecoder?: Serialization.Decoder;
|
|
39
|
+
/**
|
|
40
|
+
* Called when an incoming payload cannot be decoded; the event is dropped.
|
|
41
|
+
*/
|
|
42
|
+
onMalformed?: (player: Player | undefined, message: string) => void;
|
|
43
|
+
}
|
|
44
|
+
export interface EventInterface {
|
|
45
|
+
fireEither(player: Player | undefined, ...args: unknown[]): void;
|
|
46
|
+
fireServer(...args: unknown[]): void;
|
|
47
|
+
fireClient(player: Player, ...args: unknown[]): void;
|
|
48
|
+
fireAllClients(...args: unknown[]): void;
|
|
49
|
+
connectServer(callback: (player: Player, ...args: unknown[]) => void): RBXScriptConnection;
|
|
50
|
+
connectClient(callback: (...args: unknown[]) => void): RBXScriptConnection;
|
|
51
|
+
invoke: MiddlewareProcessor<any[], void>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The argument list a remote delivered: `args` as they are without a decoder, otherwise the buffer
|
|
55
|
+
* and blob list unpacked. `undefined` when the payload was malformed, after reporting it through
|
|
56
|
+
* `onMalformed`. Decoding runs under `pcall`: a hostile buffer raises instead of yielding garbage.
|
|
57
|
+
*/
|
|
58
|
+
export declare function decodeArguments(decoder: Serialization.Decoder | undefined, player: Player | undefined, args: unknown[], onMalformed?: (player: Player | undefined, message: string) => void): unknown[] | undefined;
|
|
59
|
+
export declare function createEvent(options: CreateEventOptions): EventInterface;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
4
|
+
local createRemoteInstance = TS.import(script, script.Parent, "createRemoteInstance").createRemoteInstance
|
|
5
|
+
local createMiddlewareProcessor = TS.import(script, script.Parent.Parent, "middleware", "createMiddlewareProcessor").createMiddlewareProcessor
|
|
6
|
+
--* A sender whose type has blob slots always sends a table; one without never does.
|
|
7
|
+
local NO_BLOBS = {}
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* The argument list a remote delivered: `args` as they are without a decoder, otherwise the buffer
|
|
11
|
+
* and blob list unpacked. `undefined` when the payload was malformed, after reporting it through
|
|
12
|
+
* `onMalformed`. Decoding runs under `pcall`: a hostile buffer raises instead of yielding garbage.
|
|
13
|
+
|
|
14
|
+
]]
|
|
15
|
+
local function decodeArguments(decoder, player, args, onMalformed)
|
|
16
|
+
if not decoder then
|
|
17
|
+
return args
|
|
18
|
+
end
|
|
19
|
+
local _binding = args
|
|
20
|
+
local payload = _binding[1]
|
|
21
|
+
local blobs = _binding[2]
|
|
22
|
+
if not (type(payload) == "buffer") or (blobs ~= nil and not (type(blobs) == "table")) then
|
|
23
|
+
local _result = onMalformed
|
|
24
|
+
if _result ~= nil then
|
|
25
|
+
_result(player, "payload is not a buffer with an optional blob list")
|
|
26
|
+
end
|
|
27
|
+
return nil
|
|
28
|
+
end
|
|
29
|
+
local ok, result = pcall(decoder, payload, (blobs or NO_BLOBS))
|
|
30
|
+
if not ok then
|
|
31
|
+
local _result = onMalformed
|
|
32
|
+
if _result ~= nil then
|
|
33
|
+
_result(player, tostring(result))
|
|
34
|
+
end
|
|
35
|
+
return nil
|
|
36
|
+
end
|
|
37
|
+
return result
|
|
38
|
+
end
|
|
39
|
+
local function createEvent(options)
|
|
40
|
+
local remote = createRemoteInstance(if options.reliability == "unreliable" then "UnreliableRemoteEvent" else "RemoteEvent", options.namespace, options.debugName, options.id)
|
|
41
|
+
local bindable
|
|
42
|
+
local invoke = createMiddlewareProcessor(options.incomingMiddleware, options.networkInfo, function(player, ...)
|
|
43
|
+
local args = { ... }
|
|
44
|
+
if RunService:IsServer() then
|
|
45
|
+
bindable:Fire(player, unpack(args))
|
|
46
|
+
else
|
|
47
|
+
bindable:Fire(unpack(args))
|
|
48
|
+
end
|
|
49
|
+
end)
|
|
50
|
+
local receive = function(player, args)
|
|
51
|
+
local decoded = decodeArguments(options.incomingDecoder, player, args, options.onMalformed)
|
|
52
|
+
if decoded then
|
|
53
|
+
invoke(player, unpack(decoded))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
local createConnection = function(callback)
|
|
57
|
+
if bindable then
|
|
58
|
+
return bindable.Event:Connect(callback)
|
|
59
|
+
end
|
|
60
|
+
bindable = Instance.new("BindableEvent")
|
|
61
|
+
-- We defer to allow any other immediate connections to take place before unloading Roblox's queue.
|
|
62
|
+
task.defer(function()
|
|
63
|
+
if RunService:IsServer() then
|
|
64
|
+
remote.OnServerEvent:Connect(function(player, ...)
|
|
65
|
+
local args = { ... }
|
|
66
|
+
return receive(player, args)
|
|
67
|
+
end)
|
|
68
|
+
else
|
|
69
|
+
remote.OnClientEvent:Connect(function(...)
|
|
70
|
+
local args = { ... }
|
|
71
|
+
return receive(nil, args)
|
|
72
|
+
end)
|
|
73
|
+
end
|
|
74
|
+
end)
|
|
75
|
+
return bindable.Event:Connect(callback)
|
|
76
|
+
end
|
|
77
|
+
return {
|
|
78
|
+
fireEither = function(self, player, ...)
|
|
79
|
+
local args = { ... }
|
|
80
|
+
if player then
|
|
81
|
+
self:fireClient(player, unpack(args))
|
|
82
|
+
else
|
|
83
|
+
self:fireServer(unpack(args))
|
|
84
|
+
end
|
|
85
|
+
end,
|
|
86
|
+
fireServer = function(self, ...)
|
|
87
|
+
local args = { ... }
|
|
88
|
+
remote:FireServer(unpack(args))
|
|
89
|
+
end,
|
|
90
|
+
fireClient = function(self, player, ...)
|
|
91
|
+
local args = { ... }
|
|
92
|
+
remote:FireClient(player, unpack(args))
|
|
93
|
+
end,
|
|
94
|
+
fireAllClients = function(self, ...)
|
|
95
|
+
local args = { ... }
|
|
96
|
+
remote:FireAllClients(unpack(args))
|
|
97
|
+
end,
|
|
98
|
+
connectServer = function(self, callback)
|
|
99
|
+
local _arg0 = RunService:IsServer()
|
|
100
|
+
assert(_arg0)
|
|
101
|
+
return createConnection(callback)
|
|
102
|
+
end,
|
|
103
|
+
connectClient = function(self, callback)
|
|
104
|
+
local _arg0 = RunService:IsClient()
|
|
105
|
+
assert(_arg0)
|
|
106
|
+
return createConnection(callback)
|
|
107
|
+
end,
|
|
108
|
+
invoke = invoke,
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
return {
|
|
112
|
+
decodeArguments = decodeArguments,
|
|
113
|
+
createEvent = createEvent,
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createRemoteInstance<T extends "RemoteEvent" | "UnreliableRemoteEvent">(remoteType: T, namespace: string, debugName: string, id: string): CreatableInstances[T];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
|
|
4
|
+
local ReplicatedStorage = _services.ReplicatedStorage
|
|
5
|
+
local RunService = _services.RunService
|
|
6
|
+
local function findByAttribute(parent, id)
|
|
7
|
+
for _, child in parent:GetChildren() do
|
|
8
|
+
if child:GetAttribute("id") == id then
|
|
9
|
+
return child
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
local function waitByAttribute(parent, id)
|
|
14
|
+
local instance = findByAttribute(parent, id)
|
|
15
|
+
local watcherThread = task.delay(5, function()
|
|
16
|
+
warn(`Flamework is waiting on '{id}' for`, parent)
|
|
17
|
+
end)
|
|
18
|
+
if not instance then
|
|
19
|
+
-- A connection rather than `ChildAdded.Wait()` in a loop: with deferred signals, children
|
|
20
|
+
-- added while the loop is between waits are never seen, so the wait never ends.
|
|
21
|
+
local thread = coroutine.running()
|
|
22
|
+
local connection
|
|
23
|
+
connection = parent.ChildAdded:Connect(function(child)
|
|
24
|
+
if child:GetAttribute("id") == id then
|
|
25
|
+
connection:Disconnect()
|
|
26
|
+
task.spawn(thread, child)
|
|
27
|
+
end
|
|
28
|
+
end)
|
|
29
|
+
instance = (coroutine.yield())
|
|
30
|
+
end
|
|
31
|
+
task.cancel(watcherThread)
|
|
32
|
+
return instance
|
|
33
|
+
end
|
|
34
|
+
local function createRemoteInstance(remoteType, namespace, debugName, id)
|
|
35
|
+
if not RunService:IsRunning() then
|
|
36
|
+
return Instance.new(remoteType)
|
|
37
|
+
end
|
|
38
|
+
local namespaceFolder = if RunService:IsServer() then findByAttribute(ReplicatedStorage, namespace) else waitByAttribute(ReplicatedStorage, namespace)
|
|
39
|
+
if not namespaceFolder then
|
|
40
|
+
namespaceFolder = Instance.new("Folder")
|
|
41
|
+
namespaceFolder.Name = namespace
|
|
42
|
+
namespaceFolder:SetAttribute("id", namespace)
|
|
43
|
+
namespaceFolder.Parent = ReplicatedStorage
|
|
44
|
+
end
|
|
45
|
+
local existingInstance = if RunService:IsServer() then findByAttribute(namespaceFolder, id) else waitByAttribute(namespaceFolder, id)
|
|
46
|
+
if not existingInstance then
|
|
47
|
+
existingInstance = Instance.new(remoteType)
|
|
48
|
+
existingInstance.Name = debugName
|
|
49
|
+
existingInstance:SetAttribute("id", id)
|
|
50
|
+
existingInstance.Parent = namespaceFolder
|
|
51
|
+
end
|
|
52
|
+
local _arg0 = existingInstance:IsA(remoteType)
|
|
53
|
+
assert(_arg0)
|
|
54
|
+
return existingInstance
|
|
55
|
+
end
|
|
56
|
+
return {
|
|
57
|
+
createRemoteInstance = createRemoteInstance,
|
|
58
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ClientReceiver, ClientSender } from "./types";
|
|
2
|
+
import { EventInterface } from "../event/createEvent";
|
|
3
|
+
type ClientMethod = ClientSender<never[]> & ClientReceiver<never[]>;
|
|
4
|
+
export declare function createClientMethod(receiver: EventInterface, sender: EventInterface): ClientMethod;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function createClientMethod(receiver, sender)
|
|
3
|
+
local method = {
|
|
4
|
+
fire = function(self, ...)
|
|
5
|
+
local args = { ... }
|
|
6
|
+
sender:fireServer(unpack(args))
|
|
7
|
+
end,
|
|
8
|
+
_fire = function(self, ...)
|
|
9
|
+
local packed = { ... }
|
|
10
|
+
sender:fireServer(unpack(packed))
|
|
11
|
+
end,
|
|
12
|
+
connect = function(self, callback)
|
|
13
|
+
return receiver:connectClient(callback)
|
|
14
|
+
end,
|
|
15
|
+
predict = function(self, ...)
|
|
16
|
+
local args = { ... }
|
|
17
|
+
return receiver.invoke(nil, unpack(args))
|
|
18
|
+
end,
|
|
19
|
+
}
|
|
20
|
+
setmetatable(method, {
|
|
21
|
+
__call = function(method, ...)
|
|
22
|
+
local args = { ... }
|
|
23
|
+
method:fire(unpack(args))
|
|
24
|
+
end,
|
|
25
|
+
})
|
|
26
|
+
return method
|
|
27
|
+
end
|
|
28
|
+
return {
|
|
29
|
+
createClientMethod = createClientMethod,
|
|
30
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EventNetworkingEvents } from "../handlers";
|
|
2
|
+
import { ClientHandler, EventCreateConfiguration, NamespaceMetadata, ServerHandler } from "./types";
|
|
3
|
+
import { SignalContainer } from "../util/createSignalContainer";
|
|
4
|
+
import { EventInterface } from "../event/createEvent";
|
|
5
|
+
export declare function createGenericHandler<T extends ClientHandler<S, R> | ServerHandler<S, R>, S, R>(globalName: string, namespaceName: string | undefined, metadata: NamespaceMetadata<R, S>, config: EventCreateConfiguration<R>, signals: SignalContainer<EventNetworkingEvents>, method: (receiver: EventInterface, sender: EventInterface) => unknown): T;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createGuardMiddleware = TS.import(script, script.Parent.Parent, "middleware", "createGuardMiddleware").createGuardMiddleware
|
|
4
|
+
local createEvent = TS.import(script, script.Parent.Parent, "event", "createEvent").createEvent
|
|
5
|
+
local getNamespaceConfig = TS.import(script, script.Parent.Parent, "util", "getNamespaceConfig").getNamespaceConfig
|
|
6
|
+
local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
|
|
7
|
+
local function createGenericHandler(globalName, namespaceName, metadata, config, signals, method)
|
|
8
|
+
local handler = {}
|
|
9
|
+
local _set = {}
|
|
10
|
+
for _, _v in metadata.incomingIds do
|
|
11
|
+
_set[_v] = true
|
|
12
|
+
end
|
|
13
|
+
local receiverNameSet = _set
|
|
14
|
+
local _set_1 = {}
|
|
15
|
+
for _, _v in metadata.outgoingIds do
|
|
16
|
+
_set_1[_v] = true
|
|
17
|
+
end
|
|
18
|
+
local senderNameSet = _set_1
|
|
19
|
+
local _set_2 = {}
|
|
20
|
+
local _array = {}
|
|
21
|
+
local _length = #_array
|
|
22
|
+
local _array_1 = metadata.incomingIds
|
|
23
|
+
local _Length = #_array_1
|
|
24
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
25
|
+
_length += _Length
|
|
26
|
+
local _array_2 = metadata.outgoingIds
|
|
27
|
+
table.move(_array_2, 1, #_array_2, _length + 1, _array)
|
|
28
|
+
for _, _v in _array do
|
|
29
|
+
_set_2[_v] = true
|
|
30
|
+
end
|
|
31
|
+
for name in _set_2 do
|
|
32
|
+
local isIncoming = receiverNameSet[name] ~= nil
|
|
33
|
+
local isOutgoing = senderNameSet[name] ~= nil
|
|
34
|
+
-- If there is no incoming/outgoing event, use the same reliability as the other.
|
|
35
|
+
local incomingChannel = if isIncoming then metadata.incomingUnreliable else metadata.outgoingUnreliable
|
|
36
|
+
local outgoingChannel = if isOutgoing then metadata.outgoingUnreliable else metadata.incomingUnreliable
|
|
37
|
+
local isIncomingUnreliable = incomingChannel[name] == true
|
|
38
|
+
local isOutgoingUnreliable = outgoingChannel[name] == true
|
|
39
|
+
local configMiddleware = config.middleware[name]
|
|
40
|
+
local incomingMiddleware = if configMiddleware ~= nil then table.clone(configMiddleware) else {}
|
|
41
|
+
local effectiveName = if namespaceName ~= nil then `{namespaceName}/{name}` else name
|
|
42
|
+
local networkInfo = {
|
|
43
|
+
eventType = "Event",
|
|
44
|
+
name = effectiveName,
|
|
45
|
+
globalName = globalName,
|
|
46
|
+
}
|
|
47
|
+
if not config.disableIncomingGuards and isIncoming then
|
|
48
|
+
local guards = metadata.incoming[name]
|
|
49
|
+
assert(guards)
|
|
50
|
+
local _arg0 = createGuardMiddleware(name, guards[1], guards[2], networkInfo, config.warnOnInvalidGuards, signals)
|
|
51
|
+
table.insert(incomingMiddleware, 1, _arg0)
|
|
52
|
+
end
|
|
53
|
+
-- A malformed serialized payload is reported like a failed guard, with no argument index.
|
|
54
|
+
local onMalformed = function(player, message)
|
|
55
|
+
if config.warnOnInvalidGuards then
|
|
56
|
+
local sender = if player ~= nil then `'{player}'` else "Server"
|
|
57
|
+
warn(`{sender} sent a malformed payload for event '{name}': {message}`)
|
|
58
|
+
end
|
|
59
|
+
signals:fire("onBadRequest", player or Players.LocalPlayer, {
|
|
60
|
+
networkInfo = networkInfo,
|
|
61
|
+
argIndex = -1,
|
|
62
|
+
argValue = message,
|
|
63
|
+
})
|
|
64
|
+
end
|
|
65
|
+
local create = function(unreliable, receives)
|
|
66
|
+
local _object = {
|
|
67
|
+
reliability = if unreliable then "unreliable" else "reliable",
|
|
68
|
+
namespace = globalName,
|
|
69
|
+
id = if unreliable then `unreliable:{effectiveName}` else effectiveName,
|
|
70
|
+
debugName = name,
|
|
71
|
+
networkInfo = networkInfo,
|
|
72
|
+
incomingMiddleware = incomingMiddleware,
|
|
73
|
+
}
|
|
74
|
+
local _left = "incomingDecoder"
|
|
75
|
+
local _result
|
|
76
|
+
if receives then
|
|
77
|
+
local _result_1 = metadata.incomingSerializers
|
|
78
|
+
if _result_1 ~= nil then
|
|
79
|
+
_result_1 = _result_1[name]
|
|
80
|
+
end
|
|
81
|
+
_result = _result_1
|
|
82
|
+
else
|
|
83
|
+
_result = nil
|
|
84
|
+
end
|
|
85
|
+
_object[_left] = _result
|
|
86
|
+
_object.onMalformed = onMalformed
|
|
87
|
+
return createEvent(_object)
|
|
88
|
+
end
|
|
89
|
+
local shared = isOutgoingUnreliable == isIncomingUnreliable
|
|
90
|
+
local receiver = create(isIncomingUnreliable, true)
|
|
91
|
+
local sender = if shared then receiver else create(isOutgoingUnreliable, false)
|
|
92
|
+
handler[name] = method(receiver, sender)
|
|
93
|
+
end
|
|
94
|
+
for _, namespaceId in metadata.namespaceIds do
|
|
95
|
+
local namespace = metadata.namespaces[namespaceId]
|
|
96
|
+
handler[namespaceId] = createGenericHandler(globalName, if namespaceName ~= nil then `{namespaceName}/{namespaceId}` else namespaceId, namespace, getNamespaceConfig(config, namespaceId), signals, method)
|
|
97
|
+
end
|
|
98
|
+
return handler
|
|
99
|
+
end
|
|
100
|
+
return {
|
|
101
|
+
createGenericHandler = createGenericHandler,
|
|
102
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
4
|
+
local createClientMethod = TS.import(script, script.Parent, "createClientMethod").createClientMethod
|
|
5
|
+
local createServerMethod = TS.import(script, script.Parent, "createServerMethod").createServerMethod
|
|
6
|
+
local createSignalContainer = TS.import(script, script.Parent.Parent, "util", "createSignalContainer").createSignalContainer
|
|
7
|
+
local createGenericHandler = TS.import(script, script.Parent, "createGenericHandler").createGenericHandler
|
|
8
|
+
local function getDefaultConfiguration(config)
|
|
9
|
+
local _object = {}
|
|
10
|
+
local _left = "middleware"
|
|
11
|
+
local _condition = config.middleware
|
|
12
|
+
if _condition == nil then
|
|
13
|
+
_condition = {}
|
|
14
|
+
end
|
|
15
|
+
_object[_left] = _condition
|
|
16
|
+
local _left_1 = "warnOnInvalidGuards"
|
|
17
|
+
local _condition_1 = config.warnOnInvalidGuards
|
|
18
|
+
if _condition_1 == nil then
|
|
19
|
+
_condition_1 = RunService:IsStudio()
|
|
20
|
+
end
|
|
21
|
+
_object[_left_1] = _condition_1
|
|
22
|
+
local _left_2 = "disableIncomingGuards"
|
|
23
|
+
local _condition_2 = config.disableIncomingGuards
|
|
24
|
+
if _condition_2 == nil then
|
|
25
|
+
_condition_2 = false
|
|
26
|
+
end
|
|
27
|
+
_object[_left_2] = _condition_2
|
|
28
|
+
return _object
|
|
29
|
+
end
|
|
30
|
+
local function createNetworkingEvent(globalName)
|
|
31
|
+
local signals = createSignalContainer()
|
|
32
|
+
local server
|
|
33
|
+
local client
|
|
34
|
+
return {
|
|
35
|
+
createServer = function(self, config, meta)
|
|
36
|
+
if RunService:IsRunning() and not RunService:IsServer() then
|
|
37
|
+
return nil
|
|
38
|
+
end
|
|
39
|
+
if server == nil then
|
|
40
|
+
server = createGenericHandler(globalName, nil, meta, getDefaultConfiguration(config), signals, createServerMethod)
|
|
41
|
+
end
|
|
42
|
+
return server
|
|
43
|
+
end,
|
|
44
|
+
createClient = function(self, config, meta)
|
|
45
|
+
if RunService:IsRunning() and not RunService:IsClient() then
|
|
46
|
+
return nil
|
|
47
|
+
end
|
|
48
|
+
if client == nil then
|
|
49
|
+
client = createGenericHandler(globalName, nil, meta, getDefaultConfiguration(config), signals, createClientMethod)
|
|
50
|
+
end
|
|
51
|
+
return client
|
|
52
|
+
end,
|
|
53
|
+
registerHandler = function(self, key, callback)
|
|
54
|
+
return signals:connect(key, callback)
|
|
55
|
+
end,
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
return {
|
|
59
|
+
createNetworkingEvent = createNetworkingEvent,
|
|
60
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EventInterface } from "../event/createEvent";
|
|
2
|
+
export declare function createServerMethod(receiver: EventInterface, sender: EventInterface): {
|
|
3
|
+
readonly _flamework_send?: unknown[] | undefined;
|
|
4
|
+
_fire: (players: Player | Player[], payload?: buffer, blobs?: Array<defined>) => void;
|
|
5
|
+
_except: (players: Player | Player[], payload?: buffer, blobs?: Array<defined>) => void;
|
|
6
|
+
_broadcast: (payload?: buffer, blobs?: Array<defined>) => void;
|
|
7
|
+
fire: (players: Player | Player[], ...args: unknown[]) => void;
|
|
8
|
+
except: (players: Player | Player[], ...args: unknown[]) => void;
|
|
9
|
+
broadcast: (...args: unknown[]) => void;
|
|
10
|
+
readonly _flamework_receive?: unknown[] | undefined;
|
|
11
|
+
connect: (cb: (player: Player, ...args: unknown[]) => void) => RBXScriptConnection;
|
|
12
|
+
predict: (player: Player, ...args: unknown[]) => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
|
|
4
|
+
local function createServerMethod(receiver, sender)
|
|
5
|
+
local method = {
|
|
6
|
+
fire = function(self, players, ...)
|
|
7
|
+
local args = { ... }
|
|
8
|
+
local _players = players
|
|
9
|
+
if typeof(_players) == "Instance" then
|
|
10
|
+
sender:fireClient(players, unpack(args))
|
|
11
|
+
else
|
|
12
|
+
for _, player in players do
|
|
13
|
+
sender:fireClient(player, unpack(args))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end,
|
|
17
|
+
broadcast = function(self, ...)
|
|
18
|
+
local args = { ... }
|
|
19
|
+
sender:fireAllClients(unpack(args))
|
|
20
|
+
end,
|
|
21
|
+
except = function(self, players, ...)
|
|
22
|
+
local args = { ... }
|
|
23
|
+
local _players = players
|
|
24
|
+
if typeof(_players) == "Instance" then
|
|
25
|
+
players = { players }
|
|
26
|
+
end
|
|
27
|
+
for _, player in Players:GetPlayers() do
|
|
28
|
+
if not (table.find(players, player) ~= nil) then
|
|
29
|
+
self:fire(player, unpack(args))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end,
|
|
33
|
+
_fire = function(self, players, ...)
|
|
34
|
+
local packed = { ... }
|
|
35
|
+
self:fire(players, unpack(packed))
|
|
36
|
+
end,
|
|
37
|
+
_broadcast = function(self, ...)
|
|
38
|
+
local packed = { ... }
|
|
39
|
+
self:broadcast(unpack(packed))
|
|
40
|
+
end,
|
|
41
|
+
_except = function(self, players, ...)
|
|
42
|
+
local packed = { ... }
|
|
43
|
+
self:except(players, unpack(packed))
|
|
44
|
+
end,
|
|
45
|
+
connect = function(self, callback)
|
|
46
|
+
return receiver:connectServer(callback)
|
|
47
|
+
end,
|
|
48
|
+
predict = function(self, player, ...)
|
|
49
|
+
local args = { ... }
|
|
50
|
+
receiver.invoke(player, unpack(args))
|
|
51
|
+
end,
|
|
52
|
+
}
|
|
53
|
+
setmetatable(method, {
|
|
54
|
+
__call = function(method, player, ...)
|
|
55
|
+
local args = { ... }
|
|
56
|
+
method:fire(player, unpack(args))
|
|
57
|
+
end,
|
|
58
|
+
})
|
|
59
|
+
return method
|
|
60
|
+
end
|
|
61
|
+
return {
|
|
62
|
+
createServerMethod = createServerMethod,
|
|
63
|
+
}
|