@skating/swiftpacket 0.1.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/LICENSE +21 -0
- package/README.md +123 -0
- package/package.json +31 -0
- package/src/client.luau +472 -0
- package/src/configuration.luau +74 -0
- package/src/cursor.luau +18 -0
- package/src/index.d.ts +122 -0
- package/src/init.luau +199 -0
- package/src/packet.luau +186 -0
- package/src/rate_limit.luau +38 -0
- package/src/registry.luau +40 -0
- package/src/schema.luau +1606 -0
- package/src/server.luau +691 -0
- package/src/signal.luau +145 -0
- package/src/thread_pool.luau +37 -0
- package/src/wire.luau +218 -0
package/src/signal.luau
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
|
|
4
|
+
--[[
|
|
5
|
+
Author: @skaterstudios
|
|
6
|
+
Website: skaterstudios.com
|
|
7
|
+
GitHub: github.com/skatingii/SwiftPacket
|
|
8
|
+
Discord Server: discord.gg/skaterstudios
|
|
9
|
+
|
|
10
|
+
Licensed under the MIT License. You may use, modify, redistribute and sell
|
|
11
|
+
this system freely, as long as this notice and the LICENSE file stay with it.
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
const ThreadPool = require("./thread_pool")
|
|
15
|
+
|
|
16
|
+
const Signal = {}
|
|
17
|
+
Signal.__index = Signal
|
|
18
|
+
|
|
19
|
+
const Connection = {}
|
|
20
|
+
Connection.__index = Connection
|
|
21
|
+
|
|
22
|
+
export type SignalFields = {
|
|
23
|
+
Head: Connection?,
|
|
24
|
+
Tail: Connection?,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ConnectionFields = {
|
|
28
|
+
Connected: boolean,
|
|
29
|
+
Once: boolean,
|
|
30
|
+
Callback: ((...any) -> ())?,
|
|
31
|
+
Thread: thread?,
|
|
32
|
+
Owner: Signal,
|
|
33
|
+
Previous: Connection?,
|
|
34
|
+
Next: Connection?,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type Signal = setmetatable<SignalFields, typeof(Signal)>
|
|
38
|
+
export type Connection = setmetatable<ConnectionFields, typeof(Connection)>
|
|
39
|
+
|
|
40
|
+
function Signal.New(): Signal
|
|
41
|
+
const State: SignalFields = {}
|
|
42
|
+
|
|
43
|
+
const self: Signal = setmetatable(State, Signal)
|
|
44
|
+
|
|
45
|
+
return self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
const function Attach(Owner: Signal, Callback: ((...any) -> ())?, Thread: thread?, Once: boolean): Connection
|
|
49
|
+
const State: ConnectionFields = {
|
|
50
|
+
Connected = true,
|
|
51
|
+
Once = Once,
|
|
52
|
+
Callback = Callback,
|
|
53
|
+
Thread = Thread,
|
|
54
|
+
Owner = Owner,
|
|
55
|
+
Previous = Owner.Tail,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const Node: Connection = setmetatable(State, Connection)
|
|
59
|
+
const Tail = Owner.Tail
|
|
60
|
+
|
|
61
|
+
if Tail then
|
|
62
|
+
Tail.Next = Node
|
|
63
|
+
else
|
|
64
|
+
Owner.Head = Node
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
Owner.Tail = Node
|
|
68
|
+
|
|
69
|
+
return Node
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
function Signal.Connect(self: Signal, Callback: (...any) -> ()): Connection
|
|
73
|
+
return Attach(self, Callback, nil, false)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
function Signal.Once(self: Signal, Callback: (...any) -> ()): Connection
|
|
77
|
+
return Attach(self, Callback, nil, true)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
function Signal.Wait(self: Signal): ...any
|
|
81
|
+
Attach(self, nil, coroutine.running(), true)
|
|
82
|
+
|
|
83
|
+
return coroutine.yield()
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
function Signal.Fire(self: Signal, ...: any)
|
|
87
|
+
local Node = self.Head
|
|
88
|
+
|
|
89
|
+
while Node do
|
|
90
|
+
if Node.Connected then
|
|
91
|
+
if Node.Once then
|
|
92
|
+
Node:Disconnect()
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
const Callback = Node.Callback
|
|
96
|
+
const Thread = Node.Thread
|
|
97
|
+
|
|
98
|
+
if Callback then
|
|
99
|
+
ThreadPool.Defer(Callback, ...)
|
|
100
|
+
elseif Thread then
|
|
101
|
+
task.defer(Thread, ...)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Node = Node.Next
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
function Signal.DisconnectAll(self: Signal)
|
|
110
|
+
local Node = self.Head
|
|
111
|
+
|
|
112
|
+
while Node do
|
|
113
|
+
Node.Connected = false
|
|
114
|
+
Node = Node.Next
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
self.Head = nil
|
|
118
|
+
self.Tail = nil
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
function Connection.Disconnect(self: Connection)
|
|
122
|
+
if not self.Connected then
|
|
123
|
+
return
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
self.Connected = false
|
|
127
|
+
|
|
128
|
+
const Owner = self.Owner
|
|
129
|
+
const Previous = self.Previous
|
|
130
|
+
const Next = self.Next
|
|
131
|
+
|
|
132
|
+
if Previous then
|
|
133
|
+
Previous.Next = Next
|
|
134
|
+
else
|
|
135
|
+
Owner.Head = Next
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if Next then
|
|
139
|
+
Next.Previous = Previous
|
|
140
|
+
else
|
|
141
|
+
Owner.Tail = Previous
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return Signal
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
|
|
4
|
+
--[[
|
|
5
|
+
Author: @skaterstudios
|
|
6
|
+
Website: skaterstudios.com
|
|
7
|
+
GitHub: github.com/skatingii/SwiftPacket
|
|
8
|
+
Discord Server: discord.gg/skaterstudios
|
|
9
|
+
|
|
10
|
+
Licensed under the MIT License. You may use, modify, redistribute and sell
|
|
11
|
+
this system freely, as long as this notice and the LICENSE file stay with it.
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
const ThreadPool = {}
|
|
15
|
+
|
|
16
|
+
const Threads: { thread } = {}
|
|
17
|
+
|
|
18
|
+
const function Call(Callback: (...any) -> (), ...: any)
|
|
19
|
+
Callback(...)
|
|
20
|
+
table.insert(Threads, coroutine.running())
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
const function Loop()
|
|
24
|
+
while true do
|
|
25
|
+
Call(coroutine.yield())
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
const function Acquire(): thread
|
|
30
|
+
return table.remove(Threads) or task.spawn(Loop)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
function ThreadPool.Defer(Callback: (...any) -> (), ...: any)
|
|
34
|
+
task.defer(Acquire(), Callback, ...)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return ThreadPool
|
package/src/wire.luau
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
|
|
4
|
+
--[[
|
|
5
|
+
Author: @skaterstudios
|
|
6
|
+
Website: skaterstudios.com
|
|
7
|
+
GitHub: github.com/skatingii/SwiftPacket
|
|
8
|
+
Discord Server: discord.gg/skaterstudios
|
|
9
|
+
|
|
10
|
+
Licensed under the MIT License. You may use, modify, redistribute and sell
|
|
11
|
+
this system freely, as long as this notice and the LICENSE file stay with it.
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
const Cursor = require("./cursor")
|
|
15
|
+
const Registry = require("./registry")
|
|
16
|
+
const Schema = require("./schema")
|
|
17
|
+
|
|
18
|
+
export type Batch = {
|
|
19
|
+
Count: number,
|
|
20
|
+
Definitions: { Registry.Definition },
|
|
21
|
+
CallIndices: { number },
|
|
22
|
+
Successes: { boolean },
|
|
23
|
+
Starts: { number },
|
|
24
|
+
Counts: { number },
|
|
25
|
+
Values: { any },
|
|
26
|
+
StopOffset: number,
|
|
27
|
+
StopInstances: number,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Wire = {}
|
|
31
|
+
|
|
32
|
+
const ResponseFlag = 32768
|
|
33
|
+
const EventIndex = -1
|
|
34
|
+
|
|
35
|
+
const ReadVarint = Cursor.ReadVarint
|
|
36
|
+
const ReadU16 = Cursor.ReadU16
|
|
37
|
+
const ReadU8 = Cursor.ReadU8
|
|
38
|
+
const Ended = Cursor.Ended
|
|
39
|
+
const Position = Cursor.Position
|
|
40
|
+
|
|
41
|
+
const function VarintSize(Value: number): number
|
|
42
|
+
return if Value < 128 then 1 elseif Value < 16384 then 2 elseif Value < 2097152 then 3 else 4
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Wire.ResponseFlag = ResponseFlag
|
|
46
|
+
Wire.EventIndex = EventIndex
|
|
47
|
+
|
|
48
|
+
function Wire.Encode(
|
|
49
|
+
Target: Cursor.Cursor,
|
|
50
|
+
Schemas: { Schema.Schema },
|
|
51
|
+
Values: Schema.Arguments,
|
|
52
|
+
Offset: number,
|
|
53
|
+
Name: string
|
|
54
|
+
)
|
|
55
|
+
Cursor.Reset(Target)
|
|
56
|
+
Cursor.Import(Target)
|
|
57
|
+
Schema.WriteArguments(Schemas, Values, Offset, Name)
|
|
58
|
+
Cursor.Export()
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
function Wire.Deliver(Target: Cursor.Cursor, Id: number, CallIndex: number?, Status: number?, Body: Cursor.Cursor?)
|
|
62
|
+
Cursor.Import(Target)
|
|
63
|
+
Cursor.WriteId(Id)
|
|
64
|
+
|
|
65
|
+
if CallIndex then
|
|
66
|
+
Cursor.Allocate(2)
|
|
67
|
+
Cursor.WriteU16(CallIndex)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if Status then
|
|
71
|
+
Cursor.Allocate(1)
|
|
72
|
+
Cursor.WriteU8(Status)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if Body then
|
|
76
|
+
Cursor.Append(Body)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
Cursor.Export()
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
function Wire.DeliverResponse(Target: Cursor.Cursor, Id: number, CallIndex: number, Body: Cursor.Cursor?)
|
|
83
|
+
if Body then
|
|
84
|
+
Wire.Deliver(Target, Id, CallIndex + ResponseFlag, 1, Body)
|
|
85
|
+
else
|
|
86
|
+
Wire.Deliver(Target, Id, CallIndex + ResponseFlag, 0, nil)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
function Wire.EncodeResults(
|
|
91
|
+
Target: Cursor.Cursor,
|
|
92
|
+
Definition: Registry.Definition,
|
|
93
|
+
Results: Schema.Arguments
|
|
94
|
+
): (boolean, string?)
|
|
95
|
+
const Responses = Definition.Responses
|
|
96
|
+
|
|
97
|
+
if Responses == nil then
|
|
98
|
+
return false, `{Definition.Name} has no response types`
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if Results[1] ~= true then
|
|
102
|
+
return false, `the handler for {Definition.Name} errored: {tostring(Results[2])}`
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
const Ok, Problem = pcall(Wire.Encode, Target, Responses, Results, 1, "")
|
|
106
|
+
|
|
107
|
+
if not Ok then
|
|
108
|
+
return false, `could not write the response for {Definition.Name}: {tostring(Problem)}`
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
return true, nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
function Wire.Size(Id: number, CallIndex: number?, Body: Cursor.Cursor): number
|
|
115
|
+
return VarintSize(Id) + (if CallIndex then 2 else 0) + Body.Offset
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
function Wire.NewBatch(): Batch
|
|
119
|
+
return {
|
|
120
|
+
Count = 0,
|
|
121
|
+
Definitions = {},
|
|
122
|
+
CallIndices = {},
|
|
123
|
+
Successes = {},
|
|
124
|
+
Starts = {},
|
|
125
|
+
Counts = {},
|
|
126
|
+
Values = {},
|
|
127
|
+
StopOffset = 0,
|
|
128
|
+
StopInstances = 0,
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
function Wire.Decode(
|
|
133
|
+
Target: Batch,
|
|
134
|
+
Source: buffer,
|
|
135
|
+
Instances: { any },
|
|
136
|
+
Untrusted: boolean,
|
|
137
|
+
Offset: number,
|
|
138
|
+
StartInstances: number
|
|
139
|
+
): number?
|
|
140
|
+
Cursor.Load(Source, Instances, Untrusted, Offset, StartInstances)
|
|
141
|
+
|
|
142
|
+
const Definitions = Target.Definitions
|
|
143
|
+
const CallIndices = Target.CallIndices
|
|
144
|
+
const Successes = Target.Successes
|
|
145
|
+
const Starts = Target.Starts
|
|
146
|
+
const Counts = Target.Counts
|
|
147
|
+
const Values = Target.Values
|
|
148
|
+
|
|
149
|
+
local Count = 0
|
|
150
|
+
local Top = 0
|
|
151
|
+
|
|
152
|
+
Target.Count = 0
|
|
153
|
+
|
|
154
|
+
while not Ended() do
|
|
155
|
+
const Id = ReadVarint()
|
|
156
|
+
const Definition = Registry.ById[Id]
|
|
157
|
+
|
|
158
|
+
if Definition == nil then
|
|
159
|
+
const Reached, ReachedInstances = Position()
|
|
160
|
+
|
|
161
|
+
Target.StopOffset = Reached - VarintSize(Id)
|
|
162
|
+
Target.StopInstances = ReachedInstances
|
|
163
|
+
|
|
164
|
+
return Id
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
const Responses = Definition.Responses
|
|
168
|
+
local CallIndex = EventIndex
|
|
169
|
+
local Success = true
|
|
170
|
+
local Schemas = Definition.Arguments
|
|
171
|
+
|
|
172
|
+
if Responses then
|
|
173
|
+
CallIndex = ReadU16()
|
|
174
|
+
|
|
175
|
+
if CallIndex >= ResponseFlag then
|
|
176
|
+
Success = ReadU8() == 1
|
|
177
|
+
Schemas = Responses
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
Count += 1
|
|
182
|
+
Definitions[Count] = Definition
|
|
183
|
+
CallIndices[Count] = CallIndex
|
|
184
|
+
Successes[Count] = Success
|
|
185
|
+
Starts[Count] = Top + 1
|
|
186
|
+
|
|
187
|
+
if Success then
|
|
188
|
+
const Size = #Schemas
|
|
189
|
+
|
|
190
|
+
if Size == 1 then
|
|
191
|
+
Top += 1
|
|
192
|
+
Values[Top] = Schemas[1].Read()
|
|
193
|
+
else
|
|
194
|
+
for Index = 1, Size do
|
|
195
|
+
Top += 1
|
|
196
|
+
Values[Top] = Schemas[Index].Read()
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
Counts[Count] = Top + 1 - Starts[Count]
|
|
202
|
+
Target.Count = Count
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
return nil
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
function Wire.Release(Target: Batch)
|
|
209
|
+
table.clear(Target.Values)
|
|
210
|
+
table.clear(Target.Definitions)
|
|
211
|
+
Target.Count = 0
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
function Wire.Unpack(Values: Schema.Arguments): ...any
|
|
215
|
+
return table.unpack(Values, 1, Values.n)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
return Wire
|