Advertisement
remi_

Bases

Jun 23rd, 2021
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.21 KB | None | 0 0
  1. -- Class management :
  2. local bases = {}
  3.  
  4. bases.Functions = require(6976698809)
  5. bases.Signal = require(6976644438)
  6. bases.classes = {}
  7.  
  8. local functions = bases.Functions
  9. local instances_proxy = functions.userdata.read_only(bases)
  10.  
  11. local table = functions.table
  12.  
  13. -- Settings :
  14. local debug_enabled = true -- Print the recognized classes after loading them.
  15. local silence_module = "__" -- Keyword to silence modules.
  16.  
  17. local TYPE_ERR = "Expected type \"%s\" when assigning  property \"%s\", got \"%s\""
  18. local NIL = "#@#@#@" -- Keyword to represent nil (You can't set a value to nil in a table and keep the reference).
  19.  
  20. local attribute_fields = {"read", "public", "private"}
  21. local type_properties = {
  22.     instance_name = "string",
  23.     base_object = "Instance",
  24.     constructor = "function"
  25. }
  26.  
  27. local function base_constructor(object, ...)
  28.     print(string.format("%s of class %s has been created!", tostring(object), object.ClassName))
  29.     print(string.format("Additional arguments: %s", ... and table.concat({...}, ", ") or "null"))
  30. end
  31.  
  32. -- Creation of attributes for objects.
  33. local create_attributes = function(class_attributes, instance, class_name)
  34.     local object_attributes = {}
  35.  
  36.     object_attributes.key = instance
  37.     object_attributes.signals = {}
  38.  
  39.     for section, attributes in pairs(class_attributes) do
  40.         local object_section = {}
  41.  
  42.         for attribute_name, info in pairs(attributes) do
  43.             object_section[attribute_name] = info[1]
  44.  
  45.             if section == "private" then continue end
  46.             object_attributes.signals[attribute_name] = bases.Signal(attribute_name)
  47.         end
  48.        
  49.         if section == "read" then object_section.ClassName = class_name end
  50.         if section == "private" then continue end
  51.        
  52.         local section_proxy = newproxy(true)
  53.         local section_meta = getmetatable(section_proxy)
  54.  
  55.         section_meta.__index = function(_, key)
  56.             if key == "pairs" then
  57.                 return object_section
  58.             else
  59.                 return object_section[key]
  60.             end
  61.         end
  62.  
  63.         section_meta.__newindex = function(_, key, value)
  64.             if typeof(value) == attributes[key][2] or type(value) == "nil" then
  65.                 object_section[key] = value
  66.                 object_attributes.signals[key]:Fire()
  67.             else
  68.                 error(string.format(TYPE_ERR, attributes[key][2], key, typeof(value)), 3)
  69.             end
  70.         end
  71.        
  72.         object_attributes[section] = section_proxy
  73.     end
  74.    
  75.     return object_attributes
  76. end
  77.  
  78. -- Create proxy for classes :
  79. local function class_proxy(class)
  80.     local public = class.public
  81.     local read = class.read
  82.     local private = class.private
  83.  
  84.     local class_proxy = newproxy(true)
  85.     local class_meta = getmetatable(class_proxy)
  86.  
  87.     -- Conventional metamethods.
  88.     class_meta.__newindex = function(class_proxy, key, value)
  89.         if public[key] then
  90.             local property_type = type_properties[key]
  91.             local value_type = typeof(value)
  92.  
  93.             assert(value_type == property_type, string.format(TYPE_ERR, property_type, key, value_type))
  94.             public[key] = value
  95.         elseif read[key] then
  96.             error("Can't modify this property.", 2)
  97.         else
  98.             error("Unable to modify class.", 2)
  99.         end
  100.     end
  101.  
  102.     class_meta.__tostring = function(class_proxy)
  103.         return class.name
  104.     end
  105.  
  106.     class_meta.__index = function(class_proxy, index)
  107.         return public[index] or read[index] or class_meta[index]
  108.     end
  109.  
  110.     return class_proxy
  111. end
  112.  
  113. -- Setup function for classes :
  114. local function setup_class(module)
  115.     local class = {
  116.         public = {},
  117.         read = {},
  118.         private = {}
  119.     }
  120.    
  121.     local read = class.read
  122.     local public = class.public
  123.     local private = class.private
  124.    
  125.     private.objects = {}
  126.     private.attributes = {
  127.         public = {},
  128.         read = {},
  129.         private = {}
  130.     }
  131.  
  132.     read.methods = {}
  133.     read.name = module.Name
  134.    
  135.     public.instance_name = module.Name
  136.     public.base_object = Instance.new("Part")
  137.  
  138.     public.constructor = base_constructor
  139.    
  140.     -- GetPropertyChangedSignal compatibility.
  141.     function read.methods:GetPropertyChangedSignal(attribute_name)
  142.         local attributes = private.objects[self]
  143.  
  144.         local signals = attributes.signals
  145.         local key = attributes.key
  146.  
  147.         local signal = signals[attribute_name]
  148.         if signal then
  149.             return signal
  150.         else
  151.             local success, signal = pcall(key.GetPropertyChangedSignal, key, attribute_name)
  152.             if not success then
  153.                 error(string.format("%s is not a valid property name.", attribute_name), 2)
  154.             else
  155.                 return signal
  156.             end
  157.         end
  158.     end
  159.    
  160.     -- Removing the object from references.
  161.     function read.methods:Destroy()
  162.         local attributes = private.objects[self]
  163.  
  164.         local key = attributes.key
  165.  
  166.         local signals = attributes.signals
  167.         for signal_name, signal in pairs(signals) do
  168.             signal:Destroy()
  169.         end
  170.  
  171.         private.objects[self] = nil
  172.         local meta = getmetatable(self)
  173.         for i, v in pairs(meta) do
  174.             meta[i] = nil
  175.         end
  176.  
  177.         key:Destroy()
  178.     end
  179.    
  180.     -- Creating the class' proxy.
  181.     local class_proxy = class_proxy(class)
  182.     local class_meta = getmetatable(class_proxy)
  183.    
  184.     -- Class functions :   
  185.     do
  186.         -- Adding new object to the class.
  187.         function class_meta.create(object)
  188.             local class = class_proxy
  189.             local instance = class.base_object:Clone()
  190.             instance.Name = class.instance_name
  191.             instance:SetAttribute("ClassName", class.name)
  192.  
  193.             local attributes = create_attributes(private.attributes, instance, class.name)
  194.  
  195.             private.objects[object] = attributes
  196.             return attributes
  197.         end
  198.  
  199.         -- Function for getting attributes in any attribute section (read, private, public)
  200.         function class_meta.get(object_get, attribute_get)
  201.             local class = class_proxy
  202.             for section, attributes in pairs(private.objects[object_get]) do
  203.                 if section == "key" or section == "signals" then continue end
  204.            
  205.                 local value = attributes[attribute_get]
  206.                
  207.                 if value then
  208.                     return value ~= NIL and value or nil
  209.                 end
  210.             end
  211.  
  212.             error(string.format("'%s' its not a valid attribute of class %s", attribute_get, class.name))
  213.             return
  214.         end
  215.  
  216.         -- Function for modifying attributes in any attribute section (read, private, public)
  217.         function class_meta.set(object_modify, attribute_modify, value_modify)
  218.             local class = class_proxy
  219.             local sections = private.objects[object_modify]
  220.             for section, attributes in pairs(sections) do
  221.                 if section == "key" or section == "signals" then continue end
  222.  
  223.                 section = sections[section]
  224.                 local value = section[attribute_modify]
  225.                 if value == value_modify then return end
  226.  
  227.                 if value then
  228.                     section[attribute_modify] = value_modify == nil and NIL or value_modify
  229.                     return
  230.                 end
  231.             end
  232.  
  233.             error(string.format("'%s' its not a valid attribute of class %s", attribute_modify, class.name))
  234.         end
  235.  
  236.         -- Adding attributes to your class.
  237.         function class_meta.add_attribute(section, name, default, attribute_type)
  238.             if not table.find(attribute_fields, section) then error("Expected a valid attribute section. (public / read / private)", 2) end
  239.             if default then attribute_type = typeof(default) end
  240.             private.attributes[section][name] = {default or NIL, attribute_type}
  241.         end
  242.  
  243.         -- Check if its a valid attribute.
  244.         function class_meta.is_attribute(object, attribute_name, user)
  245.             for section, attributes in pairs(private.objects[object]) do
  246.                 if section == "key" or section == "signals" then continue end
  247.                 if user and section == "private" then continue end
  248.                 if attributes[attribute_name] then return true end
  249.             end
  250.  
  251.             return false
  252.         end
  253.  
  254.         -- Get object from the object's key (the key its the instance that identifies the object)
  255.         function class_meta.getObjectFromKey(key)
  256.             for object, attributes in pairs(private.objects) do
  257.                 if attributes.key == key then
  258.                     return object
  259.                 end
  260.             end
  261.             return
  262.         end
  263.  
  264.         -- Get the key instance from the object.
  265.         function class_meta.getKeyFromObject(object_goal)
  266.             for object, sections in pairs(private.objects) do
  267.                 if object == object_goal then
  268.                     return sections.key
  269.                 end
  270.             end
  271.  
  272.             return
  273.         end
  274.     end
  275.    
  276.     return class_proxy
  277. end
  278.  
  279. --Object management functions.
  280. local getKeyInstance; getKeyInstance = function(object)
  281.     if type(object) == "table" then
  282.         local unwrapped = {}
  283.  
  284.         for key, value in next, object do
  285.             unwrapped[key] = getKeyInstance(value)
  286.         end
  287.  
  288.         return unwrapped
  289.     elseif type(object) == "userdata" and bases.classes[object.ClassName] then
  290.         local class = bases.classes[object.ClassName][2]
  291.  
  292.         local keyInstance = class.getKeyFromObject(object)
  293.         if keyInstance then
  294.             return keyInstance
  295.         end
  296.  
  297.         return object
  298.     else
  299.         return object
  300.     end
  301. end
  302.  
  303. local wrapFunction; wrapFunction = function(normalFunction)
  304.     if type(normalFunction) == "function" then
  305.         local wrappedFunction = function(...)
  306.             local arguments = getKeyInstance{...}
  307.             return normalFunction(unpack(arguments))
  308.         end
  309.         return wrappedFunction
  310.     else
  311.         return normalFunction
  312.     end
  313. end
  314.  
  315. local createObject = function(class, ...)
  316.     local object = newproxy(true)
  317.     local meta = getmetatable(object)
  318.  
  319.     local attributes = class.create(object)
  320.  
  321.     local private = attributes.private
  322.     local public = attributes.public
  323.     local read = attributes.read
  324.  
  325.     meta.__index = function(object, index)
  326.         if class.is_attribute(object, index, true) then return class.get(object, index) end
  327.         return class.methods[index] or wrapFunction(attributes.key[index])
  328.     end
  329.  
  330.     meta.__newindex = function(object, key, value)
  331.         if public[key] then
  332.             public[key] = value
  333.         elseif read[key] then
  334.             error("can't set value", 2)
  335.         else
  336.             attributes.key[key] = value
  337.         end
  338.     end
  339.  
  340.     meta.__tostring = function(object)
  341.         return tostring(attributes.key)
  342.     end
  343.    
  344.     class.constructor(object, ...)
  345.     return object
  346. end
  347.  
  348. local function load_classes()
  349.     if #bases.classes > 1 then return end
  350.     local class_debug = "Classes Recognized:"
  351.     for _, classModule in pairs(script:GetChildren()) do
  352.         local is_silenced = string.sub(classModule.Name, 1, 2) == silence_module
  353.  
  354.         if (not classModule:IsA("ModuleScript")) or is_silenced then continue end
  355.  
  356.         local success, class = pcall(require, classModule)
  357.         if not success then continue end
  358.  
  359.         class_debug ..= class_debug and "\n\t\t\t\t\t\t\t\t"..class.name or ""
  360.         bases.classes[class.name] = {function(...) return createObject(class, ...) end, class}
  361.     end
  362.     if debug_enabled then print(class_debug) else class_debug = nil end
  363. end
  364.  
  365. -- Setting up the class management proxy.
  366. local bases_proxy = newproxy(true)
  367. local bases_meta = getmetatable(bases_proxy)
  368.  
  369. bases_meta.__metatable = "Class manager."
  370.  
  371. bases_meta.__call = function(_, ...)
  372.     local args = {...}
  373.     local intention = args[1]
  374.  
  375.     if intention == "setup" then
  376.         return setup_class(unpack(args, 2))
  377.     elseif intention == "modules" then
  378.         return bases.Functions, bases.Signal
  379.     else
  380.         -- Class modules wrapper :
  381.         load_classes()
  382.  
  383.         return {
  384.             GetConstructor = function(className)
  385.                 local class = bases.classes[className]
  386.                 if class then
  387.                     return class[1]
  388.                 end
  389.                 error(string.format('Unable to find a constructor for class "%s"', className), 2)
  390.             end,
  391.             GetObject = function(instance)
  392.                 local className = instance:GetAttribute("ClassName")
  393.                 local class = bases.classes[className]
  394.                 if class then
  395.                     return class[2].getObjectFromKey(instance)
  396.                 else
  397.                     return instance
  398.                 end
  399.             end,
  400.         }
  401.     end
  402. end
  403.  
  404. bases_meta.__newindex = function()
  405.     error("Can't modify read only userdata.", 2)
  406. end
  407.  
  408. bases_meta.__index = function(_, key)
  409.     if key == "classes" then return end
  410.     return bases[key]
  411. end
  412.  
  413. return bases_proxy
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement