You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
13 KiB
332 lines
13 KiB
local ADDON_NAME, Internal = ...
|
|
local External = _G[ADDON_NAME]
|
|
local L = Internal.L
|
|
|
|
local PREFIX = ADDON_NAME
|
|
|
|
C_ChatInfo.RegisterAddonMessagePrefix(PREFIX)
|
|
|
|
-- First character of sent messages decribe what the message is about
|
|
local ControlCharacters = {
|
|
RequestAccess = '\1', -- Request access to character data
|
|
RequestAccessResponse = '\2', -- Response for access request
|
|
|
|
AuthorityNotification = '\3', -- Notify someone that you are an authority for a character
|
|
|
|
RegisterCharacterEventsMultiple = '\4', -- Register for character data events, more parts to follow
|
|
RegisterCharacterEventsLast = '\5', -- Register for character data events, last or only part
|
|
|
|
CharacterDataMultiple = '\6', -- Character data, more parts to follow
|
|
CharacterDataLast = '\7', -- Character data, last or only part
|
|
|
|
Ping = '\8', -- Check if a player is still online
|
|
Pong = '\9', -- Respond to ping
|
|
|
|
RequestSharedData = '\10',
|
|
SendSharedData = '\11',
|
|
}
|
|
|
|
local LibSerialize = LibStub("LibSerialize")
|
|
local LibDeflate = LibStub("LibDeflate")
|
|
|
|
local sharedDataLastSeen = {}
|
|
|
|
function Internal.RequestAccessToCharacter(name, realm)
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RequestAccess, "WHISPER", name .. "-" .. realm);
|
|
end
|
|
local MAX_LENGTH = 253
|
|
function Internal.RegisterRemoteCharacterEvents(slug, ...)
|
|
--@TODO get a character to request this data from instead of always from the target
|
|
|
|
local compressed = LibDeflate:CompressDeflate(slug .. " " .. table.concat({...}, " "))
|
|
local encoded = LibDeflate:EncodeForWoWAddonChannel(compressed)
|
|
|
|
for i=1,#encoded,MAX_LENGTH do
|
|
local data = strsub(encoded, i, math.min(i + MAX_LENGTH,#encoded))
|
|
if i + MAX_LENGTH >= #encoded then
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RegisterCharacterEventsLast .. data, "WHISPER", slug);
|
|
else
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RegisterCharacterEventsMultiple .. data, "WHISPER", slug);
|
|
end
|
|
end
|
|
end
|
|
|
|
local eventsToRegister = {}
|
|
local registerFrame = CreateFrame("Frame")
|
|
registerFrame:SetScript("OnUpdate", function (self)
|
|
local tbl = {}
|
|
for character,events in pairs(eventsToRegister) do
|
|
wipe(tbl)
|
|
for event in pairs(events) do
|
|
tbl[#tbl+1] = event
|
|
end
|
|
|
|
Internal.RegisterRemoteCharacterEvents(character, unpack(tbl))
|
|
end
|
|
wipe(eventsToRegister)
|
|
self:Hide()
|
|
end)
|
|
function Internal.RegisterRemoteCharacterEvent(slug, event)
|
|
local tbl = eventsToRegister[slug]
|
|
if not tbl then
|
|
tbl = {}
|
|
eventsToRegister[slug] = tbl
|
|
end
|
|
|
|
tbl[event] = true
|
|
registerFrame:Show()
|
|
end
|
|
do
|
|
-- local
|
|
function Internal.SendRemoteCharacterDataTable(slug, key, ...)
|
|
|
|
end
|
|
end
|
|
|
|
-- This is here because I thought a ping/pong might be useful
|
|
local PingTimers = {}
|
|
function Internal.PingCharacter(name, realm)
|
|
local slug = realm and (name .. "-" .. realm) or name
|
|
|
|
if PingTimers[slug] ~= false then
|
|
PingTimers[slug] = false
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.Ping, "WHISPER", slug);
|
|
|
|
-- If we dont get a response in 10 minutes wipe the value and allow for more pings
|
|
C_Timer.After(10, function ()
|
|
if PingTimers[slug] == false then
|
|
PingTimers[slug] = nil
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
local function ADDON_LOADED(event, name)
|
|
if name == ADDON_NAME then
|
|
if BtWTodoAuthorization == nil then
|
|
BtWTodoAuthorization = {} -- Characters that are authorized to view data
|
|
end
|
|
|
|
-- for character in pairs(BtWTodoAuthorization) do
|
|
-- Internal.PingCharacter(character)
|
|
-- end
|
|
|
|
Internal.UnregisterEvent("ADDON_LOADED", ADDON_LOADED)
|
|
end
|
|
end
|
|
Internal.RegisterEvent("ADDON_LOADED", ADDON_LOADED)
|
|
|
|
Internal.RegisterEvent("PING", function (event, character)
|
|
print(character)
|
|
end)
|
|
|
|
-- Used to store multipart messages
|
|
local CharacterEventsStore = {}
|
|
local CharacterDataStore = {}
|
|
Internal.RegisterEvent("CHAT_MSG_ADDON", function (event, prefix, text, channel, sender, target, zoneChannelID, localID, name, instanceID)
|
|
local name, realm = UnitFullName("player")
|
|
if prefix ~= PREFIX or sender == name .. "-" .. realm then
|
|
return
|
|
end
|
|
|
|
local control = strsub(text, 1, 1)
|
|
|
|
if control == ControlCharacters.RequestAccess then
|
|
External.TriggerEvent("REQUEST_ACCESS", sender)
|
|
elseif control == ControlCharacters.RequestAccessResponse then
|
|
External.TriggerEvent("REQUEST_ACCESS_RESPONSE", strsub(text, 2, 2) == 1)
|
|
elseif control == ControlCharacters.AuthorityNotification then
|
|
External.TriggerEvent("AUTHORITY_NOTIFICATION", strsub(text, 2), sender)
|
|
elseif control == ControlCharacters.RegisterCharacterEventsMultiple or control == ControlCharacters.RegisterCharacterEventsLast then
|
|
local tbl = CharacterEventsStore[sender]
|
|
if not tbl then
|
|
tbl = {}
|
|
CharacterEventsStore[sender] = tbl
|
|
end
|
|
|
|
tbl[#tbl+1] = strsub(text, 2)
|
|
|
|
if control == ControlCharacters.RegisterCharacterEventsLast then
|
|
local compressed = LibDeflate:DecodeForWoWAddonChannel(table.concat(tbl, ""))
|
|
local data = LibDeflate:DecompressDeflate(compressed)
|
|
|
|
-- print(sender, strsplit(" ", data))
|
|
External.TriggerEvent("REGISTER_CHARACTER_EVENT", sender, strsplit(" ", data))
|
|
|
|
CharacterEventsStore[sender] = nil
|
|
end
|
|
elseif control == ControlCharacters.OnCharacterEvent then
|
|
local compressed = LibDeflate:DecodeForWoWAddonChannel(strsub(text, 2))
|
|
local data = LibDeflate:DecompressDeflate(compressed)
|
|
|
|
External.TriggerEvent("ON_CHARACTER_EVENT", sender, strsplit(" ", data))
|
|
elseif control == ControlCharacters.CharacterDataMultiple or control == ControlCharacters.CharacterDataLast then
|
|
local tbl = CharacterDataStore[sender]
|
|
if not tbl then
|
|
tbl = {}
|
|
CharacterDataStore[sender] = tbl
|
|
end
|
|
|
|
tbl[#tbl+1] = strsub(text, 2)
|
|
|
|
if control == ControlCharacters.CharacterDataLast then
|
|
local compressed = LibDeflate:DecodeForWoWAddonChannel(table.concat(tbl, ""))
|
|
local data = LibDeflate:DecompressDeflate(compressed)
|
|
External.TriggerEvent("CHARACTER_DATA", sender, LibSerialize:Deserialize(data))
|
|
|
|
CharacterDataStore[sender] = nil
|
|
end
|
|
elseif control == ControlCharacters.Ping or control == ControlCharacters.Pong then
|
|
PingTimers[sender] = GetTime()
|
|
|
|
if control == ControlCharacters.Ping then
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.Pong, "WHISPER", sender);
|
|
end
|
|
|
|
External.TriggerEvent("PING", sender)
|
|
elseif control == ControlCharacters.RequestSharedData then
|
|
local id = strsub(text, 2)
|
|
|
|
--[==[@debug@
|
|
print("[BtWTodo] Request Shared Data " .. id)
|
|
--@end-debug@]==]
|
|
|
|
-- Super hacky cheat to prevent everyone from sending the same data when requested,
|
|
-- first person to send it will trigger the SendSharedData below, that'll update the last seen value
|
|
-- so when the timer runs out and tries to send it it'll not send it to the same place it got it from
|
|
C_Timer.After(math.random() * 5, function()
|
|
Internal.SendSharedData(id, BtWTodoCache[id])
|
|
end)
|
|
elseif control == ControlCharacters.SendSharedData then
|
|
local encoded = strsub(text, 2)
|
|
local compressed = LibDeflate:DecodeForWoWAddonChannel(encoded)
|
|
local full = LibDeflate:DecompressDeflate(compressed)
|
|
local id, serialized = strsplit(" ", full, 2)
|
|
local success, data = LibSerialize:Deserialize(serialized)
|
|
if not success or not Internal.ValidateSharedData(id, data) then
|
|
return
|
|
end
|
|
|
|
--[==[@debug@
|
|
print("[BtWTodo] Received Shared Data " .. id)
|
|
--@end-debug@]==]
|
|
|
|
BtWTodoCache[id] = data
|
|
sharedDataLastSeen[channel .. ":" .. id] = GetTime()
|
|
|
|
External.TriggerEvent("SHARED_DATA:" .. id, data)
|
|
end
|
|
end)
|
|
|
|
-- Shared data is anything that can be shared to other players, like which are the available korthia dailies, which quests for the current assault
|
|
local lastRequested = {}
|
|
local sharedDataValidators = {}
|
|
function Internal.RegisterSharedData(id, validator)
|
|
if type(id) ~= "string" then
|
|
error("External.RegisterSharedData(id, validator): id must be string")
|
|
elseif id:match("[^%w-_]") then
|
|
error(L["External.RegisterSharedData(id, validator): id must contain only word characters, dashes, and underscores"])
|
|
elseif sharedDataValidators[id] then
|
|
error("External.RegisterSharedData(id, validator): " .. id .. " is already registered")
|
|
elseif type(validator) ~= "function" then
|
|
error("External.RegisterSharedData(id, validator): validator must be a function")
|
|
end
|
|
sharedDataValidators[id] = validator
|
|
end
|
|
function Internal.ValidateSharedData(id, data)
|
|
if not sharedDataValidators[id] then
|
|
return true
|
|
end
|
|
|
|
return sharedDataValidators[id](id, data)
|
|
end
|
|
function Internal.GetSharedData(id)
|
|
if type(id) ~= "string" or id:match("[^%w-_]") then
|
|
error(L["Shared data id must be a string containing only word characters, dashes, and underscores"])
|
|
end
|
|
|
|
local data = BtWTodoCache[id]
|
|
|
|
if not data and (lastRequested[id] == nil or lastRequested[id] < GetTime() - 60) then
|
|
lastRequested[id] = GetTime()
|
|
if IsInGuild() then
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RequestSharedData .. id, "GUILD")
|
|
end
|
|
if IsInGroup(LE_PARTY_CATEGORY_HOME) then
|
|
if IsInRaid() then
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RequestSharedData .. id, "RAID")
|
|
else
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RequestSharedData .. id, "PARTY")
|
|
end
|
|
end
|
|
if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, ControlCharacters.RequestSharedData .. id, "INSTANCE_CHAT")
|
|
end
|
|
end
|
|
|
|
return data and CopyTable(data)
|
|
end
|
|
function External.GetSharedData(id)
|
|
return Internal.GetSharedData(id)
|
|
end
|
|
function Internal.SaveSharedData(id, data, dontSend)
|
|
local isDifferent = (type(BtWTodoCache[id]) ~= type(data) or data == nil or not tCompare(data, BtWTodoCache[id], 10))
|
|
local send = (dontSend == nil or dontSend) and isDifferent
|
|
|
|
--[==[@debug@
|
|
if isDifferent then
|
|
print("[" .. ADDON_NAME .. "] Save Shared Data " .. id, type(BtWTodoCache[id]), type(data), data == nil, type(data) == "table" and type(BtWTodoCache[id]) == "table" and not tCompare(data, BtWTodoCache[id], 10))
|
|
end
|
|
--@end-debug@]==]
|
|
|
|
BtWTodoCache[id] = data
|
|
if send then
|
|
Internal.SendSharedData(id, data)
|
|
end
|
|
end
|
|
function External.SaveSharedData(id, data, dontSend)
|
|
return Internal.SaveSharedData(id, data, dontSend)
|
|
end
|
|
function Internal.WipeSharedData(id)
|
|
Internal.SaveSharedData(id, nil)
|
|
end
|
|
function External.WipeSharedData(id)
|
|
return Internal.WipeSharedData(id)
|
|
end
|
|
function Internal.SendSharedData(id, data)
|
|
if data == nil or not Internal.ValidateSharedData(id, data) then
|
|
return
|
|
end
|
|
|
|
local serialized = LibSerialize:Serialize(data)
|
|
local compressed = LibDeflate:CompressDeflate(id .. " " .. serialized)
|
|
local encoded = ControlCharacters.SendSharedData .. LibDeflate:EncodeForWoWAddonChannel(compressed)
|
|
|
|
if #encoded > 254 then -- We dont chunk shared data, just keep it smaller
|
|
error("Shared data for " .. id .. " is too large")
|
|
return
|
|
end
|
|
|
|
--[==[@debug@
|
|
print("[BtWTodo] Send Shared Data " .. id)
|
|
--@end-debug@]==]
|
|
|
|
if IsInGuild() and (sharedDataLastSeen["GUILD:" .. id] == nil or sharedDataLastSeen["GUILD:" .. id] < GetTime() - 60) then
|
|
sharedDataLastSeen["GUILD:" .. id] = GetTime()
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, encoded, "GUILD")
|
|
end
|
|
if IsInGroup(LE_PARTY_CATEGORY_HOME) then
|
|
if IsInRaid() and (sharedDataLastSeen["RAID:" .. id] == nil or sharedDataLastSeen["RAID:" .. id] < GetTime() - 60) then
|
|
sharedDataLastSeen["RAID:" .. id] = GetTime()
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, encoded, "RAID")
|
|
end
|
|
if not IsInRaid() and (sharedDataLastSeen["PARTY:" .. id] == nil or sharedDataLastSeen["PARTY:" .. id] < GetTime() - 60) then
|
|
sharedDataLastSeen["PARTY:" .. id] = GetTime()
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, encoded, "PARTY")
|
|
end
|
|
end
|
|
if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and (sharedDataLastSeen["INSTANCE_CHAT:" .. id] == nil or sharedDataLastSeen["INSTANCE_CHAT:" .. id] < GetTime() - 60) then
|
|
sharedDataLastSeen["INSTANCE_CHAT:" .. id] = GetTime()
|
|
ChatThrottleLib:SendAddonMessage("NORMAL", PREFIX, encoded, "INSTANCE_CHAT")
|
|
end
|
|
end
|
|
|