kirda

Framework

Apr 6th, 2021 (edited)
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.99 KB | None | 0 0
  1. --[[
  2. AUTHOR: kirda @imKirda
  3. DATE: April 7 2021
  4.  
  5. DESC:
  6.     Module required by framework server and client script to share functionality
  7.  
  8.     Module startup phrases:
  9.         1: __init__
  10.         2: __initserver__ || __initclient__
  11.         3: __postinit__
  12.         4: __postserverinit__ || __postclientinit__
  13.         5: __start__
  14.         6: __startserver__ || __startclient__
  15.         7: __after__
  16.         8: __afterserver__ || __afterclient__
  17.        
  18.     Expected startup phrase uses:
  19.         1, 2 - Initialize the module
  20.         3, 4 - Require other modules
  21.         5, 6 - Main module logic after all modules are initialized
  22.         7, 8 - Finish module logic after all modules have ran
  23.        
  24. API:
  25. EXTERNAL:
  26.     table getInternal()
  27.         - Returns 'Internal' table for internal use by framework scripts
  28.         - Locked by framework script after calling
  29.        
  30.         NOTE: All contents of 'Internal' table are removed after framework loaded
  31.    
  32.     Instance forEachInstance(Instance)
  33.         - Loops through given instance and returns it's children by order in loop
  34.    
  35.     void assertType(any, string, number)
  36.         - Asserts if given type of parameter one value is second parameter
  37.        
  38.         NOTE: This function should not be used outside of functions
  39.  
  40. INTERNAL:
  41.     table getShared()
  42.         - Returns the "Shared" table to allow server to register remotes
  43.    
  44.     void initialize()
  45.         - Initializes the shared framework module
  46.    
  47.     Folder assertFolder(string, Instance)
  48.         - Returns folder with given name from second parameter Instance if found
  49.         - If folder has not been found, throws an error
  50.    
  51.     void connect(table)
  52.         - Connects shared framework with given (Client or Server) framework table
  53.         - Initializes connected core
  54.    
  55.     void start()
  56.         - Starts all modules
  57.    
  58.     void clear()
  59.         - Clears framework garbage
  60.    
  61.     void lockedFunction()
  62.         - Throws error "This function is locked" to lock some internal functions
  63.         - To lock functions, they are overriden with this one
  64.    
  65.     void lockInternal()
  66.         - Locks internal functions from 'External' table
  67.    
  68.     void finishInitializationPeriod()
  69.         - Locks all functions that are inaccessible after module initialization
  70.    
  71.     void setInstanceName(Instance, string)
  72.         - Sets name of given instance to given string if it's not already named
  73.         like the string to prevent setting property to same value as it is
  74.    
  75.     void setInstanceParent(Instance, Instance)
  76.         - Sets parent of given instance to second parameter instance if it's not
  77.         if it's not already parented to it to prevent setting same parent twice
  78.  
  79. SHARED:
  80.     void registerEvent(string, BindableEvent)
  81.         - Registers bindable event with given name if none are registered
  82.         - If second parameter is not given, creates new event instance
  83.    
  84.     void registerFunction(string, BindableFunction)
  85.         - Registers bindable function with given name if none are registered
  86.         - If second parameter is not given, creates new function instance
  87.    
  88.     void deleteEvent(string)
  89.         - Destroys event with given name if found
  90.    
  91.     void deleteFunction(string)
  92.         - Destroy function with given name if found
  93.    
  94.     void fireEvent(string, any...)
  95.         - Fires event with given name if found, fires tuple as parameters
  96.    
  97.     any... awaitEvent(string)
  98.         - Yields until event with given name fires
  99.         - Returns what the event yielded
  100.    
  101.     any... invokeFunction(string, any...)
  102.         - Invokes function with given name if found and returns what it invoked
  103.         - Passes given tuple as invoke parameters
  104.    
  105.     BindableEvent? getEvent(string)
  106.         - Returns event with given name
  107.    
  108.     BindableFunction? getFunction(string)
  109.         - Returns function with given name
  110.    
  111.     RemoteEvent? getRemoteEvent(string)
  112.         - Returns remote event with given name
  113.    
  114.     RemoteFunction? getRemoteFunction(string)
  115.         - Returns remote function with given name
  116.    
  117.     RBXScriptConnection onEvent(string, function)
  118.         - Connects event with given name to given function if found
  119.    
  120.     void onInvoke(string, function)
  121.         - Sets function with given name's invoke callback to given function
  122.    
  123.     any require(string)
  124.         - Returns module with given name
  125.    
  126.     any requireShared(string)
  127.         - Returns shared module with given name
  128.    
  129. --]]
  130. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  131. local RunService = game:GetService("RunService")
  132.  
  133. local FUNCTION_LOCKED_MESSAGE = "This function is locked"
  134. local DEFAULT_START_ORDER = 9
  135.  
  136. local network_folder = ReplicatedStorage.Network
  137. local modules_folder = ReplicatedStorage.Modules
  138.  
  139. local External = {}
  140. local Internal = {}
  141.  
  142. local Core
  143. local Shared = {
  144.     Functions = {},
  145.     Network = {
  146.         Functions = {},
  147.         Events = {},
  148.     },
  149.     Modules = {},
  150.    
  151.     NetworkFolder = network_folder,
  152. }
  153.  
  154. local is_server = RunService:IsServer()
  155.  
  156. --[[
  157.     if is_server, returns the first value, else returns the second value
  158. --]]
  159. local function server_and_or(and_value, or_value)
  160.     if is_server then
  161.         return and_value
  162.     else
  163.         return or_value
  164.     end
  165. end
  166.  
  167. --[[
  168.     Returns all core and shared modules combined
  169. --]]
  170. local function get_all_modules()
  171.     local modules = {}
  172.    
  173.     local function add(_table)
  174.         for name, module in next, _table do
  175.             modules[name] = module
  176.         end
  177.     end
  178.    
  179.     add(Core.Modules)
  180.     add(Shared.Modules)
  181.    
  182.     return modules
  183. end
  184.  
  185. --[[
  186.     Calls function with given name from given module if found
  187. --]]
  188. local function call_module_function(module, function_name)
  189.     External.assertType(module, "table", 1)
  190.     External.assertType(function_name, "string", 2)
  191.    
  192.     local _function = rawget(module, function_name)
  193.    
  194.     if _function then
  195.         _function()
  196.     end
  197. end
  198.  
  199. --[[
  200.     Returns if module is shared
  201. --]]
  202. local function is_shared(module)
  203.     for _, shared_module in next, Shared.Modules do
  204.         if module == shared_module then
  205.             return true
  206.         end
  207.     end
  208.    
  209.     return false
  210. end
  211.  
  212. function External.getInternal()
  213.     return Internal
  214. end
  215.  
  216. function External.forEachInstance(instance)
  217.     External.assertType(instance, "Instance", 1)
  218.    
  219.     local children = instance:GetChildren()
  220.     local index = 0
  221.    
  222.     return function()
  223.         index += 1
  224.        
  225.         return children[index]
  226.     end
  227. end
  228.  
  229. function External.assertType(given_value, expected_type, argument_index)
  230.     do
  231.         local argument2_type = typeof(expected_type)
  232.         local argument3_type = typeof(argument_index)
  233.        
  234.         if argument2_type ~= "string" then
  235.             error(("invalid argument #2 to 'assertType' (string expected, got %s)"):format(argument2_type), 2)
  236.         end
  237.        
  238.         if argument3_type ~= "number" then
  239.             error(("invalid argument #3 to 'assertType' (number expected, got %s)"):format(argument3_type), 2)
  240.         end
  241.        
  242.         if argument_index <= 0 then
  243.             error("argument #3 to 'assertType' must be higher than 0")
  244.         end
  245.        
  246.         if math.floor(argument_index) ~= argument_index then
  247.             error("argument #3 to 'assertType' must be whole number")
  248.         end
  249.     end
  250.    
  251.     local given_value_type = typeof(given_value)
  252.     local is_invalid = given_value_type ~= expected_type
  253.    
  254.     local is_roblox_type = false
  255.     local is_enum_type = false
  256.    
  257.     if is_invalid then
  258.         if given_value_type == "Instance" then
  259.             is_roblox_type = true
  260.             is_invalid = not given_value:IsA(expected_type)
  261.         elseif given_value_type == "EnumItem" then
  262.             is_enum_type = true
  263.             is_invalid = tostring(given_value.EnumType) ~= expected_type
  264.         end
  265.     end
  266.    
  267.     if is_invalid then
  268.         if is_roblox_type then
  269.             given_value_type = given_value.ClassName
  270.         elseif is_enum_type then
  271.             given_value_type = tostring(given_value.EnumType)
  272.         end
  273.        
  274.         local calling_function_name = debug.info(2, "n")
  275.        
  276.         error(
  277.             string.format(
  278.                 "invalid argument #%d to '%s' (%s expected, got %s)",
  279.                 argument_index,
  280.                 calling_function_name,
  281.                 expected_type,
  282.                 given_value_type
  283.             ),
  284.             3
  285.         )
  286.     end
  287. end
  288.  
  289. function Internal.getShared()
  290.     return Shared
  291. end
  292.  
  293. function Internal.initialize()
  294.     --[[
  295.         Registers or creates new internal remote event with given name
  296.     --]]
  297.     local function create_internal_remote_event(remote_name)
  298.         External.assertType(remote_name, "string", 1)
  299.        
  300.         local existing_event = network_folder:FindFirstChild(remote_name)
  301.        
  302.         if existing_event then
  303.             Shared.Network.Events[remote_name] = existing_event
  304.         else
  305.             local event = Instance.new("RemoteEvent")
  306.             event.Name = remote_name
  307.             event.Parent = network_folder
  308.            
  309.             Shared.Network.Events[remote_name] = event
  310.         end
  311.     end
  312.    
  313.     for remote in External.forEachInstance(network_folder) do
  314.         local remote_directory = nil
  315.         local remote_name = remote.Name
  316.        
  317.         if remote:IsA("RemoteEvent") then
  318.             remote_directory = Shared.Network.Events
  319.         elseif remote:IsA("RemoteFunction") then
  320.             remote_directory = Shared.Network.Functions
  321.         else
  322.             warn(("'%s' is not remote, consider removing it from 'Network' folder"):format(remote_name))
  323.             continue
  324.         end
  325.        
  326.         if remote_directory[remote_name] then
  327.             warn(("'%s' remote event has duplicate, consider renaming it or removing from 'Network' folder"):format(remote_name))
  328.             continue
  329.         end
  330.        
  331.         remote_directory[remote_name] = remote
  332.     end
  333.    
  334.     create_internal_remote_event("@register_remote")
  335. end
  336.  
  337. function Internal.connect(core)
  338.     Core = core
  339.     core = nil
  340.    
  341.     do
  342.         Shared.Functions.forEachInstance = External.forEachInstance
  343.         Shared.Functions.assertType = External.assertType
  344.        
  345.         for name, _function in next, Core.Functions do
  346.             Shared.Functions[name] = _function
  347.         end
  348.        
  349.         Core.Functions = nil
  350.        
  351.         local shared_metatable = {
  352.             __index = Shared.Functions,
  353.         }
  354.        
  355.         function shared_metatable:__newindex()
  356.             error("Cannot modify this table", 2)
  357.         end
  358.        
  359.         setmetatable(shared, shared_metatable)
  360.     end
  361.    
  362.     for communicator in External.forEachInstance(Core.CommunicationFolder) do
  363.         local communicator_directory = nil
  364.         local communicator_name = communicator.Name
  365.        
  366.         if communicator:IsA("BindableEvent") then
  367.             communicator_directory = Core.Bindable.Events
  368.         elseif communicator:IsA("BindableFunction") then
  369.             communicator_directory = Core.Bindable.Functions
  370.         else
  371.             warn(("'%s' is not communicator, consider removing it from 'Communication' folder"):format(communicator_name))
  372.             continue
  373.         end
  374.        
  375.         communicator_directory[communicator_name] = communicator
  376.     end
  377.    
  378.     local modules do
  379.         modules = {}
  380.        
  381.         --[[
  382.             Copies given table's content into the modules table
  383.         --]]
  384.         local function add(_table)
  385.             External.assertType(_table, "table", 1)
  386.            
  387.             for index, module in ipairs(_table) do
  388.                 local module_name = module.Name
  389.                
  390.                 for _, existing_module in ipairs(modules) do
  391.                     if existing_module.Name == module_name then
  392.                         warn(("'%s' has duplicate module, consider renaming it or removing from 'Modules' folder"):format(module_name))
  393.                         continue
  394.                     end
  395.                 end
  396.                
  397.                 table.insert(modules, module)
  398.             end
  399.         end
  400.        
  401.         add(Core.ModulesFolder:GetChildren())
  402.         add(modules_folder:GetChildren())
  403.     end
  404.    
  405.     for _, module in ipairs(modules) do
  406.         local module_name = module.Name
  407.        
  408.         if not module:IsA("ModuleScript") then
  409.             warn(("'%s' is not a module script, consider removing it from 'Modules' folder"):format(module_name))
  410.             continue
  411.         end
  412.        
  413.         local initialized_module = require(module)
  414.        
  415.         local module_directory do
  416.             if module.Parent == modules_folder then
  417.                 module_directory = Shared.Modules
  418.             else
  419.                 module_directory = Core.Modules
  420.             end
  421.         end
  422.        
  423.         module_directory[module_name] = initialized_module
  424.        
  425.         if type(initialized_module) ~= "table" then
  426.             continue
  427.         end
  428.        
  429.         call_module_function(initialized_module, "__init__")
  430.        
  431.         if is_shared(initialized_module) then
  432.             local core_init_function_name = server_and_or("__initserver__", "__initclient__")
  433.             call_module_function(initialized_module, core_init_function_name)
  434.         end
  435.     end
  436.    
  437.     for name, module in next, get_all_modules() do
  438.         if type(module) ~= "table" then
  439.             continue
  440.         end
  441.        
  442.         call_module_function(module, "__postinit__")
  443.        
  444.         if is_shared(module) then
  445.             local core_postinit_function_name = server_and_or("__postinitserver__", "__postinitclient__")
  446.             call_module_function(module, core_postinit_function_name)
  447.         end
  448.     end
  449. end
  450.  
  451. function Internal.start()
  452.     local sorted_modules = {}
  453.     local all_modules = get_all_modules()
  454.    
  455.     for name in next, all_modules do
  456.         table.insert(sorted_modules, name)
  457.     end
  458.    
  459.     table.sort(sorted_modules, function(first_name, second_name)
  460.         return (all_modules[first_name].__order__ or DEFAULT_START_ORDER)
  461.             < (all_modules[second_name].__order__ or DEFAULT_START_ORDER)
  462.     end)
  463.    
  464.     for _, module_name in ipairs(sorted_modules) do
  465.         local module = all_modules[module_name]
  466.         call_module_function(module, "__start__")
  467.        
  468.         if is_shared(module) then
  469.             local core_start_function_name = server_and_or("__startserver__", "__startclient__")
  470.             call_module_function(module, core_start_function_name)
  471.         end
  472.     end
  473.    
  474.     for _, module in next, all_modules do
  475.         call_module_function(module, "__after__")
  476.        
  477.         if is_shared(module) then
  478.             local core_after_function_name = server_and_or("__afterserver__", "__afterclient__")
  479.             call_module_function(module, core_after_function_name)
  480.         end
  481.     end
  482. end
  483.  
  484. function Internal.clear()
  485.     call_module_function = nil
  486.     get_all_modules = nil
  487.     server_and_or = nil
  488.     Internal = nil
  489.    
  490.     for remote_name, remote in next, Shared.Network.Events do
  491.         local is_internal = string.match(remote_name, "^@%S+$") ~= nil
  492.        
  493.         if is_internal then
  494.             remote:Destroy()
  495.         end
  496.     end
  497. end
  498.  
  499. function Internal.finishInitializationPeriod()
  500.     function Shared.Functions.registerEvent()
  501.         error("Cannot register event after initialization period", 2)
  502.     end
  503.    
  504.     function Shared.Functions.registerFunction()
  505.         error("Cannot register function after initialization period", 2)
  506.     end
  507.    
  508.     if is_server then
  509.         function Shared.Functions.registerRemoteEvent()
  510.             error("Cannot register remote event after initialization period", 2)
  511.         end
  512.        
  513.         function Shared.Functions.registerRemoteFunction()
  514.             error("Cannot register remote function after initialization period", 2)
  515.         end
  516.     end
  517. end
  518.  
  519. function Internal.lockedFunction()
  520.     error(FUNCTION_LOCKED_MESSAGE, 2)
  521. end
  522.  
  523. function Internal.assertFolder(folder_name, folder_parent)
  524.     External.assertType(folder_name, "string", 1)
  525.     External.assertType(folder_parent, "Instance", 2)
  526.    
  527.     local folder = folder_parent:FindFirstChild(folder_name)
  528.    
  529.     if folder then
  530.         return folder
  531.     else
  532.         error(("'%s' folder has not been found in '%s'"):format(folder_name, folder_parent.Name), 2)
  533.     end
  534. end
  535.  
  536. function Internal.setInstanceParent(_instance, parent)
  537.     External.assertType(_instance, "Instance", 1)
  538.     External.assertType(parent, "Instance", 2)
  539.  
  540.     if _instance.Parent ~= parent then
  541.         _instance.Parent = parent
  542.     end
  543. end
  544.  
  545. function Internal.setInstanceName(_instance, name)
  546.     External.assertType(_instance, "Instance", 1)
  547.     External.assertType(name, "string", 2)
  548.  
  549.     if _instance.Name ~= name then
  550.         _instance.Name = name
  551.     end
  552. end
  553.  
  554. function Internal.lockInternal()
  555.     External.getInternal = Internal.lockedFunction
  556. end
  557.  
  558. function Shared.Functions.registerEvent(event_name, event_instance)
  559.     External.assertType(event_name, "string", 1)
  560.    
  561.     if Shared.Functions.getEvent(event_name) then
  562.         error(("'%s' event is already registered"):format(event_name), 2)
  563.     end
  564.    
  565.     local event do
  566.         if event_instance then
  567.             External.assertType(event_instance, "BindableEvent", 2)
  568.            
  569.             Internal.setInstanceName(event_instance, event_name)
  570.             event = event_instance
  571.         else
  572.             event = Instance.new("BindableEvent")
  573.             event.Name = event_name
  574.         end
  575.     end
  576.    
  577.     Internal.setInstanceParent(event, Core.CommunicationFolder)
  578.     Core.Bindable.Events[event_name] = event
  579. end
  580.  
  581. function Shared.Functions.registerFunction(function_name, function_instance)
  582.     External.assertType(function_name, "string", 1)
  583.    
  584.     if Shared.Functions.getFunction(function_name) then
  585.         error(("'%s' function is already registered"):format(function_name), 2)
  586.     end
  587.    
  588.     local _function do
  589.         if function_instance then
  590.             External.assertType(function_instance, "BindableFunction", 2)
  591.            
  592.             Internal.setInstanceName(function_instance, function_name)
  593.             _function = function_instance
  594.         else
  595.             _function = Instance.new("BindableFunction")
  596.             _function.Name = function_name
  597.         end
  598.     end
  599.    
  600.     Internal.setInstanceParent(_function, Core.CommunicationFolder)
  601.     Core.Bindable.Functions[function_name] = _function
  602. end
  603.  
  604. function Shared.Functions.fireEvent(event_name, ...)
  605.     External.assertType(event_name, "string", 1)
  606.    
  607.     local event = Shared.Functions.getEvent(event_name)
  608.    
  609.     if event then
  610.         event:Fire(...)
  611.     else
  612.         error(("'%s' is not an existing event"):format(event_name), 2)
  613.     end
  614. end
  615.  
  616. function Shared.Functions.awaitEvent(event_name)
  617.     External.assertType(event_name, "string", 1)
  618.    
  619.     local event = Shared.Functions.getEvent(event_name)
  620.    
  621.     if event then
  622.         return event.Event:Wait()
  623.     else
  624.         error(("'%s' is not an existing event"):format(event_name), 2)
  625.     end
  626. end
  627.  
  628. function Shared.Functions.invokeFunction(function_name, ...)
  629.     External.assertType(function_name, "string", 1)
  630.    
  631.     local _function = Shared.Functions.getFunction(function_name)
  632.    
  633.     if _function then
  634.         return _function:Invoke(...)
  635.     else
  636.         error(("'%s' is not an existing function"):format(function_name), 2)
  637.     end
  638. end
  639.  
  640. function Shared.Functions.getFunction(function_name)
  641.     External.assertType(function_name, "string", 1)
  642.    
  643.     return Core.Bindable.Functions[function_name]
  644. end
  645.  
  646. function Shared.Functions.getEvent(event_name)
  647.     External.assertType(event_name, "string", 1)
  648.    
  649.     return Core.Bindable.Events[event_name]
  650. end
  651.  
  652. function Shared.Functions.deleteEvent(event_name)
  653.     External.assertType(event_name, "string", 1)
  654.    
  655.     local event = Shared.Functions.getEvent(event_name)
  656.    
  657.     if event then
  658.         event:Destroy()
  659.     else
  660.         error(("'%s' is not an existing event"):format(event_name), 2)
  661.     end
  662. end
  663.  
  664. function Shared.Functions.deleteFunction(function_name)
  665.     External.assertType(function_name, "string", 1)
  666.    
  667.     local _function = Shared.Functions.getFunction(function_name)
  668.    
  669.     if _function then
  670.         _function:Destroy()
  671.     else
  672.         error(("'%s' is not an existing function"):format(function_name), 2)
  673.     end
  674. end
  675.  
  676. function Shared.Functions.getRemoteEvent(event_name)
  677.     External.assertType(event_name, "string", 1)
  678.  
  679.     return Shared.Network.Events[event_name]
  680. end
  681.  
  682. function Shared.Functions.getRemoteFunction(function_name)
  683.     External.assertType(function_name, "string", 1)
  684.  
  685.     return Shared.Network.Functions[function_name]
  686. end
  687.  
  688. function Shared.Functions.onEvent(event_name, callback)
  689.     External.assertType(event_name, "string", 1)
  690.     External.assertType(callback, "function", 2)
  691.    
  692.     local event = Shared.Functions.getEvent(event_name)
  693.    
  694.     if event then
  695.         return event.Event:Connect(callback)
  696.     else
  697.         error(("'%s' is not an existing event"):format(event_name), 2)
  698.     end
  699. end
  700.  
  701. function Shared.Functions.onInvoke(function_name, callback)
  702.     External.assertType(function_name, "string", 1)
  703.     External.assertType(callback, "function", 2)
  704.    
  705.     local _function = Shared.Functions.getFunction(function_name)
  706.    
  707.     if _function then
  708.         _function.OnInvoke = callback
  709.     else
  710.         error(("'%s' is not an existing function"):format(function_name), 2)
  711.     end
  712. end
  713.  
  714. function Shared.Functions.require(module_name)
  715.     External.assertType(module_name, "string", 1)
  716.    
  717.     return Core.Modules[module_name] or Shared.Modules[module_name]
  718. end
  719.  
  720. function Shared.Functions.requireShared(module_name)
  721.     External.assertType(module_name, "string", 1)
  722.    
  723.     return Shared.Modules[module_name]
  724. end
  725.  
  726. return External
Add Comment
Please, Sign In to add comment