Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- AUTHOR: kirda @imKirda
- DATE: April 7 2021
- DESC:
- Module required by framework server and client script to share functionality
- Module startup phrases:
- 1: __init__
- 2: __initserver__ || __initclient__
- 3: __postinit__
- 4: __postserverinit__ || __postclientinit__
- 5: __start__
- 6: __startserver__ || __startclient__
- 7: __after__
- 8: __afterserver__ || __afterclient__
- Expected startup phrase uses:
- 1, 2 - Initialize the module
- 3, 4 - Require other modules
- 5, 6 - Main module logic after all modules are initialized
- 7, 8 - Finish module logic after all modules have ran
- API:
- EXTERNAL:
- table getInternal()
- - Returns 'Internal' table for internal use by framework scripts
- - Locked by framework script after calling
- NOTE: All contents of 'Internal' table are removed after framework loaded
- Instance forEachInstance(Instance)
- - Loops through given instance and returns it's children by order in loop
- void assertType(any, string, number)
- - Asserts if given type of parameter one value is second parameter
- NOTE: This function should not be used outside of functions
- INTERNAL:
- table getShared()
- - Returns the "Shared" table to allow server to register remotes
- void initialize()
- - Initializes the shared framework module
- Folder assertFolder(string, Instance)
- - Returns folder with given name from second parameter Instance if found
- - If folder has not been found, throws an error
- void connect(table)
- - Connects shared framework with given (Client or Server) framework table
- - Initializes connected core
- void start()
- - Starts all modules
- void clear()
- - Clears framework garbage
- void lockedFunction()
- - Throws error "This function is locked" to lock some internal functions
- - To lock functions, they are overriden with this one
- void lockInternal()
- - Locks internal functions from 'External' table
- void finishInitializationPeriod()
- - Locks all functions that are inaccessible after module initialization
- void setInstanceName(Instance, string)
- - Sets name of given instance to given string if it's not already named
- like the string to prevent setting property to same value as it is
- void setInstanceParent(Instance, Instance)
- - Sets parent of given instance to second parameter instance if it's not
- if it's not already parented to it to prevent setting same parent twice
- SHARED:
- void registerEvent(string, BindableEvent)
- - Registers bindable event with given name if none are registered
- - If second parameter is not given, creates new event instance
- void registerFunction(string, BindableFunction)
- - Registers bindable function with given name if none are registered
- - If second parameter is not given, creates new function instance
- void deleteEvent(string)
- - Destroys event with given name if found
- void deleteFunction(string)
- - Destroy function with given name if found
- void fireEvent(string, any...)
- - Fires event with given name if found, fires tuple as parameters
- any... awaitEvent(string)
- - Yields until event with given name fires
- - Returns what the event yielded
- any... invokeFunction(string, any...)
- - Invokes function with given name if found and returns what it invoked
- - Passes given tuple as invoke parameters
- BindableEvent? getEvent(string)
- - Returns event with given name
- BindableFunction? getFunction(string)
- - Returns function with given name
- RemoteEvent? getRemoteEvent(string)
- - Returns remote event with given name
- RemoteFunction? getRemoteFunction(string)
- - Returns remote function with given name
- RBXScriptConnection onEvent(string, function)
- - Connects event with given name to given function if found
- void onInvoke(string, function)
- - Sets function with given name's invoke callback to given function
- any require(string)
- - Returns module with given name
- any requireShared(string)
- - Returns shared module with given name
- --]]
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local RunService = game:GetService("RunService")
- local FUNCTION_LOCKED_MESSAGE = "This function is locked"
- local DEFAULT_START_ORDER = 9
- local network_folder = ReplicatedStorage.Network
- local modules_folder = ReplicatedStorage.Modules
- local External = {}
- local Internal = {}
- local Core
- local Shared = {
- Functions = {},
- Network = {
- Functions = {},
- Events = {},
- },
- Modules = {},
- NetworkFolder = network_folder,
- }
- local is_server = RunService:IsServer()
- --[[
- if is_server, returns the first value, else returns the second value
- --]]
- local function server_and_or(and_value, or_value)
- if is_server then
- return and_value
- else
- return or_value
- end
- end
- --[[
- Returns all core and shared modules combined
- --]]
- local function get_all_modules()
- local modules = {}
- local function add(_table)
- for name, module in next, _table do
- modules[name] = module
- end
- end
- add(Core.Modules)
- add(Shared.Modules)
- return modules
- end
- --[[
- Calls function with given name from given module if found
- --]]
- local function call_module_function(module, function_name)
- External.assertType(module, "table", 1)
- External.assertType(function_name, "string", 2)
- local _function = rawget(module, function_name)
- if _function then
- _function()
- end
- end
- --[[
- Returns if module is shared
- --]]
- local function is_shared(module)
- for _, shared_module in next, Shared.Modules do
- if module == shared_module then
- return true
- end
- end
- return false
- end
- function External.getInternal()
- return Internal
- end
- function External.forEachInstance(instance)
- External.assertType(instance, "Instance", 1)
- local children = instance:GetChildren()
- local index = 0
- return function()
- index += 1
- return children[index]
- end
- end
- function External.assertType(given_value, expected_type, argument_index)
- do
- local argument2_type = typeof(expected_type)
- local argument3_type = typeof(argument_index)
- if argument2_type ~= "string" then
- error(("invalid argument #2 to 'assertType' (string expected, got %s)"):format(argument2_type), 2)
- end
- if argument3_type ~= "number" then
- error(("invalid argument #3 to 'assertType' (number expected, got %s)"):format(argument3_type), 2)
- end
- if argument_index <= 0 then
- error("argument #3 to 'assertType' must be higher than 0")
- end
- if math.floor(argument_index) ~= argument_index then
- error("argument #3 to 'assertType' must be whole number")
- end
- end
- local given_value_type = typeof(given_value)
- local is_invalid = given_value_type ~= expected_type
- local is_roblox_type = false
- local is_enum_type = false
- if is_invalid then
- if given_value_type == "Instance" then
- is_roblox_type = true
- is_invalid = not given_value:IsA(expected_type)
- elseif given_value_type == "EnumItem" then
- is_enum_type = true
- is_invalid = tostring(given_value.EnumType) ~= expected_type
- end
- end
- if is_invalid then
- if is_roblox_type then
- given_value_type = given_value.ClassName
- elseif is_enum_type then
- given_value_type = tostring(given_value.EnumType)
- end
- local calling_function_name = debug.info(2, "n")
- error(
- string.format(
- "invalid argument #%d to '%s' (%s expected, got %s)",
- argument_index,
- calling_function_name,
- expected_type,
- given_value_type
- ),
- 3
- )
- end
- end
- function Internal.getShared()
- return Shared
- end
- function Internal.initialize()
- --[[
- Registers or creates new internal remote event with given name
- --]]
- local function create_internal_remote_event(remote_name)
- External.assertType(remote_name, "string", 1)
- local existing_event = network_folder:FindFirstChild(remote_name)
- if existing_event then
- Shared.Network.Events[remote_name] = existing_event
- else
- local event = Instance.new("RemoteEvent")
- event.Name = remote_name
- event.Parent = network_folder
- Shared.Network.Events[remote_name] = event
- end
- end
- for remote in External.forEachInstance(network_folder) do
- local remote_directory = nil
- local remote_name = remote.Name
- if remote:IsA("RemoteEvent") then
- remote_directory = Shared.Network.Events
- elseif remote:IsA("RemoteFunction") then
- remote_directory = Shared.Network.Functions
- else
- warn(("'%s' is not remote, consider removing it from 'Network' folder"):format(remote_name))
- continue
- end
- if remote_directory[remote_name] then
- warn(("'%s' remote event has duplicate, consider renaming it or removing from 'Network' folder"):format(remote_name))
- continue
- end
- remote_directory[remote_name] = remote
- end
- create_internal_remote_event("@register_remote")
- end
- function Internal.connect(core)
- Core = core
- core = nil
- do
- Shared.Functions.forEachInstance = External.forEachInstance
- Shared.Functions.assertType = External.assertType
- for name, _function in next, Core.Functions do
- Shared.Functions[name] = _function
- end
- Core.Functions = nil
- local shared_metatable = {
- __index = Shared.Functions,
- }
- function shared_metatable:__newindex()
- error("Cannot modify this table", 2)
- end
- setmetatable(shared, shared_metatable)
- end
- for communicator in External.forEachInstance(Core.CommunicationFolder) do
- local communicator_directory = nil
- local communicator_name = communicator.Name
- if communicator:IsA("BindableEvent") then
- communicator_directory = Core.Bindable.Events
- elseif communicator:IsA("BindableFunction") then
- communicator_directory = Core.Bindable.Functions
- else
- warn(("'%s' is not communicator, consider removing it from 'Communication' folder"):format(communicator_name))
- continue
- end
- communicator_directory[communicator_name] = communicator
- end
- local modules do
- modules = {}
- --[[
- Copies given table's content into the modules table
- --]]
- local function add(_table)
- External.assertType(_table, "table", 1)
- for index, module in ipairs(_table) do
- local module_name = module.Name
- for _, existing_module in ipairs(modules) do
- if existing_module.Name == module_name then
- warn(("'%s' has duplicate module, consider renaming it or removing from 'Modules' folder"):format(module_name))
- continue
- end
- end
- table.insert(modules, module)
- end
- end
- add(Core.ModulesFolder:GetChildren())
- add(modules_folder:GetChildren())
- end
- for _, module in ipairs(modules) do
- local module_name = module.Name
- if not module:IsA("ModuleScript") then
- warn(("'%s' is not a module script, consider removing it from 'Modules' folder"):format(module_name))
- continue
- end
- local initialized_module = require(module)
- local module_directory do
- if module.Parent == modules_folder then
- module_directory = Shared.Modules
- else
- module_directory = Core.Modules
- end
- end
- module_directory[module_name] = initialized_module
- if type(initialized_module) ~= "table" then
- continue
- end
- call_module_function(initialized_module, "__init__")
- if is_shared(initialized_module) then
- local core_init_function_name = server_and_or("__initserver__", "__initclient__")
- call_module_function(initialized_module, core_init_function_name)
- end
- end
- for name, module in next, get_all_modules() do
- if type(module) ~= "table" then
- continue
- end
- call_module_function(module, "__postinit__")
- if is_shared(module) then
- local core_postinit_function_name = server_and_or("__postinitserver__", "__postinitclient__")
- call_module_function(module, core_postinit_function_name)
- end
- end
- end
- function Internal.start()
- local sorted_modules = {}
- local all_modules = get_all_modules()
- for name in next, all_modules do
- table.insert(sorted_modules, name)
- end
- table.sort(sorted_modules, function(first_name, second_name)
- return (all_modules[first_name].__order__ or DEFAULT_START_ORDER)
- < (all_modules[second_name].__order__ or DEFAULT_START_ORDER)
- end)
- for _, module_name in ipairs(sorted_modules) do
- local module = all_modules[module_name]
- call_module_function(module, "__start__")
- if is_shared(module) then
- local core_start_function_name = server_and_or("__startserver__", "__startclient__")
- call_module_function(module, core_start_function_name)
- end
- end
- for _, module in next, all_modules do
- call_module_function(module, "__after__")
- if is_shared(module) then
- local core_after_function_name = server_and_or("__afterserver__", "__afterclient__")
- call_module_function(module, core_after_function_name)
- end
- end
- end
- function Internal.clear()
- call_module_function = nil
- get_all_modules = nil
- server_and_or = nil
- Internal = nil
- for remote_name, remote in next, Shared.Network.Events do
- local is_internal = string.match(remote_name, "^@%S+$") ~= nil
- if is_internal then
- remote:Destroy()
- end
- end
- end
- function Internal.finishInitializationPeriod()
- function Shared.Functions.registerEvent()
- error("Cannot register event after initialization period", 2)
- end
- function Shared.Functions.registerFunction()
- error("Cannot register function after initialization period", 2)
- end
- if is_server then
- function Shared.Functions.registerRemoteEvent()
- error("Cannot register remote event after initialization period", 2)
- end
- function Shared.Functions.registerRemoteFunction()
- error("Cannot register remote function after initialization period", 2)
- end
- end
- end
- function Internal.lockedFunction()
- error(FUNCTION_LOCKED_MESSAGE, 2)
- end
- function Internal.assertFolder(folder_name, folder_parent)
- External.assertType(folder_name, "string", 1)
- External.assertType(folder_parent, "Instance", 2)
- local folder = folder_parent:FindFirstChild(folder_name)
- if folder then
- return folder
- else
- error(("'%s' folder has not been found in '%s'"):format(folder_name, folder_parent.Name), 2)
- end
- end
- function Internal.setInstanceParent(_instance, parent)
- External.assertType(_instance, "Instance", 1)
- External.assertType(parent, "Instance", 2)
- if _instance.Parent ~= parent then
- _instance.Parent = parent
- end
- end
- function Internal.setInstanceName(_instance, name)
- External.assertType(_instance, "Instance", 1)
- External.assertType(name, "string", 2)
- if _instance.Name ~= name then
- _instance.Name = name
- end
- end
- function Internal.lockInternal()
- External.getInternal = Internal.lockedFunction
- end
- function Shared.Functions.registerEvent(event_name, event_instance)
- External.assertType(event_name, "string", 1)
- if Shared.Functions.getEvent(event_name) then
- error(("'%s' event is already registered"):format(event_name), 2)
- end
- local event do
- if event_instance then
- External.assertType(event_instance, "BindableEvent", 2)
- Internal.setInstanceName(event_instance, event_name)
- event = event_instance
- else
- event = Instance.new("BindableEvent")
- event.Name = event_name
- end
- end
- Internal.setInstanceParent(event, Core.CommunicationFolder)
- Core.Bindable.Events[event_name] = event
- end
- function Shared.Functions.registerFunction(function_name, function_instance)
- External.assertType(function_name, "string", 1)
- if Shared.Functions.getFunction(function_name) then
- error(("'%s' function is already registered"):format(function_name), 2)
- end
- local _function do
- if function_instance then
- External.assertType(function_instance, "BindableFunction", 2)
- Internal.setInstanceName(function_instance, function_name)
- _function = function_instance
- else
- _function = Instance.new("BindableFunction")
- _function.Name = function_name
- end
- end
- Internal.setInstanceParent(_function, Core.CommunicationFolder)
- Core.Bindable.Functions[function_name] = _function
- end
- function Shared.Functions.fireEvent(event_name, ...)
- External.assertType(event_name, "string", 1)
- local event = Shared.Functions.getEvent(event_name)
- if event then
- event:Fire(...)
- else
- error(("'%s' is not an existing event"):format(event_name), 2)
- end
- end
- function Shared.Functions.awaitEvent(event_name)
- External.assertType(event_name, "string", 1)
- local event = Shared.Functions.getEvent(event_name)
- if event then
- return event.Event:Wait()
- else
- error(("'%s' is not an existing event"):format(event_name), 2)
- end
- end
- function Shared.Functions.invokeFunction(function_name, ...)
- External.assertType(function_name, "string", 1)
- local _function = Shared.Functions.getFunction(function_name)
- if _function then
- return _function:Invoke(...)
- else
- error(("'%s' is not an existing function"):format(function_name), 2)
- end
- end
- function Shared.Functions.getFunction(function_name)
- External.assertType(function_name, "string", 1)
- return Core.Bindable.Functions[function_name]
- end
- function Shared.Functions.getEvent(event_name)
- External.assertType(event_name, "string", 1)
- return Core.Bindable.Events[event_name]
- end
- function Shared.Functions.deleteEvent(event_name)
- External.assertType(event_name, "string", 1)
- local event = Shared.Functions.getEvent(event_name)
- if event then
- event:Destroy()
- else
- error(("'%s' is not an existing event"):format(event_name), 2)
- end
- end
- function Shared.Functions.deleteFunction(function_name)
- External.assertType(function_name, "string", 1)
- local _function = Shared.Functions.getFunction(function_name)
- if _function then
- _function:Destroy()
- else
- error(("'%s' is not an existing function"):format(function_name), 2)
- end
- end
- function Shared.Functions.getRemoteEvent(event_name)
- External.assertType(event_name, "string", 1)
- return Shared.Network.Events[event_name]
- end
- function Shared.Functions.getRemoteFunction(function_name)
- External.assertType(function_name, "string", 1)
- return Shared.Network.Functions[function_name]
- end
- function Shared.Functions.onEvent(event_name, callback)
- External.assertType(event_name, "string", 1)
- External.assertType(callback, "function", 2)
- local event = Shared.Functions.getEvent(event_name)
- if event then
- return event.Event:Connect(callback)
- else
- error(("'%s' is not an existing event"):format(event_name), 2)
- end
- end
- function Shared.Functions.onInvoke(function_name, callback)
- External.assertType(function_name, "string", 1)
- External.assertType(callback, "function", 2)
- local _function = Shared.Functions.getFunction(function_name)
- if _function then
- _function.OnInvoke = callback
- else
- error(("'%s' is not an existing function"):format(function_name), 2)
- end
- end
- function Shared.Functions.require(module_name)
- External.assertType(module_name, "string", 1)
- return Core.Modules[module_name] or Shared.Modules[module_name]
- end
- function Shared.Functions.requireShared(module_name)
- External.assertType(module_name, "string", 1)
- return Shared.Modules[module_name]
- end
- return External
Add Comment
Please, Sign In to add comment