ESOUI SVN TaosGroupUltimate

[/] [trunk/] [TaosGroupUltimate/] [libs/] [LibGroupSocket/] [handlers/] [ResourceHandler.lua] - Rev 40

Compare with Previous | Blame | View Log

-- The Group Resource Protocol
-- *bitArray* flags, *uint8* magicka percentage, *uint8* stamina percentage[, *uint16* magicka maximum, *uint16* stamina maximum]
-- flags:
--   1: isFullUpdate - the user is sending max values in addition to percentages in this packet
--   2: requestsFullUpdate - the user does not have all the necessary data and wants to have a full update from everyone (e.g. after reloading the ui)
--   3: sharesPercentagesOnly - the user does not want to share maximum values
--   4: largeMagickaPool - the user has more than 2^16 magicka, so the value has been divided by 2; around 100k magicka seems to be the most that is possible right now
--   5: largeStaminaPool - the user has more than 2^16 stamina, so the value has been divided by 2

local LGS = LibStub("LibGroupSocket")
local type, version = LGS.MESSAGE_TYPE_RESOURCES, 2
local handler, saveData = LGS:RegisterHandler(type, version)
if(not handler) then return end
local SKIP_CREATE = true
local ON_RESOURCES_CHANGED = "OnResourcesChanged"
local MIN_SEND_TIMEOUT = 2
local MIN_COMBAT_SEND_TIMEOUT = 1
local Log = LGS.Log

handler.resources = {}
local resources = handler.resources
local sendFullUpdate = true
local needFullUpdate = true
local lastSendTime = 0
local defaultData = {
    version = 1,
    enabled = true,
    percentOnly = true,
}

local function GetCachedUnitResources(unitTag, skipCreate)
    local unitName = GetUnitName(unitTag)
    local unitResources = resources[unitName]
    if(not unitResources and not skipCreate) then
        resources[unitName] = {
            [POWERTYPE_MAGICKA] = { current = 1000, maximum = 1000, percent = 255 },
            [POWERTYPE_STAMINA] = { current = 1000, maximum = 1000, percent = 255 },
            percentageOnly = true,
            hasFullData = false,
            lastUpdate = 0,
        }
        unitResources = resources[unitName]
    end
    return unitResources
end

function handler:GetLastUpdateTime(unitTag)
    local unitResources = GetCachedUnitResources(unitTag, SKIP_CREATE)
    if(unitResources) then return unitResources.lastUpdate end
    return -1
end

