myspace / CommonLua /Core /notify.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
6.7 kB
-- Notifications provide easy way to make lazy updates after certain changes are complete.
-- Multiple notifications within the same millisecond are treated as one.
--- Maintains a mapping of delayed call objects and their corresponding threads.
--- `DelayedCallObjects` is a table that stores the objects to be called when a delayed call is triggered.
--- `DelayedCallThreads` is a table that stores the game time threads that will execute the delayed calls.
--- `NotifyLastTimeCalled` is a variable that stores the last time a notification was triggered.
MapVar("DelayedCallObjects", {})
MapVar("DelayedCallThreads", {})
MapVar("NotifyLastTimeCalled", 0)
---
--- Executes the delayed notifications for the specified method.
--- This function is called by the game time thread created in the `Notify` function.
--- It iterates through the list of objects associated with the method and calls the corresponding method or function on each valid object.
--- After all notifications have been executed, the `DelayedCallObjects` and `DelayedCallThreads` tables are cleared for the method.
---
--- @param method string|function The method or function to be called for the delayed notifications.
--- @return function The function that will be executed by the game time thread.
---
local function DoNotify(method)
return function()
Sleep(0)
Sleep(0)
local objs = DelayedCallObjects[method]
if objs then
local i = 1
if type(method) == "function" then
while true do
local obj = objs[i]
-- This check is purposedly not re-phrased to "if not obj",
-- because obj may well be "false" in a valid situation(when a notification has been cancelled).
if obj == nil then
break
end
if IsValid(obj) then
procall(method, obj)
end
i = i + 1
end
else
while true do
local obj = objs[i]
-- This check is purposedly not re-phrased to "if not obj". Please, don't change.
if obj == nil then
break
end
if IsValid(obj) then
procall(obj[method], obj)
end
i = i + 1
end
end
else
assert(false, "Missing notify list for method " .. tostring(method))
end
DelayedCallObjects[method] = nil
DelayedCallThreads[method] = nil
end
end
---
--- Recreates the `DelayedCallThreads` table by iterating through the `DelayedCallObjects` table and creating a new game time thread for each method that has associated objects.
--- This function is called when the `NotifyLastTimeCalled` variable is updated, indicating that a new frame has started.
---
--- @internal
function RecreateNotifyStructures()
assert(not next(DelayedCallThreads))
for k, v in pairs(DelayedCallThreads) do
DelayedCallThreads[k] = DelayedCallObjects[k] and CreateGameTimeThread(DoNotify(k)) or nil
end
end
-- calls the func or the obj method when the current thread completes (but within the same millisecond)
-- multiple calls with the same obj/method pair result in one call only
---
--- Notifies the specified object using the given method.
---
--- If the `NotifyLastTimeCalled` variable indicates that a new frame has started, the `RecreateNotifyStructures()` function is called to recreate the `DelayedCallThreads` table.
---
--- If a thread for the given method does not exist, a new game time thread is created using the `DoNotify(method)` function and stored in the `DelayedCallThreads` table. The object is also added to the `DelayedCallObjects` table.
---
--- If a thread for the given method already exists, the object is added to the `DelayedCallObjects` table.
---
--- @param obj table The object to notify
--- @param method string|function The method to call on the object, or a function to call with the object as the argument
---
function Notify(obj, method)
if not obj then
return
end
local now = GameTime()
if NotifyLastTimeCalled ~= now then
RecreateNotifyStructures()
NotifyLastTimeCalled = now
end
local thread = DelayedCallThreads[method]
if not thread then
thread = CreateGameTimeThread(DoNotify(method))
DelayedCallThreads[method] = thread
DelayedCallObjects[method] = {obj, [obj]=true}
else
local objs = DelayedCallObjects[method]
if not objs[obj] then
objs[#objs + 1] = obj
objs[obj] = true
end
end
end
--- notifies all objects in <objlist>
---
--- Notifies all objects in the given list using the specified method.
---
--- If the `NotifyLastTimeCalled` variable indicates that a new frame has started, the `RecreateNotifyStructures()` function is called to recreate the `DelayedCallThreads` table.
---
--- For each object in the list, if a thread for the given method does not exist, a new game time thread is created using the `DoNotify(method)` function and stored in the `DelayedCallThreads` table. The object is also added to the `DelayedCallObjects` table.
---
--- If a thread for the given method already exists, the object is added to the `DelayedCallObjects` table.
---
--- @param objects_to_call table The list of objects to notify
--- @param method string|function The method to call on the objects, or a function to call with each object as the argument
---
function ListNotify(objects_to_call, method)
if #objects_to_call < 1 then
return
end
Notify(objects_to_call[1], method)
local objs = DelayedCallObjects[method]
assert(objs)
for i = 2, #objects_to_call do
local obj = objects_to_call[i]
if not objs[obj] then
objs[#objs + 1] = obj
objs[obj] = true
end
end
end
--- Cancels a notification for the given object and method.
---
--- If the object is registered to receive notifications for the given method, this function removes the object from the list of objects to notify.
---
--- @param obj table The object to cancel the notification for
--- @param method string|function The method to cancel the notification for
function CancelNotify(obj, method)
local objs = DelayedCallObjects[method]
if objs[obj] then
objs[obj] = nil
for i = 1, #objs do
if objs[i] == obj then
objs[i] = false
return
end
end
end
end