I am working on a module that reads the properties of classes and instances, as well as other features.
I am wondering how to go about publishing, optimizing (if needed), and adding more features to the module.
Note: it composes of multiple module scripts:
> ClassInfo (ModuleScript)
> ClassesModule (ModuleScript)
> TweenModule (ModuleScript)
Here are the scripts:
ClassInfo
local tweenService = game:GetService("TweenService")
local repr = require(game.ServerStorage.Repr)
local ClassInfo = {}
ClassInfo.ClassModule = require(script.ClassesModule)
ClassInfo.TweenModule = require(script.TweenModule)
ClassInfo.tweenDifference = function(editingInstance : Instance, goalInstance : Instance, tweenInfo : TweenInfo, exeptionProperties : {string})
local className = editingInstance.ClassName
goalInstance = if typeof(goalInstance) == "Instance" then goalInstance else Instance.new(className)
tweenInfo = if typeof(tweenInfo) == "TweenInfo" then tweenInfo else TweenInfo.new()
exeptionProperties = if typeof(exeptionProperties) == "table" then exeptionProperties else {}
local className2 = goalInstance.ClassName
if className == className2 then
local info = ClassInfo.ClassModule.getPropertiesDifferences(editingInstance, goalInstance, {Show_ReadOnly = false, Show_Hidden = false, Show_Deprecated = false, Show_Functions = false, Show_Events = false})
if info then
table.foreach(info, function(k, v)
if (v.category == "Data" or v.category == "Surface") or not ClassInfo.TweenModule.canTweenValue(workspace.CoolPart[k]) or table.find(exeptionProperties, k) then
info[k] = nil
end
end)
local toTween = {}
table.foreach(info, function(k, v)
toTween[k] = workspace.Baseplate[k]
end)
local tween = tweenService:Create(editingInstance, tweenInfo, toTween)
return tween
end
end
end
return ClassInfo
ClassesModule
local repr = require(game.ServerStorage.Repr)
local httpService = game:GetService("HttpService")
local propertiesVersion = httpService:GetAsync("http://setup.roproxy.com/versionQTStudio")
local jsonProperties = httpService:GetAsync("http://setup.roproxy.com/".. propertiesVersion .."-API-Dump.json")
local classesInfo = httpService:JSONDecode(jsonProperties)
local defaultDetailInfo = {
["Show_NotReplicated"] = true,
["Show_Deprecated"] = true, -- false
["Show_ReadOnly"] = true,
["Show_Hidden"] = true, -- false
["Show_Functions"] = true,
["Show_Events"] = true,
}
local allClasses : {string: string} = {}
local loadedAllClasses = false
local classModule = {}
classModule.API_Version = tonumber(propertiesVersion) or propertiesVersion
function classModule.getAllClassNames()
if not loadedAllClasses then
table.foreach(classesInfo.Classes, function(_, v)
if v.Name and typeof(v.Name) == "string" then
local className = v.Name
allClasses[className] = className
end
end)
loadedAllClasses = true
end
return allClasses
end
function classModule.ChangeDefaultDetails(detailName : string, value : boolean)
if typeof(detailName) == "string" then
if defaultDetailInfo[detailName] then
if typeof(value) == "boolean" then
defaultDetailInfo[detailName] = value
else
warn(value, " is not a boolean!")
end
else
warn(detailName .. " is not a valid value!")
end
else
warn(detailName, " is not a string!")
end
end
function classModule.getInfo(
className : string,
details : {
["Show_NotReplicated"] : boolean,
["Show_Deprecated"] : boolean,
["Show_ReadOnly"] : boolean,
["Show_Hidden"] : boolean,
["Show_Functions"] : boolean,
["Show_Events"] : boolean
}
)
local didFind = false
local members = {} -- properties
local checkedClasses = {}
local classLoops = {{name = className, isInherited = false}} -- classes
local currIndex = 1
for i, passedClass in ipairs(classLoops) do
-- passedClass = .name (class name), .isInherited (is it inherited)
local passedClassName = passedClass.name
local isInherited = passedClass.isInherited
table.foreach(classesInfo.Classes, function(_, v)
-- v = classInfo
if v.Members and v.Name and v.Name == passedClassName then
didFind = true
local Superclass : string = v.Superclass
local ClassName : string = v.Name
local MemoryCategory : string = v.MemoryCategory
if not table.find(checkedClasses, passedClassName) then
table.insert(classLoops, {name = Superclass, isInherited = true})
end
table.insert(checkedClasses, passedClassName)
local NotReplicated = if typeof(details.Show_NotReplicated) == "boolean" then details.Show_NotReplicated else defaultDetailInfo.Show_NotReplicated
local Deprecated = if typeof(details.Show_Deprecated) == "boolean" then details.Show_Deprecated else defaultDetailInfo.Show_Deprecated
local ReadOnly = if typeof(details.Show_ReadOnly) == "boolean" then details.Show_ReadOnly else defaultDetailInfo.Show_ReadOnly
local Hidden = if typeof(details.Show_Hidden) == "boolean" then details.Show_Hidden else defaultDetailInfo.Show_Hidden
local Functions = if typeof(details.Show_Functions) == "boolean" then details.Show_Functions else defaultDetailInfo.Show_Functions
local Events = if typeof(details.Show_Events) == "boolean" then details.Show_Events else defaultDetailInfo.Show_Events
table.foreach(v.Members, function(_, attributeInfo)
-- attributeInfo = property/attribute info
local canShow = true
local this_notReplicated = false
local this_deprecated = false
local this_readOnly = false
local this_hidden = false
local this_function = attributeInfo.MemberType == "Function"
local this_event = attributeInfo.MemberType == "Event"
if attributeInfo.Tags then
this_notReplicated = if table.find(attributeInfo.Tags, "NotReplicated") then true else false
this_deprecated = if table.find(attributeInfo.Tags, "Deprecated") then true else false
this_readOnly = if table.find(attributeInfo.Tags, "ReadOnly") then true else false
this_hidden = if table.find(attributeInfo.Tags, "Hidden") then true else false
end
canShow = canShow and (NotReplicated or not this_notReplicated)
canShow = canShow and (Deprecated or not this_deprecated)
canShow = canShow and (ReadOnly or not this_readOnly)
canShow = canShow and (Hidden or not this_hidden)
canShow = canShow and (Functions or not this_function)
canShow = canShow and (Events or not this_event)
if canShow then
local allValues = {}
-- Basic Info --
local dataCategory : string = attributeInfo.Category
-- the category seen from studio
local memberType : string = attributeInfo.MemberType
-- usually "Property"
local dataName : string = attributeInfo.Name
-- the name of property
-- Formally/JSON "Tags" Functionally/params "Details" --
local isNotReplicated : boolean = this_notReplicated
local isDeprecated : boolean = this_deprecated
local isReadOnly : boolean = this_readOnly
local isHidden : boolean = this_hidden
-- security (TO-DO) --
local canRead = attributeInfo.Security.Read -- no idea
local canWrite = attributeInfo.Security.Write -- no idea
-- "None",
-- "RobloxScriptSecurity",
-- "LocalUserSecurity",
-- "PluginSecurity"
-- serialization --
local canLoad : boolean
local canSave : boolean
if attributeInfo.Serialization then
canLoad = attributeInfo.Serialization.CanLoad
canSave = attributeInfo.Serialization.CanSave
-- what do these mean?
end
-- Thread Safety --
local threadSafetyValue : string = attributeInfo.ThreadSafety
local threadSafety = {} -- see docs for def.
threadSafety.Value = threadSafetyValue
threadSafety.IsUnsafe = threadSafetyValue == "Unsafe"
threadSafety.IsReadParallel = threadSafetyValue == "ReadSafe"
threadSafety.IsSafe = threadSafetyValue == "Safe"
--threadSafety.IsLocalSafe = threadSafetyValue == ""
-- need this ^ (doesn't exist for some reason)
-- Value info --
local valueType : string
local valueTypeCategory : string
if attributeInfo.ValueType then
valueType = attributeInfo.ValueType.Name
-- the type, from typeof()
valueTypeCategory = attributeInfo.ValueType.Category
-- Always primitive
end
-- Function parameters --
local parameters : {} = nil
local returnType : string = nil
local returnCategory : string = nil
if memberType == "Function" then
parameters = attributeInfo.Parameters
if attributeInfo.ReturnType.Name ~= "void" then
returnType = attributeInfo.ReturnType.Name
returnCategory = attributeInfo.ReturnType.Category
end
end
allValues.category = dataCategory
allValues.memberType = memberType
allValues.name = dataName
allValues.isNotReplicated = isNotReplicated
allValues.isDeprecated = isDeprecated
allValues.isReadOnly = isReadOnly
allValues.isHidden = isHidden
allValues.canRead = canRead
allValues.canWrite = canWrite
allValues.serialization_canLoad = canLoad or nil
allValues.serialization_canSave = canSave or nil
allValues.threadSafety = threadSafety
allValues.valueType = valueType or nil
allValues.valueTypeCategory = valueTypeCategory or nil
allValues.isInherited = isInherited
allValues.inheritedFrom = if isInherited then passedClassName else nil
allValues.parameters = parameters
allValues.returnType = returnType
allValues.returnCategory = returnCategory
members[dataName] = allValues
end
end)
end
end)
end
if not didFind then
warn("getInfo fail! (Check parameters)")
return nil
end
return members
end
function classModule.getInstanceProperties(
passedInstance : Instance,
details : {
["Show_NotReplicated"] : boolean,
["Show_Deprecated"] : boolean,
["Show_ReadOnly"] : boolean,
["Show_Hidden"] : boolean,
["Show_Functions"] : boolean,
["Show_Events"] : boolean
}
)
local class = passedInstance.ClassName
--[[ revise
if details == nil then
details = {
Show_NotReplicated = true,
Show_Hidden = true,
Show_ReadOnly = true,
Show_Deprecated = true
}
end
--]]
if class then
local info = classModule.getInfo(class, details)
if info then
local newTable : {string : string} = {}
table.foreach(info, function(k : string, v)
if string.gsub(k, "%s", "") == k and v.canWrite ~= "PluginSecurity" and v.canWrite ~= "RobloxScriptSecurity" and v.canWrite ~= "LocalUserSecurity" then
if v.memberType == "Property" then
if k == "Origin" then
--if v.valueTypeCategory == "DataType" then
--warn(repr(v, {pretty = true}))
end
if k ~= "Origin" then
if k == "RobloxLocked" then
--warn(repr(v, {pretty = true}))
end
newTable[k] = passedInstance[k]
end
elseif v.memberType == "Function" then
newTable[k] = function(self, ...)
passedInstance[k](self, ...)
end
elseif v.memberType == "Event" then
else
warn(k, " has type ", v.memberType)
end
end
end)
return newTable
end
end
end
function classModule.getPropertiesDifferences(
instance1 : Instance,
instance2 : Instance,
details : {
["Show_NotReplicated"] : boolean,
["Show_Deprecated"] : boolean,
["Show_ReadOnly"] : boolean,
["Show_Hidden"] : boolean,
["Show_Functions"] : boolean,
["Show_Events"] : boolean
}
)
if instance1.ClassName == instance2.ClassName then
local info1 = classModule.getInstanceProperties(instance1, details)
local info2 = classModule.getInstanceProperties(instance2, details)
local classInfo = classModule.getInfo(instance1.ClassName, details)
local differences = {}
table.foreach(info1, function(k, _)
local val1 = info1[k]
local val2 = info2[k]
if val1 ~= val2 and type(val1 or val2) ~= "function" then
--table.insert(differences, k)
differences[k] = classInfo[k]
end
end)
return differences
else
warn(instance1, instance2, " are not the same class!")
end
end
return classModule
TweenModule
local tweenModule = {}
local canTweenValues = {
"number",
"boolean",
"CFrame",
"Rect",
"Color3",
"UDim",
"UDim2",
"Vector2",
"Vector2int16",
"Vector3",
"EnumItem",
}
tweenModule.tweenableTypes = {}
for i, type_ in ipairs(canTweenValues) do
table.insert(tweenModule.tweenableTypes, type_)
end
function tweenModule.canTweenValue(value)
if table.find(canTweenValues, typeof(value)) then
return true
else
return nil
end
end
return tweenModule
Any help and comments are appreciated!
Current Features
ClassInfo:
- tweenDifference: - Tweens one instance to have the same properties of the second instance (assuming they have the same class)
- returns the other two modules
ClassesModule:
- API_Version - the version of the API used
- getAllClassNames - returns the name of all the classes from the API
- ChangeDefaultDetails - edits the default value used for getting properties
- getInfo - returns the info/attributes/properties of the given class
- getInstanceProperties - returns the info/attributes/properties of the given instance
- getPropertiesDifferences - returns the property names that are different between the two instances (assuming they have the same class name)
TweenModule:
- canTweenValue - returns a boolean dictating if the given datatype can be tweened