local function OnData(unitTag, data, isSelf)
    local index, bitIndex = 1, 1
    local isFullUpdate, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    local requestsFullUpdate, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    local sharesPercentagesOnly, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    local largeMagickaPool, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    local largeStaminaPool, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    local hasMoreStamina, index, bitIndex = LGS:ReadBit(data, index, bitIndex)
    --  Log("OnData %s (%d byte): is full: %s, needs full: %s, percent only: %s", GetUnitName(unitTag), #data, tostring(isFullUpdate), tostring(requestsFullUpdate), tostring(sharesPercentagesOnly))
    index = index + 1
    if(not isSelf and requestsFullUpdate) then
        sendFullUpdate = true
    end

    local expectedLength = isFullUpdate and 7 or 3
    if(#data < expectedLength) then Log("ResourceHandler received only %d of %d byte", #data, expectedLength) return end

    local unitResources = GetCachedUnitResources(unitTag)
    local magicka = unitResources[POWERTYPE_MAGICKA]
    local stamina = unitResources[POWERTYPE_STAMINA]

    unitResources.percentageOnly = sharesPercentagesOnly

    magicka.percent, index = LGS:ReadUint8(data, index)
    stamina.percent, index = LGS:ReadUint8(data, index)

    if(sharesPercentagesOnly) then
        magicka.maximum = 1000
        stamina.maximum = 1000
        if(hasMoreStamina) then
            stamina.maximum = stamina.maximum * 2
        else
            magicka.maximum = magicka.maximum * 2
        end
        unitResources.hasFullData = false
    elseif(isFullUpdate) then
        magicka.maximum, index = LGS:ReadUint16(data, index)
        if(largeMagickaPool) then magicka.maximum = magicka.maximum * 2 end
        stamina.maximum, index = LGS:ReadUint16(data, index)
        if(largeStaminaPool) then stamina.maximum = stamina.maximum * 2 end
        unitResources.hasFullData = true
    elseif(not unitResources.hasFullData and not isSelf) then
        needFullUpdate = true
    end

    magicka.current = math.floor((magicka.percent / 255) * magicka.maximum)
    stamina.current = math.floor((stamina.percent / 255) * stamina.maximum)

    unitResources.lastUpdate = GetTimeStamp()

    --  Log("magicka: %d/%d stamina: %d/%d", magicka.current, magicka.maximum, stamina.current, stamina.maximum)
    LGS.cm:FireCallbacks(ON_RESOURCES_CHANGED, unitTag, magicka.current, magicka.maximum, stamina.current, stamina.maximum, isSelf)
end

function handler:RegisterForResourcesChanges(callback)
    LGS.cm:RegisterCallback(ON_RESOURCES_CHANGED, callback)
end

function handler:UnregisterForResourcesChanges(callback)
    LGS.cm:UnregisterCallback(ON_RESOURCES_CHANGED, callback)
end

local function GetPowerValues(unitResources, powerType)
    local data = unitResources[powerType]
    local current, maximum = GetUnitPower("player", powerType)
    local percent = math.floor(current / maximum * 255)
    return data, current, maximum, percent
end

function handler:Send()
    if(not saveData.enabled or not IsUnitGrouped("player")) then return end
    local now = GetTimeStamp()
    local timeout = IsUnitInCombat("player") and MIN_COMBAT_SEND_TIMEOUT or MIN_SEND_TIMEOUT
    if(now - lastSendTime < timeout) then return end

    local unitResources = GetCachedUnitResources("player")
    local magicka, magickaCurrent, magickaMaximum, magickaPercent = GetPowerValues(unitResources, POWERTYPE_MAGICKA)
    local stamina, staminaCurrent, staminaMaximum, staminaPercent = GetPowerValues(unitResources, POWERTYPE_STAMINA)

    local percentOnly = saveData.percentOnly
    sendFullUpdate = sendFullUpdate or (not percentOnly and (magicka.maximum ~= magickaMaximum or stamina.maximum ~= staminaMaximum))
    if(magicka.percent ~= magickaPercent or stamina.percent ~= staminaPercent or sendFullUpdate or needFullUpdate) then
        local largeMagickaPool = (magickaMaximum >= 2^16)
        local largeStaminaPool = (staminaMaximum >= 2^16)
        local hasMoreStamina = staminaMaximum > magickaMaximum

        local data = {}
        local index, bitIndex = 1, 1
        index, bitIndex = LGS:WriteBit(data, index, bitIndex, (sendFullUpdate and not percentOnly))
        index, bitIndex = LGS:WriteBit(data, index, bitIndex, needFullUpdate)
        index, bitIndex = LGS:WriteBit(data, index, bitIndex, percentOnly)
        if(sendFullUpdate and not percentOnly) then
            index, bitIndex = LGS:WriteBit(data, index, bitIndex, largeMagickaPool)
            index, bitIndex = LGS:WriteBit(data, index, bitIndex, largeStaminaPool)
        else
            index, bitIndex = LGS:WriteBit(data, index, bitIndex, false)
            index, bitIndex = LGS:WriteBit(data, index, bitIndex, false)
        end
        index, bitIndex = LGS:WriteBit(data, index, bitIndex, hasMoreStamina)
        index = index + 1
        index = LGS:WriteUint8(data, index, magickaPercent)
        index = LGS:WriteUint8(data, index, staminaPercent)
        if(sendFullUpdate and not percentOnly) then
            if(largeMagickaPool) then magickaMaximum = math.floor(magickaMaximum / 2) end
            index = LGS:WriteUint16(data, index, magickaMaximum)

            if(largeStaminaPool) then staminaMaximum = math.floor(staminaMaximum / 2) end
            index = LGS:WriteUint16(data, index, staminaMaximum)
        end

        --              Log("Send %d byte: is full: %s, needs full: %s, percent only: %s", #data, tostring(sendFullUpdate), tostring(needFullUpdate), tostring(percentOnly))
        if(LGS:Send(type, data)) then
            lastSendTime = now
            magicka.percent = magickaPercent
            stamina.percent = staminaPercent
            if(sendFullUpdate and not percentOnly) then
                if(largeMagickaPool) then magicka.maximum = magicka.maximum * 2 end
                magicka.maximum = magickaMaximum
                if(largeStaminaPool) then stamina.maximum = stamina.maximum * 2 end
                stamina.maximum = staminaMaximum
            end
            sendFullUpdate = false
            needFullUpdate = false
        end
    end
end

local function OnUpdate()
    handler:Send()
end

local isActive = false

local function StartSending()
    if(not isActive and saveData.enabled and IsUnitGrouped("player")) then
        EVENT_MANAGER:RegisterForUpdate("LibGroupSocketResourceHandler", 1000, OnUpdate)
        isActive = true
    end
end

local function StopSending()
    if(isActive) then
        EVENT_MANAGER:UnregisterForUpdate("LibGroupSocketResourceHandler")
        isActive = false
    end
end

local function OnUnitCreated(_, unitTag)
    sendFullUpdate = true
    StartSending()
end

local function OnUnitDestroyed(_, unitTag)
    resources[GetUnitName(unitTag)] = nil
    if(isActive and not IsUnitGrouped("player")) then
        StopSending()
    end
end

function handler:InitializeSettings(optionsData, IsSendingDisabled) -- TODO: localization
    optionsData[#optionsData + 1] = {
        type = "header",
        name = "Resource Handler",
    }
optionsData[#optionsData + 1] = {
    type = "checkbox",
    name = "Enable sending",
    tooltip = "Controls if the handler does send data. It will still receive and process incoming data.",
    getFunc = function() return saveData.enabled end,
    setFunc = function(value)
        saveData.enabled = value
        if(value) then StartSending() else StopSending() end
    end,
    disabled = IsSendingDisabled,
    default = defaultData.enabled
}
optionsData[#optionsData + 1] = {
    type = "checkbox",
    name = "Send percentages only",
    tooltip = "If this is turned on, your maximum resources won't be shared with your group members",
    getFunc = function() return saveData.percentOnly end,
    setFunc = function(value) saveData.percentOnly = value end,
    disabled = IsSendingDisabled,
    default = defaultData.percentOnly
}
end

-- savedata becomes available twice in case the standalone lib is loaded
local function InitializeSaveData(data)
    saveData = data

    if(not saveData.version) then
        ZO_DeepTableCopy(defaultData, saveData)
    end

    --  if(saveData.version == 1) then
    --      -- update it
    --  end
end

local function Unload()
    LGS.cm:UnregisterCallback(type, handler.dataHandler)
    LGS.cm:UnregisterCallback("savedata-ready", InitializeSaveData)
    EVENT_MANAGER:UnregisterForEvent("LibGroupSocketResourceHandler", EVENT_UNIT_CREATED)
    EVENT_MANAGER:UnregisterForEvent("LibGroupSocketResourceHandler", EVENT_UNIT_DESTROYED)
    StopSending()
end

local function Load()
    InitializeSaveData(saveData)
    LGS.cm:RegisterCallback("savedata-ready", function(data)
        InitializeSaveData(data.handlers[type])
    end)

    handler.dataHandler = OnData
    LGS.cm:RegisterCallback(type, OnData)
    EVENT_MANAGER:RegisterForEvent("LibGroupSocketResourceHandler", EVENT_UNIT_CREATED, OnUnitCreated)
    EVENT_MANAGER:RegisterForEvent("LibGroupSocketResourceHandler", EVENT_UNIT_DESTROYED, OnUnitDestroyed)
    handler.Unload = Unload

    StartSending()
end

if(handler.Unload) then handler.Unload() end
Load()

Compare with Previous | Blame