I’d just like to know if there are any blatant errors or use case oversights with this module;
Render Module is a QoL “service simplifier” for easily updating large amounts of objects.
- Can handle all basic RunService binds.
- Primarily targetted at metamethod use case; Can also be used for module updating.
- Can be called via the Server or the Client
- Warns you if you’re trying to update the same object twice
- Will default to updating via Heartbeat if no Priority has been assigned.
Methods
- Render:Add(Object - table; Priority - string)
Adds the Object to the pool found in Priority. Goes through some basic checks to ensure it will function correctly. - Render:BindAll()
Binds all the Priorities found within RenderPool + creates a Heartbeat connection for Heartbeat. - Render:CeaseAll()
Iterates through every table in the RenderPool, calling Object:Cease() if it has it, nullifying the pointer and removing it from the table.
.
RenderModule code
local RunService = game:GetService("RunService")
local Server = RunService:IsServer()
local RENDER = {}
local RenderPool = not Server and {
First = {};
Input = {};
Character = {};
Camera = {};
Last = {};
Heartbeat = {};
} or {
Heartbeat = {};
}
local HeartbeatCon = nil
function RENDER:BindAll()
if HeartbeatCon ~= nil then warn("Attempting to rebind, returning") return end
for Name, tbl in pairs (RenderPool) do
if Name ~= "Heartbeat" then
RunService:BindToRenderStep(Name, Enum.RenderPriority[Name].Value, function(dt)
for entry, object in pairs(tbl) do
object:Update(dt)
if object[1] == "Cease" then
object:Cease()
table.remove(tbl, entry)
end
end
end)
else
HeartbeatCon = RunService.Heartbeat:Connect(function(dt)
for entry, object in pairs(tbl) do
object:Update(dt)
if object[1] == "Cease" then
object:Cease()
table.remove(tbl, entry)
end
end
end)
end
end
end
function Obliterate(tbl)
for entry, object in pairs(tbl) do
if object and object["Cease"] and type(object["Cease"]) == "function" then
object:Cease()
end
object = nil
table.remove(tbl, entry)
end
end
function RENDER:CeaseAll()
--Unbind and disconnect all
for Name, Con in pairs (RenderPool) do
RunService:UnbindFromRenderStep(Name)
Obliterate(Con)
end
HeartbeatCon:Disconnect()
HeartbeatCon = nil
end
function RENDER:Add(Object, Priority)
local Pool
--No object case
local ENV = getfenv(2)["script"]:GetFullName()
assert(Object, string.format("'%s' tried to add something to the Render without an object!", ENV))
assert(Object["Update"] and type(Object["Update"]) == "function", string.format("'%s' passed an object without an Update function!", ENV))
--Object already exists case
for _, tbl in pairs(RenderPool) do
for entry, obj in pairs(tbl) do
if obj == Object then
warn("We're being passed an object already getting updates")
warn(string.format("Returning to the script: %s", ENV))
return
end
end
end
--Check/Set Priority
if not Priority then
warn("Object added to Render without priority, defaulting to Heartbeat")
Priority = "Heartbeat"
elseif not RenderPool[Priority] then
warn(not Server and string.format("'%s' isn't a valid priority", Priority) or "We are the server, only Heartbeat available.")
Priority = "Heartbeat"
end
Pool = RenderPool[Priority]
--Finally add to the pool
table.insert(Pool, Object)
end
RENDER:BindAll()
return RENDER
ClientTest code
local Render = require(game.ReplicatedStorage:WaitForChild("RenderModule"))
local Item = {}
local Item_mt = {__index = Item}
function Item.New()
local self = {}
self.Speech = "Hullo there"
self.LifeTime = 0
return setmetatable(self, Item_mt)
end
function Item:Update(dt)
--Action
print(self.Speech)
self.LifeTime = self.LifeTime + dt
--Should we cease?
if self.LifeTime > 5 then
self[1] = "Cease"
end
end
function Item:Cease()
--Action
print("Oh no! I have to go!")
self = nil
end
local Ayy = Item.New()
wait(2)
Render:Add(Ayy) --Adds an Item without a priority reference. Add us to Heartbeat
wait(2)
Render:Add(Ayy, "Camera") --Tries to add an Item already in a RenderPool, we'll bounce back.
ServerTest code
local Render = require(game.ReplicatedStorage:WaitForChild("RenderModule"))
local Item = {}
local Item_mt = {__index = Item}
function Item.New()
local self = {}
self.Speech = "Hullo there"
self.LifeTime = 0
return setmetatable(self, Item_mt)
end
function Item:Update(dt)
--Action
print(self.Speech)
self.LifeTime = self.LifeTime + dt
--Should we cease?
if self.LifeTime > 5 then
self[1] = "Cease"
end
end
function Item:Cease()
--Action
print("Oh no! I have to go!")
self = nil
end
local Ayy = Item.New()
wait(2)
Render:Add(Ayy, "Camera") --Tests if we can add things to different priorities.
wait(2)
Render:BindAll() --Trys to bind all the priority calls, we are already bound, so we fail and return.
wait()
Render:CeaseAll() --Trys to unbind all our binds, succeeds.
Render:BindAll() --Trys to bind all the priority calls, we are no longer bound, so we succeed.
I’m curious to know;
- Would this help you?
- Are there any problems with this module you can see that I’ve missed?
- Are there any additional functions you’d want out of this kind of simplifier?
- What are your use cases?
I’ve been using a system similair to this for a while now, figured I should make it publicly available if it helps!