· 5 years ago · Jul 25, 2020, 01:46 PM
1--[[
2
3 -------
4 Voidage
5 -------
6
7 Thank you for deciding to use Forged Loader!
8
9 Forged Loader is a largely OOP based Framework, with a lot of inspiration (obviously) from Aero Game Framework.
10 (Thank you so much Crazyman32 for the default modules in AGF. I've included some/all with this release.)
11
12 In addition to not only working identically to AGF, I have an new type of module that can be added to the "Objects" folder.
13 Any ModuleScript located in this folder will be treated as a proper Class with built-in functions such as .new(), :Wrap(), :Super()
14 Creation of these Objects are simple.
15
16 self.Shared.Objects.MyObject.new();
17 or
18 self.Objects.MyObject.new();
19
20 All Objects DO have access to the same things that services/controllers do.
21
22 Objects have a :Wrap(Roblox Instance) function.
23 Wrapping a created object with a Roblox Instance means that you can get/set/change the properties of that instance easily from within
24 the object. Destroy the Object will also destroy the Roblox Instance, and destroying the Object will destroy the Roblox Instance.
25
26 - Example
27
28 local MyBomb = self.Objects.Bomb.new();
29 MyBomb:Wrap(workspace.Part);
30
31 MyBomb.Transparency = 0.5;
32 MyBomb:Destroy();
33
34]]--
35
36local HttpService = game:GetService("HttpService");
37
38local IsServer = game:GetService("RunService"):IsServer();
39
40local ForgedFolder;
41
42if not IsServer then
43 ForgedFolder = game.Players.LocalPlayer.PlayerScripts:WaitForChild("Forged");
44else
45 ForgedFolder = game:GetService("ServerScriptService"):WaitForChild("Forged");
46end
47
48-- Roblox locations (folders)
49local Locations = {};
50if not IsServer then
51 Locations.Controllers = ForgedFolder:WaitForChild("Controllers");
52 Locations.Services = {};
53else
54 Locations.Services = ForgedFolder:WaitForChild("Services");
55end
56Locations.Objects = ForgedFolder:WaitForChild("Objects");
57Locations.Modules = ForgedFolder:WaitForChild("Modules");
58
59-- Shared Roblox locations.
60Locations.Shared = game:GetService("ReplicatedStorage"):WaitForChild("Forged"):WaitForChild("Shared");
61Locations.Shared_Objects = Locations.Shared:WaitForChild("Objects");
62Locations.Shared_Modules = Locations.Shared:WaitForChild("Modules");
63
64if IsServer then
65 Locations.Remotes = Instance.new("Folder", Locations.Shared);
66 Locations.Remotes.Name = "Remotes";
67else
68 Locations.Remotes = Locations.Shared:WaitForChild("Remotes");
69end
70
71-- Modules to be used by Forged Loader.
72local Maid = require(Locations.Shared_Modules:WaitForChild("Maid"));
73local Event = require(Locations.Shared_Modules:WaitForChild("Event"));
74local Thread = require(Locations.Shared_Modules:WaitForChild("Thread"));
75local StringUtil = require(Locations.Shared_Modules:WaitForChild("StringUtil"));
76
77-- The table that is indexed by wrapped modules.
78local Forged = {};
79Forged.Controllers = {};
80Forged.Objects = {};
81Forged.Modules = {};
82Forged.Services = {};
83
84Forged.Globals = {}; -- Globally registered variables.
85Forged.Registry = {};
86
87-- Shared modules
88Forged.Shared = {};
89Forged.Shared.Objects = {};
90Forged.Shared.Modules = {};
91
92Forged.ObjectCreated = Event.new();
93
94local ObjectCreatedServer;
95
96-- Local variables
97if not IsServer then
98 Forged.Player = game.Players.LocalPlayer;
99end
100
101Forged.Total_Loaded = {};
102Forged.Waiting_Start = {};
103Forged.Waiting_Init = {};
104
105-- Booleans that keep track of what has been loaded,
106Forged.Modules_Loaded = false;
107Forged.Controllers_Loaded = false;
108Forged.Objects_Loaded = false;
109Forged.Services_Loaded = false;
110
111local function index(t, index)
112 if rawget(t, index) then
113 return rawget(t, index);
114 elseif Forged[index] then
115 return rawget(Forged, index);
116 elseif rawget(t, "_Is_Object") then
117 --if rawget(t.Values, index) then return rawget(t.Values, index) end;
118 for i,v in pairs(t._Components) do -- First check components...
119 if rawget(v, index) then
120 return rawget(v, index);
121 end
122 end
123 if rawget(t, index) then return rawget(t, index) end;
124 if rawget(t, "Wrapped") ~= nil then -- Maybe its a part method or property?
125 local result;
126 local success;
127 success, result = pcall(function()
128 result = t.Wrapped[index];
129 end)
130 if success then
131 if typeof(t.Wrapped[index]) ~= "function" then
132 return t.Wrapped[index];
133 else
134 return function()
135 t.Wrapped[index](t.Wrapped);
136 end
137 end
138 end
139 end
140 return nil;
141 end
142end
143
144Forged.__index = index;
145
146local function newindex(a,b,c)
147 if rawget(a, "Wrapped") ~= nil then
148 local result;
149 local success;
150 success, result = pcall(function()
151 result = a.Wrapped[b];
152 end)
153 if success then
154 a.Wrapped[b] = c;
155 return;
156 end
157 end
158 if a._Properties then
159 if a._Properties[b] ~= nil then -- If this is a registered property, then change it and fire event.
160 if a._Clamps[b] then
161 if c < a._Clamps[b][1] then
162 c = a._Clamps[b][1];
163 end
164 if c > a._Clamps[b][2] then
165 c = a._Clamps[b][2];
166 end
167 end
168 a._PropertyEvents[b]:Fire(c, a._Properties[b]);
169 rawset(a._Properties, b, c);
170 return;
171 end
172 end
173 if rawget(a, "_Is_Object") == true then
174 a.Changed:Fire(b, c, a[b]);
175 end
176 rawset(a,b,c);
177end
178
179Forged.__newindex = newindex;
180
181function Forged:Log(...)
182 local str = "";
183 for i,v in pairs({...}) do
184 str = str .. v;
185 end
186 print(str);
187end
188
189function Forged:GetService(ServiceName)
190 if typeof(ServiceName) == "string" then
191 warn("Deprecated - use self.Services." .. ServiceName);
192 end
193 return nil;
194end
195
196-- Allows a module (table) to work with Forged Framework
197function Forged:WrapModule(t, modulescript, skipInit)
198 t._PropertyEvents = {};
199 t._Properties = {};
200 t._Clamps = {};
201 setmetatable(t, Forged);
202 if skipInit == nil then
203 skipInit = false;
204 end
205 if skipInit == false then
206 if t.Init then
207 t:Init();
208 end
209 end
210 return t;
211end
212
213-- Register a global variable which other scripts can access.
214function Forged:RegisterGlobal(PropertyName)
215 if self[PropertyName] then
216 Forged.Globals[PropertyName] = self[PropertyName];
217 end
218end
219
220-- Register a property which fires an event when change.
221function Forged:RegisterProperty(PropertyName)
222 self._Properties[PropertyName] = false;
223 self._PropertyEvents[PropertyName] = Event.new();
224 return {
225 Clamp = function(_, min, max)
226 self._Clamps[PropertyName] = {min, max};
227 end;
228 }
229end
230
231-- Get the event from a registered property.
232function Forged:GetPropertyChangedEvent(PropertyName)
233 if self._PropertyEvents[PropertyName] then
234 return self._PropertyEvents[PropertyName];
235 end
236end
237
238-- Create a new registry.
239function Forged:CreateRegistry(RegistryName)
240 if Forged.Registry[RegistryName] then warn("Registry " .. RegistryName .. " already exists.") return end;
241 Forged.Registry[RegistryName] = {};
242end
243
244-- Remove a registry
245function Forged:RemoteRegistry(RegistryName)
246 if Forged.Registry[RegistryName] then
247 Forged.Registry[RegistryName] = nil;
248 else
249 warn("Attempt to remove registry " .. RegistryName .. " that doesn't exist.");
250 end
251end
252
253-- Get a registry.
254function Forged:GetRegistry(RegistryName)
255 return Forged.Registry[RegistryName];
256end
257
258function Forged:AddTableToRegistry(Table, RegistryName)
259 local registry = Forged:GetRegistry(RegistryName);
260 table.insert(registry, Table);
261end
262
263-- Create add something to the registry.
264function Forged:AddToRegistry(RegistryName)
265 local registry = Forged:GetRegistry(RegistryName);
266 table.insert(registry, self);
267end
268
269
270local function SearchSplit(STR, Location)
271 local found;
272 local iteration;
273
274 local function Search(Location, splitTable, lastSplit, iter)
275 if splitTable[iter] == lastSplit then
276 found = Location[lastSplit];
277 return found;
278 end
279 local f = Location:FindFirstChild(splitTable[iter]);
280 if f then
281 iter = iter + 1;
282 Search(f, splitTable, lastSplit, iter);
283 end
284 end
285
286 local split = string.split(STR, "/");
287 local totalSplits = #split;
288 local lastSplit = split[#split];
289
290 Search(Location, split, lastSplit, 1);
291 return found;
292end
293
294function Forged:GetSharedResource(ResourceName)
295 local found = SearchSplit(ResourceName, Locations.Shared.Resources);
296 return found;
297end
298
299function Forged:GetResource(ResourceName)
300 local found = SearchSplit(ResourceName, ForgedFolder.Resources);
301 return found;
302end
303
304-- Remove from registry
305function Forged:RemoveFromRegistry(RegistryName)
306 local registry = Forged:GetRegistry(RegistryName);
307 if registry then
308 for i,v in pairs(registry) do
309 if v._ID == self._ID then
310 table.remove(registry, i);
311 end
312 end
313 else
314 warn("Attempt to remove object from registry " .. RegistryName .. " that doesn't exist.");
315 end
316end
317
318-- Set as replicated
319function Forged:SetReplicated(Player)
320 self.ReplicatedPlayer = Player;
321 if self.ReplicationChanged then
322 self.ReplicationChanged:Fire(Player);
323 end
324 if Player == nil then
325 self._Replicated = false;
326 else
327 self._Replicated = true;
328 end
329end
330
331-- Server: Create a new event for the client to connect to.
332function Forged:RegisterClientEvent(EventName)
333 local newclientevent = Instance.new("RemoteEvent");
334 local servicefolder = Locations.Remotes:FindFirstChild(self._Name);
335 if not servicefolder then
336 servicefolder = Instance.new("Folder", Locations.Remotes);
337 servicefolder.Name = self._Name;
338 end
339 local newclientevent = Instance.new("RemoteEvent", servicefolder);
340 newclientevent.Name = "ForgedEvent_" .. EventName;
341end
342
343-- Server: Fire a client event
344function Forged:FireClientEvent(EventName, Player, ...)
345 if Locations.Remotes[self._Name]:FindFirstChild("ForgedEvent_" .. EventName) then
346 Locations.Remotes[self._Name]["ForgedEvent_" .. EventName]:FireClient(Player, table.unpack{...});
347 end
348end
349
350-- Server: Fire a client event for ALL clients
351function Forged:FireAllClientsEvent(EventName, ...)
352 --local callingscriptname = getfenv(2).script.Name;
353 if Locations.Remotes[self._Name]:FindFirstChild("ForgedEvent_" .. EventName) then
354 Locations.Remotes[self._Name]["ForgedEvent_" .. EventName]:FireAllClients(table.unpack({...}))
355 end
356end
357
358
359-- Create a new remote.
360local function NewRemote(ServiceName, RemoteName)
361 local servicefolder = Locations.Remotes:FindFirstChild(ServiceName)
362 if not servicefolder then
363 servicefolder = Instance.new("Folder", Locations.Remotes);
364 servicefolder.Name = ServiceName;
365 end
366 local newremote = Instance.new("RemoteFunction");
367 newremote.Name = RemoteName;
368 newremote.Parent = servicefolder;
369 return newremote;
370end
371
372
373local function SetupNetworking(ServiceName, Table)
374 if IsServer then -- Create a metable for the .Client table on the service.
375 if Table.Client then
376 for i,v in pairs(Table.Client) do
377 if typeof(v) == "function" then
378 local newnetworkremote = NewRemote(ServiceName, i);
379 function newnetworkremote.OnServerInvoke(...)
380 local args = {...}
381 local player = args[1];
382 local res = v(player, ...);
383 return res;
384 end
385 end
386 end
387 end
388 end
389end
390
391local function LoadControllersAndServicesToTable(Location, Table)
392 for i,v in pairs(Location:GetChildren()) do
393 if v:IsA("ModuleScript") then
394 local req = require(v);
395 if IsServer then SetupNetworking(v.Name, req) end; -- Setup .Client for Networking.
396 req._Is_Object = false;
397 Table[v.Name] = req;
398 if req.Init then
399 Forged.Waiting_Init[v.Name] = req;
400 end
401 if req.Start then
402 Forged.Waiting_Start[v.Name] = req;
403 end
404 Forged:WrapModule(req, v, true);
405 req._Name = v.Name;
406 elseif v:IsA("Folder") then
407 Table[v.Name] = {};
408 LoadControllersAndServicesToTable(v, Table[v.Name]);
409 end
410 end
411end
412
413local function LoadModulesInLocationToTable(Location, Table)
414 setmetatable(Table, {__index = function(t, i)
415 if Location[i] then
416 local TargetObject = Location[i];
417 if TargetObject:IsA("ModuleScript") then
418 local req = require(TargetObject);
419 Forged:WrapModule(req, TargetObject, false);
420 req._Is_Object = false;
421 Table[TargetObject.Name] = req;
422 return req;
423 elseif TargetObject:IsA("Folder") then
424 Table[TargetObject.Name] = {};
425 LoadModulesInLocationToTable(TargetObject, Table[TargetObject.Name]);
426 return Table[TargetObject.Name];
427 end
428 end
429 end})
430end
431
432
433local function LoadObjectsInLocationToTable(Location, Table)
434 setmetatable(Table, {__index = function(t, i)
435 local mod = Location[i];
436 if Location[i] then
437 local TargetObject = Location[i];
438 if TargetObject:IsA("ModuleScript") then
439 local req = require(TargetObject);
440
441 Forged:WrapModule(req, TargetObject, true);
442
443 for i,v in pairs(TargetObject:GetChildren()) do
444 if v:IsA("ModuleScript") then
445 local childreq = require(v);
446 Forged:WrapModule(childreq, v, true);
447 req[v.Name] = childreq;
448 end
449 end
450
451 req._Components = {};
452 req._Interfaces = {};
453 req._Name = TargetObject.Name;
454 req.Replicated = req.Replicated or false;
455
456 -- Add a class to this objects component list.
457 function req:Super(Class, ...)
458 table.insert(req._Components, Class);
459 --table.insert(req._Components, Class.new(...));
460 if #Class._Components >= 1 then
461 for i,v in pairs(Class._Components) do
462 req:Super(v);
463 end
464 end
465 end
466
467 -- Interfaces
468 function req:Implements(Class, ...)
469 if (Class.Interface) then
470 if (Class.Interface == true) then
471 table.insert(req._Interfaces, Class);
472 if (Class.Contract) then
473 for i,v in pairs(Class.Contract) do
474 if req[v] == nil then
475 error(req._Name .. " must define all methods or properties from any interfaces it implements.");
476 end
477 end
478 end
479 end
480 end
481 end
482
483 function req:Extends(Class, ...)
484 req:Super(Class, ...);
485 end
486
487
488 if req.Init then
489 req:Init();
490 end
491
492
493 -- A new instance of this Object.
494 function req.new(userArgs, ...)
495 local newobject = {};
496
497 newobject.__index = index;
498 newobject.__newindex = newindex;
499
500 setmetatable(newobject, Forged);
501
502 for i,v in pairs(req) do
503 newobject[i] = v;
504 end
505
506
507 newobject._Maid = Maid.new();
508
509 newobject.Loaded = Event.new();
510 newobject.Changed = Event.new();
511 newobject.ReplicationChanged = Event.new();
512
513 newobject._Name = TargetObject.Name;
514 newobject._Replicated = req.Replicated;
515 newobject._ID = HttpService:GenerateGUID(false);
516 newobject._Created_Ran = false;
517 newobject._Is_Object = true;
518 newobject.Wrapped = nil;
519 newobject._Loaded = false;
520 newobject._Replicated = nil;
521
522 -- Everything the maid has will be cleaned on Object:Destroy()
523 function newobject:GiveMaid(Thing)
524 spawn(function()
525 newobject._Maid:GiveTask(Thing);
526 end)
527 end
528
529 -- Call maid cleaning on this object.
530 function newobject:Destroy()
531 newobject._Maid:DoCleaning();
532 end
533
534 -- Attach a ROBLOX instance to this object.
535 -- In the case this Instance is destroyed, the Object will be cleaned up.
536 -- If the Object is destroyed, the wrapped instance will also be destroyed.
537 function newobject:Wrap(RoInstance)
538 newobject.Wrapped = RoInstance;
539 newobject:GiveMaid(RoInstance);
540 -- Detect when this Roblox Instance is destroyed.
541 if RoInstance.Parent then
542 RoInstance.Parent.ChildRemoved:Connect(function(Child)
543 if Child == RoInstance then
544 newobject:Destroy();
545 end
546 end)
547 end
548 end
549
550 function newobject:Replicate(Player)
551 if IsServer then
552 if newobject._Replicated == nil then
553 ObjectCreatedServer:FireClient(Player, TargetObject.Parent, newobject._Name, newobject);
554 end
555 end
556 end
557
558 if newobject.Created then
559 if userArgs then
560 newobject:Created(table.unpack(userArgs));
561 end
562 end
563
564 -- If the object contains component classes,
565 -- then go through and fire the Supered function on them.
566 if #req._Components >= 1 then
567 for i,v in pairs(req._Components) do
568 if v.Supered then
569 v:Supered(newobject, ...);
570 end
571 end
572 end
573
574 newobject.Loaded:Fire();
575 newobject._Loaded = true;
576
577 Forged.ObjectCreated:Fire(newobject);
578
579
580 return newobject;
581 end
582
583 return req; -- Return the object.
584 elseif TargetObject:IsA("Folder") then
585 Table[TargetObject.Name] = {};
586 LoadObjectsInLocationToTable(TargetObject, Table[TargetObject.Name]);
587 return Table[TargetObject.Name];
588 end
589 end
590 end})
591
592end
593
594-- Function that injects methods into the client .Services
595local function ClientLoadNetworking()
596 if not IsServer then
597 for i,v in pairs(Locations.Remotes:GetChildren()) do
598 Forged.Services[v.Name] = {};
599 for a,b in pairs(v:GetChildren()) do
600 if b:IsA("RemoteFunction") or b:IsA("RemoteEvent") then
601 if not StringUtil.StartsWith(b.Name, "ForgedEvent_") then
602 Forged.Services[v.Name][b.Name] = function(Player, ...)
603 local lol = b:InvokeServer(...);
604 return lol;
605 end;
606 else
607 local actualName = string.split(b.Name, "_");
608 actualName = actualName[2];
609 Forged.Services[v.Name][actualName] = Event.new();
610 b.OnClientEvent:Connect(function(...)
611 Forged.Services[v.Name][actualName]:Fire(...);
612 end)
613 end
614 end
615 end
616 end
617 end
618end
619
620local function InitServicesAndControllers()
621 for i,v in pairs(Forged.Waiting_Init) do
622 v:Init();
623 end
624end
625
626local function StartServicesAndControllers()
627 for i,v in pairs(Forged.Waiting_Start) do
628 Thread.Spawn(function()
629 v:Start();
630 end)
631 end
632end
633
634-- Create the Rap event to replicate objects from server to client.
635if IsServer then
636 --ObjectCreatedServer = NewRemote("Replication", "Rep");
637 ObjectCreatedServer = Instance.new("RemoteEvent");
638 ObjectCreatedServer.Name = "ObjectCreatedServer";
639 ObjectCreatedServer.Parent = game:GetService("ReplicatedStorage");
640else
641 ObjectCreatedServer = game:GetService("ReplicatedStorage"):WaitForChild("ObjectCreatedServer");
642 ObjectCreatedServer.OnClientEvent:Connect(function(objectPathParent, objectName, object)
643 local newobject = objectPathParent[objectName].new();
644 newobject.Loaded:Connect(function()
645 for i,v in pairs(object) do
646 if newobject[i] then
647 print'updating value';
648 newobject[i] = v;
649 end
650 end
651 end)
652 end)
653end
654
655-- lazy load main Modules and Objects
656LoadModulesInLocationToTable(Locations.Modules, Forged.Modules);
657LoadObjectsInLocationToTable(Locations.Objects, Forged.Objects);
658
659-- lazy load shared Modules and Objects
660LoadModulesInLocationToTable(Locations.Shared_Modules, Forged.Shared.Modules);
661LoadObjectsInLocationToTable(Locations.Shared_Objects, Forged.Shared.Objects);
662
663-- Load Controllers/Services
664if not IsServer then
665 ClientLoadNetworking();
666 LoadControllersAndServicesToTable(Locations.Controllers, Forged.Controllers);
667else
668 LoadControllersAndServicesToTable(Locations.Services, Forged.Services);
669end
670
671-- Start/Init everything
672InitServicesAndControllers();
673StartServicesAndControllers();
674
675
676return true;