• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

How can I directly get synced data?

Status
Not open for further replies.
Level 24
Joined
Jun 26, 2020
Messages
1,866
Hello, I'm trying something like this:
Lua:
local data = nil

local t = CreateTrigger()
ForForce(GetPlayersAll(), function ()
    BlzTriggerRegisterPlayerSyncEvent(t, GetEnumPlayer(), "focus", false)
end)
TriggerAddAction(t, function ()
    data = BlzGetTriggerSyncData()
end)

---@param p any
---@return any
function GetSyncPlayerData(p)
    if p == GetLocalPlayer() then
        BlzSendSyncData("focus", <my data>)
    end
    return data
end
The thing is the function BlzSendSyncData creates another thread, so the value data is not set immediatly, so the function SyncData always returns me the previous value instead of the current, I tried yield the function with the [Lua] - Precise Wait (GUI-friendly), but it didn't work, what can I do?
 
Level 24
Joined
Jun 26, 2020
Messages
1,866
I tried this using [Lua] - Definitive Doubly-Linked List:
Lua:
local threads = LinkedList.create()
local data = nil

---@param co thread
local function EnqueueThread(co)
    threads:insert(co)
end

---@return thread
local function DequeueThread()
    local node = threads.next
    local co = node.value
    node:remove()
    return co
end

local t = CreateTrigger()
ForForce(GetPlayersAll(), function ()
    BlzTriggerRegisterPlayerSyncEvent(t, GetEnumPlayer(), "focus", false)
end)
TriggerAddAction(t, function ()
    data = BlzGetTriggerSyncData()
    coroutine.resume(DequeueThread())
end)

---@param p player
---@return any
function GetSyncPlayerData(p)
    if p == GetLocalPlayer() then
        BlzSendSyncData("focus", <my data>)
    end

    local co = coroutine.running()
    EnqueueThread(co)
    coroutine.yield(co)

    return data
end
Instead of yield the thread for a specific value, I yield it until the value data is set. It seems to work, but I'm not sure, is there something I should worry?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,233
The reason the first one is not working is because sync operations take physical time so cannot complete instantly. The value of data is being returned before it has been updated.

The second approach seems to be roughly what I would recommend using. You are using coroutine yield to handle the required delay, although it assumes that the caller is itself a coroutine and not a main thread which cannot yield. I would recommend enqueuing the coroutine before syncing the data as that generally makes more sense even if it makes no difference in practice due to the single threaded nature of Lua. You can probably get around using local data by instead using coroutine.resume to pass the synced value back to the corotine.yield call directly. At least that is my understanding of the Lua reference manual on coroutines.

Another approach that is possible is to make your data sync API asynchronous. In such a case after issuing a call to sync data you would have an object representing the status of the data sync that can be queried if completed or not. This can be useful if you poll/stagger the results rather than expect to run them synchronously. If you do run them synchronously it would largely fall back to the behaviour of your current coroutine implementation in order to achieve the required yield (blocking) behaviour.

A final approach would be an API that you pass a function to consume the results of the synced data. However this would likely degenerate into spaghetti code but could be a good solution if code is generated by some sort of compiler.
 
Level 24
Joined
Jun 26, 2020
Messages
1,866
@Dr Super Good I changed the system a bit, about yielding the main thread, always print an error message when doing that, so I think is handled, about checking the results, I'm not sure how to do that, here is what I done:

Lua:
if OnGlobalInit -- https://www.hiveworkshop.com/threads/global-initialization.317099/
    and LinkedList -- https://www.hiveworkshop.com/threads/definitive-doubly-linked-list.339392/
    and Obj2Str then

    local PREFIX = "SYNC"

    local threads = LinkedList.create()

    ---@param co thread
    local function EnqueueThread(co)
        threads:insert(co)
    end

    ---@return thread
    local function DequeueThread()
        local node = threads.next
        local co = node.value
        node:remove()
        return co
    end

    ---Syncs the value of the player of the returned value of
    ---the given function.
    ---@generic T
    ---@param p player
    ---@param func fun(...: any): T
    ---@param ... any
    ---@return T
    function GetSyncedData(p, func, ...)
        if p == GetLocalPlayer() then
            BlzSendSyncData(PREFIX, Obj2Str(func(...)))
        end

        EnqueueThread(coroutine.running())

        return coroutine.yield()
    end

    OnMapInit(function ()
        local t = CreateTrigger()
        ForForce(FORCE_PLAYING, function ()
            BlzTriggerRegisterPlayerSyncEvent(t, GetEnumPlayer(), PREFIX, false)
        end)
        TriggerAddAction(t, function ()
            coroutine.resume(DequeueThread(), Str2Obj(BlzGetTriggerSyncData()))
        end)
    end)
end
Lua:
if OnGlobalInit -- https://www.hiveworkshop.com/threads/global-initialization.317099/
    and Wc3Type then -- https://www.hiveworkshop.com/threads/debug-utils-ingame-console-etc.330758/

    local h = nil ---@type hashtable

    ---I do it in this way, because I can't get a general conversion to all this types
    local Names = {
        player = "Player",
        unit = "Unit",
        destructable = "Destructable",
        item = "Item",
        ability = "Ability",
        force = "Force",
        group = "Group",
        trigger = "Trigger",
        timer = "Timer",
        location = "Location",
        region = "Region",
        rect = "Rect",
        sound = "Sound",
        effect = "Effect",
        fogmodifier = "FogModifier",
        dialog = "Dialog",
        button = "Button",
        timerdialog = "TimerDialog",
        leaderboard = "Leaderboard",
        multiboard = "Multiboard",
        texttag = "TextTag",
        lightning = "Lightning",
        image = "Image",
        ubersplat = "Ubersplat",
        hashtable = "Hashtable",
        framehandle = "Frame"
    }

    ---Converts an object to a string id
    ---@param o any
    ---@return string
    function Obj2Str(o)
        local typ = type(o)
        if typ == "nil" then
            return "nil"
        elseif typ == "number" then
            return "number: " .. tostring(o)
        elseif typ == "boolean" then
            return "boolean: " .. (o and "true" or "false")
        elseif typ == "string" then
            return "string: " .. o
        --[[elseif typ == "table" then
            local s = {}
            for k, v in pairs(o) do
                table.insert(s, "[" .. Obj2Str(k).. "] = " .. Obj2Str(v) .. "\n")
            end
            s = s:sub(1, s:len() - 2)
            return "table: {\n\t" .. table.concat(s, ", ") .. "}"]]--
        elseif typ == "userdata" then
            return Wc3Type(o) .. ": " .. GetHandleId(o)
        end
        error("Invalid object type", 2)
    end

    ---Converts an string id to object
    ---@param s string
    ---@return any
    function Str2Obj(s)
        if s == "nil" then
            return nil
        end

        local pos = s:find(":")
        if not pos then
            error("Invalid string id", 2)
        end

        local typ = s:sub(1, pos - 1)
        local value = s:sub(pos + 2)

        if typ == "number" then
            return tonumber(value)
        elseif typ == "boolean" then
            return value == "true"
        elseif typ == "string" then
            return value
        --[[elseif typ == "table" then
            local result = {}
            local comma = 0
            s = s:sub(11, s:len() - 1)
            while true do
                comma = s:find(",", comma + 1)
                if not comma then break end
                
            end
            return result]]--
        else
            local func = _G["Load" .. Names[typ] .. "Handle"]

            if not func then
                error("Invalid string id", 2)
            end

            SaveFogStateHandle(h, 0, 0, ConvertFogState(tonumber(value)))
            return func(h, 0, 0)
        end
    end

    OnMapInit(function ()
        h = InitHashtable()
    end)
    
end
 
Status
Not open for further replies.
Top