This repository has been archived by the owner on Jul 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 57
/
AeroServer.server.lua
396 lines (310 loc) · 10.1 KB
/
AeroServer.server.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
-- Aero Server
-- Stephen Leitnick
-- July 21, 2017
local AeroServer = {
Services = {};
Modules = {};
Shared = {};
}
local mt = {__index = AeroServer}
local servicesFolder = game:GetService("ServerStorage").Aero.Services
local modulesFolder = game:GetService("ServerStorage").Aero.Modules
local sharedFolder = game:GetService("ReplicatedStorage").Aero.Shared
local internalFolder = game:GetService("ReplicatedStorage").Aero.Internal
local remoteServices = Instance.new("Folder")
remoteServices.Name = "AeroRemoteServices"
local players = {}
local modulesAwaitingStart = {}
local SpawnNow = require(sharedFolder.Thread:Clone()).SpawnNow
local Settings = require(internalFolder:WaitForChild("Settings"))
local settingsPerTbl = {}
local function PreventEventRegister()
error("Cannot register event after Init method")
end
local function PreventFunctionRegister()
error("Cannot register function after Init method")
end
local function PreventMethodCache()
error("Cannot mark method as cachable after Init method")
end
function AeroServer:RegisterEvent(eventName)
local event = self.Shared.Signal.new()
self._events[eventName] = event
return event
end
function AeroServer:RegisterClientEvent(eventName)
local event = Instance.new("RemoteEvent")
event.Name = eventName
event.Parent = self._remoteFolder
self._clientEvents[eventName] = event
return event
end
function AeroServer:Fire(eventName, ...)
self._events[eventName]:Fire(...)
end
function AeroServer:FireEvent(eventName, ...)
warn("FireEvent has been deprecated in favor of Fire")
self:Fire(eventName, ...)
end
function AeroServer:FireClient(eventName, client, ...)
self._clientEvents[eventName]:FireClient(client, ...)
end
function AeroServer:FireClientEvent(eventName, client, ...)
warn("FireClientEvent has been deprecated in favor of FireClient")
self:FireClient(eventName, client, ...)
end
function AeroServer:FireAllClients(eventName, ...)
self._clientEvents[eventName]:FireAllClients(...)
end
function AeroServer:FireAllClientsEvent(eventName, ...)
warn("FireAllClientsEvent has been deprecated in favor of FireAllClients")
self:FireAllClients(eventName, ...)
end
function AeroServer:FireOtherClients(eventName, clientIgnore, ...)
local event = self._clientEvents[eventName]
for _,player in ipairs(players) do
if (player ~= clientIgnore) then
event:FireClient(player, ...)
end
end
end
function AeroServer:FireAllClientsEventExcept(eventName, client, ...)
warn("FireAllClientsEventExcept has been deprecated in favor of FireOtherClients")
self:FireOtherClients(eventName, client, ...)
end
function AeroServer:ConnectEvent(eventName, func)
return self._events[eventName]:Connect(func)
end
function AeroServer:ConnectClientEvent(eventName, func)
return self._clientEvents[eventName].OnServerEvent:Connect(func)
end
function AeroServer:WaitForEvent(eventName)
return self._events[eventName]:Wait()
end
function AeroServer:WaitForClientEvent(eventName)
return self._clientEvents[eventName]:Wait()
end
function AeroServer:RegisterClientFunction(funcName, func, cacheTTL)
local remoteFunc = Instance.new("RemoteFunction")
remoteFunc.Name = funcName
remoteFunc.OnServerInvoke = function(...)
return func(self.Client, ...)
end
if (cacheTTL ~= nil) then
local cache = Instance.new("NumberValue")
cache.Name = "Cache"
cache.Value = cacheTTL
cache.Parent = remoteFunc
end
remoteFunc.Parent = self._remoteFolder
return remoteFunc
end
function AeroServer:WrapModule(tbl)
assert(type(tbl) == "table", "Expected table for argument")
tbl._events = {}
setmetatable(tbl, mt)
local objSettings = (settingsPerTbl[tbl] or Settings:GetDefault())
if (type(tbl.Init) == "function" and not (objSettings.PreventInit or tbl.__aeroPreventInit)) then
tbl:Init()
end
if (type(tbl.Start) == "function" and not (objSettings.PreventStart or tbl.__aeroPreventStart)) then
if (modulesAwaitingStart) then
modulesAwaitingStart[#modulesAwaitingStart + 1] = tbl
else
SpawnNow(tbl.Start, tbl)
end
end
return tbl
end
function AeroServer:CacheClientMethod(methodName, ttl)
assert(self._clientCaches, "CacheClientMethod must be called within Init method")
assert(type(methodName) == "string", "CacheClientMethod argument #1 must be a string")
assert(self.Client and type(self.Client[methodName]) == "function", "CacheClientMethod argument #1 must be a client method")
if (ttl == nil) then ttl = 0 end
assert(type(ttl) == "number" and ttl >= 0, "CacheClientMethod argument #2 must be a number >= 0")
self._clientCaches[methodName] = (ttl or 0)
end
-- Setup table to load modules on demand:
local function LazyLoadSetup(tbl, folder)
setmetatable(tbl, {
__index = function(t, i)
local child = folder[i]
if (child:IsA("ModuleScript")) then
local objSettings = Settings:Get(child)
local obj = require(child)
settingsPerTbl[obj] = objSettings
rawset(t, i, obj)
if (type(obj) == "table" and not objSettings.Standalone) then
-- Only wrap module if it's actually a table, and not a table disguised as a function:
local objMetatable = getmetatable(obj)
if (not (objMetatable and objMetatable.__call)) then
AeroServer:WrapModule(obj)
end
end
return obj
elseif (child:IsA("Folder")) then
local nestedTbl = {}
rawset(t, i, nestedTbl)
LazyLoadSetup(nestedTbl, child)
return nestedTbl
end
end;
})
end
-- Load service from module:
local function LoadService(module, servicesTbl, parentFolder)
local serviceSettings = Settings:Get(module)
local remoteFolder = Instance.new("Folder")
remoteFolder.Name = module.Name
remoteFolder.Parent = parentFolder
local service = require(module)
servicesTbl[module.Name] = service
if (type(service.Client) ~= "table") then
service.Client = {}
end
service.Client.Server = service
setmetatable(service, mt)
service._events = {}
service._clientEvents = {}
service._clientCaches = {}
service._remoteFolder = remoteFolder
settingsPerTbl[service] = serviceSettings
end
local function InitService(service)
-- Initialize:
if (type(service.Init) == "function") then
service:Init()
end
-- Client functions:
for funcName,func in pairs(service.Client) do
if (type(func) == "function") then
service:RegisterClientFunction(funcName, func, service._clientCaches[funcName])
end
end
-- Disallow registering events/functions after init:
service.RegisterEvent = PreventEventRegister
service.RegisterClientEvent = PreventEventRegister
service.RegisterClientFunction = PreventFunctionRegister
service.CacheClientMethod = PreventMethodCache
end
local function StartService(service)
-- Start services on separate threads:
if (type(service.Start) == "function") then
SpawnNow(service.Start, service)
end
end
local function Init()
local function PlayerAdded(player)
players[#players + 1] = player
end
local function PlayerRemoving(player)
local nPlayers = #players
for i = 1,nPlayers do
if (players[i] == player) then
players[i] = players[nPlayers]
players[nPlayers] = nil
end
end
end
-- Load service modules:
local function LoadAllServices(parent, servicesTbl, parentFolder)
for _,child in ipairs(parent:GetChildren()) do
if (child:IsA("ModuleScript")) then
if (not Settings:IsSettingsModule(child)) then
LoadService(child, servicesTbl, parentFolder)
end
elseif (child:IsA("Folder")) then
local tbl = {}
local folder = Instance.new("Folder")
folder.Name = child.Name
folder.Parent = parentFolder
servicesTbl[child.Name] = tbl
LoadAllServices(child, tbl, folder)
end
end
end
-- Initialize services:
local function InitAllServices(services)
-- Collect all services:
local serviceTables = {}
local function CollectServices(svcs)
for _,service in pairs(svcs) do
if (getmetatable(service) == mt) then
serviceTables[#serviceTables + 1] = service
else
CollectServices(service)
end
end
end
CollectServices(services)
-- Sort services by optional Order setting or __aeroOrder field:
local function GetOrder(service)
local svcSettings = settingsPerTbl[service]
local order
if (type(svcSettings.Order) == "number") then
order = svcSettings.Order
elseif (type(service.__aeroOrder) == "number") then
order = service.__aeroOrder
else
order = Settings.InternalSettings.DefaultOrder
end
return order
end
table.sort(serviceTables, function(a, b)
return (GetOrder(a) < GetOrder(b))
end)
-- Initialize services:
for _,service in ipairs(serviceTables) do
InitService(service)
end
end
-- Remove unused folders:
local function ScanRemoteFoldersForEmpty(parent)
for _,child in ipairs(parent:GetChildren()) do
if (child:IsA("Folder")) then
local remoteFunction = child:FindFirstChildWhichIsA("RemoteFunction", true)
local remoteEvent = child:FindFirstChildWhichIsA("RemoteEvent", true)
if ((not remoteFunction) and (not remoteEvent)) then
child:Destroy()
else
ScanRemoteFoldersForEmpty(child)
end
end
end
end
-- Start services:
local function StartAllServices(services)
for _,service in pairs(services) do
if (getmetatable(service) == mt) then
StartService(service)
else
StartAllServices(service)
end
end
end
-- Start modules that were already loaded:
local function StartLoadedModules()
for _,tbl in pairs(modulesAwaitingStart) do
SpawnNow(tbl.Start, tbl)
end
modulesAwaitingStart = nil
end
--------------------------------------------------------------------
players = game:GetService("Players"):GetPlayers()
game:GetService("Players").PlayerAdded:Connect(PlayerAdded)
game:GetService("Players").PlayerRemoving:Connect(PlayerRemoving)
-- Lazy-load server and shared modules:
LazyLoadSetup(AeroServer.Modules, modulesFolder)
LazyLoadSetup(AeroServer.Shared, sharedFolder)
-- Load, init, and start services:
LoadAllServices(servicesFolder, AeroServer.Services, remoteServices)
InitAllServices(AeroServer.Services)
ScanRemoteFoldersForEmpty(remoteServices)
StartAllServices(AeroServer.Services)
StartLoadedModules()
-- Expose server framework to client and global scope:
remoteServices.Parent = game:GetService("ReplicatedStorage").Aero
_G.AeroServer = AeroServer
_G.Aero = AeroServer
end
Init()