- Joined
- Jun 26, 2020
- Messages
- 1,928
Hello, I made this system to directly get the syncronized value of a function that returns a async value without the necessity of events:
Requires:
Usage
To get information of the camera of a player:
Maybe you wanna sync various values, and for that you can sync them individually, but every sync has a delay and they will be acumulated, to sync various values in one take you should pass a table that contains every function and after their indexes should be a table with the passed parameters (you can use
It returns a table with the returned values in the order you set them:
And if the passed function returns a lua table, it will return a copy of that lua table:
In case you are running the sync in a created thread (like running a trigger with another trigger) and you wanna also yield the first thread, use the function
WARNING: Trying to sync handles like units or frames could lead to errors that lead to desyncs, because they don't have the same handle id between players.
Pls give feedback.
Test map (Test it in multiplayer or multi-instance to look better the results):
Requires:
- Total Initialization: [Lua] - Total Initialization
- A system that converts the triggeractions to coroutines, like [Lua] - Precise Wait (GUI-friendly)
- A
Obj2Str
andStr2Obj
functions, I'm using: Typecast objects to string and viceversa
Lua:
if Debug then Debug.beginFile("GetSyncedData") end
OnInit("GetSyncedData", function ()
Require "Obj2Str" -- https://www.hiveworkshop.com/pastebin/65b5fc46fc82087ba24609b14f2dc4ff.25120
local PREFIX = "GetSyncedData__SYNC"
local END_PREFIX = "GetSyncedData__END_SYNC"
local BLOCK_LENGHT = 246
local LocalPlayer = GetLocalPlayer()
local callbacks = {} ---@type (fun(noSync: boolean?): thread)[]
local actString = ""
local actThread = nil ---@type thread
local areWaiting = {} ---@type table<thread, thread[]>
local areSyncs = false
---@param s string
local function Sync(s)
while true do
local sub = s:sub(1, BLOCK_LENGHT)
s = s:sub(BLOCK_LENGHT + 1)
if s:len() > 0 then
BlzSendSyncData(PREFIX, sub)
else
BlzSendSyncData(END_PREFIX, sub)
break
end
end
end
---Syncs the value of the player of the returned value of the given function,
---you can also pass the parameters of the function.
---
---Or you can pass an array (table) with various functions to sync and the next
---index of each function should be an array (table) with its arguments, and then the returned
---value is an array (table) with the results in the order you set them.
---
---The sync takes time, so the function yields the thread until the data is synced.
---@async
---@generic T
---@param p player
---@param func fun(...): T || table
---@vararg any
---@return T | table
function GetSyncedData(p, func, ...)
if not (GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
error("The player " .. GetPlayerName(p) .. " is not an in-game player.", 2)
end
local data = ""
if type(func) == "function" then
if p == LocalPlayer then
data = Obj2Str(func(...))
end
elseif type(func) == "table" then
if p == LocalPlayer then
local result = {}
for i = 1, #func, 2 do
table.insert(result, func[i](func[i+1] and table.unpack(func[i+1])))
end
data = Obj2Str(result)
end
else
error("Invalid parameter", 2)
end
local t = coroutine.running()
table.insert(callbacks, function (noSync)
if not noSync and p == LocalPlayer then
Sync(data)
end
return t
end)
if #callbacks == 1 then
actThread = callbacks[1]()
end
areSyncs = true
local success, value = coroutine.yield() ---@type boolean, T | table
if not success then
error("Error during the conversion", 2)
end
return value
end
---Yields the thread until last sync called from `GetSyncedData` ends.
---
---It does nothing if there isn't a queued sync from `GetSyncedData` or is the thread that function yielded or is yielded for another reason.
function WaitLastSync()
if areSyncs then
local thr = coroutine.running()
local last = callbacks[#callbacks](true)
if last == thr or coroutine.status(thr) == "suspended" then
return
end
if not areWaiting[last] then
areWaiting[last] = {}
end
table.insert(areWaiting[last], thr)
coroutine.yield(thr)
end
end
local t = CreateTrigger()
for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
BlzTriggerRegisterPlayerSyncEvent(t, Player(i), PREFIX, false)
BlzTriggerRegisterPlayerSyncEvent(t, Player(i), END_PREFIX, false)
end
TriggerAddAction(t, function ()
actString = actString .. BlzGetTriggerSyncData()
if BlzGetTriggerSyncPrefix() == END_PREFIX then
table.remove(callbacks, 1)
local thre = nil
if #callbacks > 0 then
thre = callbacks[1]()
else
areSyncs = false
end
coroutine.resume(actThread, pcall(Str2Obj, actString))
if areWaiting[actThread] then
for _, thr in ipairs(areWaiting[actThread]) do
coroutine.resume(thr)
end
areWaiting[actThread] = nil
end
actThread = thre or actThread -- Is nil if there are no more callbacks
actString = ""
end
end)
end)
if Debug then Debug.endFile() end
Usage
To get information of the camera of a player:
Lua:
-- Prints to all players the camera target distance of the Player 0.
print(GetSyncedData(Player(0), GetCameraField, CAMERA_FIELD_TARGET_DISTANCE))
nil
for no passing any parameter), example:
Lua:
local data = GetSyncedData(p, {
GetCameraBoundMaxX, nil,
GetCameraBoundMaxY, nil,
GetCameraBoundMinX, nil,
GetCameraBoundMinY, nil,
GetCameraField, {CAMERA_FIELD_ANGLE_OF_ATTACK}
})
Lua:
print("Bounds: " .. table.concat(data, ", ", 1, 4))
print("Angle of attack: " .. data[5])
Lua:
local data = GetSyncedData(Player(0), os.date, "*t")
for k, v in pairs(data) do
print(k, v) -- print the osdate of the Player Red
end
WaitLastSync
Lua:
local data
local testThread = CreateTrigger()
TriggerAddAction(testThread, function ()
data = GetSyncedData(Player(0), os.date, "*t")
end)
local t = CreateTrigger()
TriggerRegisterPlayerChatEvent(t, Player(0), "-wait", true)
TriggerAddAction(t, function ()
data = nil
TriggerExecute(testThread)
WaitLastSync() -- without this line, this thread won't be yielded
for k, v in pairs(data) do
print(k, v)
end
end)
WARNING: Trying to sync handles like units or frames could lead to errors that lead to desyncs, because they don't have the same handle id between players.
Pls give feedback.
Test map (Test it in multiplayer or multi-instance to look better the results):
Attachments
Last edited: