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.

410 lines
15 KiB

local _, Cell = ...
local L = Cell.L
local F = Cell.funcs
local LibDeflate = LibStub:GetLibrary("LibDeflate")
local deflateConfig = {level = 9}
local Serializer = LibStub:GetLibrary("LibSerialize")
local Comm = LibStub:GetLibrary("AceComm-3.0")
local function Serialize(data)
local serialized = Serializer:Serialize(data) -- serialize
local compressed = LibDeflate:CompressDeflate(serialized, deflateConfig) -- compress
return LibDeflate:EncodeForWoWAddonChannel(compressed) -- encode
end
local function Deserialize(encoded)
local decoded = LibDeflate:DecodeForWoWAddonChannel(encoded) -- decode
local decompressed = LibDeflate:DecompressDeflate(decoded) -- decompress
if not decompressed then
F.Debug("Error decompressing: " .. errorMsg)
return
end
local success, data = Serializer:Deserialize(decompressed) -- deserialize
if not success then
F.Debug("Error deserializing: " .. data)
return
end
return data
end
-----------------------------------------
-- for WA
-----------------------------------------
function F.Notify(type, ...)
if WeakAuras then
WeakAuras.ScanEvents("CELL_NOTIFY", type, ...)
end
end
-----------------------------------------
-- shared
-----------------------------------------
local sendChannel
local function UpdateSendChannel()
if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then
sendChannel = "INSTANCE_CHAT"
elseif IsInRaid() then
sendChannel = "RAID"
else
sendChannel = "PARTY"
end
end
-----------------------------------------
-- Check Version
-----------------------------------------
local eventFrame = CreateFrame("Frame")
eventFrame:SetScript("OnEvent", function(self, event, ...)
self[event](self, ...)
end)
eventFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
function eventFrame:GROUP_ROSTER_UPDATE()
if IsInGroup() then
eventFrame:UnregisterEvent("GROUP_ROSTER_UPDATE")
UpdateSendChannel()
Comm:SendCommMessage("CELL_VERSION", Cell.version, sendChannel, nil, "NORMAL")
end
end
eventFrame:RegisterEvent("PLAYER_LOGIN")
function eventFrame:PLAYER_LOGIN()
if IsInGuild() then
Comm:SendCommMessage("CELL_VERSION", Cell.version, "GUILD", nil, "NORMAL")
end
end
Comm:RegisterComm("CELL_VERSION", function(prefix, message, channel, sender)
if sender == UnitName("player") then return end
local version = tonumber(string.match(message, "%d+"))
local myVersion = tonumber(string.match(Cell.version, "%d+"))
if (not CellDB["lastVersionCheck"] or time()-CellDB["lastVersionCheck"]>=25200) and version and myVersion and myVersion < version then
CellDB["lastVersionCheck"] = time()
F.Print(L["New version found (%s). Please visit %s to get the latest version."]:format(message, "|cFF00CCFFhttps://www.curseforge.com/wow/addons/cell|r"))
end
end)
-----------------------------------------
-- Notify Marks
-----------------------------------------
Comm:RegisterComm("CELL_MARKS", function(prefix, message, channel, sender)
if sender == UnitName("player") then return end
local data = Deserialize(message)
if Cell.vars.hasPartyMarkPermission and CellDB["tools"]["marks"][1] and (strfind(CellDB["tools"]["marks"][3], "^target") or strfind(CellDB["tools"]["marks"][3], "^both")) and data then
sender = F.GetClassColorStr(select(2, UnitClass(sender)))..sender.."|r"
if data[1] then -- lock
F.Print(L["%s lock %s on %s."]:format(sender, F.GetMarkEscapeSequence(data[2]), data[3]))
else
F.Print(L["%s unlock %s from %s."]:format(sender, F.GetMarkEscapeSequence(data[2]), data[3]))
end
end
end)
function F.NotifyMarkLock(mark, name, class)
name = F.GetClassColorStr(class)..name.."|r"
F.Print(L["%s lock %s on %s."]:format(L["You"], F.GetMarkEscapeSequence(mark), name))
UpdateSendChannel()
Comm:SendCommMessage("CELL_MARKS", Serialize({true, mark, name}), sendChannel, nil, "ALERT")
end
function F.NotifyMarkUnlock(mark, name, class)
name = F.GetClassColorStr(class)..name.."|r"
F.Print(L["%s unlock %s from %s."]:format(L["You"], F.GetMarkEscapeSequence(mark), name))
UpdateSendChannel()
Comm:SendCommMessage("CELL_MARKS", Serialize({false, mark, name}), sendChannel, nil, "ALERT")
end
-----------------------------------------
-- Priority Check
-----------------------------------------
local myPriority
local highestPriority = 99
Cell.hasHighestPriority = false
local function UpdatePriority()
myPriority = 99
if UnitIsGroupLeader("player") then
myPriority = 0
else
if IsInRaid() then
for i = 1, GetNumGroupMembers() do
if UnitIsUnit("player", "raid"..i) then
myPriority = i
break
end
end
elseif IsInGroup() then -- party
local players = {}
local pName, pRealm = UnitFullName("player")
pRealm = pRealm or GetRealmName()
pName = pName.."-"..pRealm
tinsert(players, pName)
for i = 1, GetNumGroupMembers()-1 do
local name, realm = UnitFullName("party"..i)
tinsert(players, name.."-"..(realm or pRealm))
end
table.sort(players)
for i, p in pairs(players) do
if p == pName then
myPriority = i
break
end
end
end
end
end
local t_check, t_send, t_update
function F.CheckPriority()
UpdatePriority()
-- NOTE: needs time to calc myPriority
C_Timer.After(1, function()
UpdateSendChannel()
Comm:SendCommMessage("CELL_CPRIO", "chk", sendChannel, nil, "ALERT")
end)
-- if t_check then t_check:Cancel() end
-- t_check = C_Timer.NewTimer(2, function()
-- UpdateSendChannel()
-- Comm:SendCommMessage("CELL_CPRIO", "chk", sendChannel, nil, "BULK")
-- end)
end
Comm:RegisterComm("CELL_CPRIO", function(prefix, message, channel, sender)
if not myPriority then return end -- receive CELL_CPRIO just after GOURP_JOINED
highestPriority = 99
-- NOTE: wait for check requests
if t_send then t_send:Cancel() end
t_send = C_Timer.NewTimer(2, function()
UpdateSendChannel()
Comm:SendCommMessage("CELL_PRIO", tostring(myPriority), sendChannel, nil, "ALERT")
end)
end)
Comm:RegisterComm("CELL_PRIO", function(prefix, message, channel, sender)
if not myPriority then return end -- receive CELL_PRIO just after GOURP_JOINED
local p = tonumber(message)
if p then
highestPriority = highestPriority < p and highestPriority or p
if t_update then t_update:Cancel() end
t_update = C_Timer.NewTimer(2, function()
Cell.hasHighestPriority = myPriority <= highestPriority
Cell.Fire("UpdatePriority", Cell.hasHighestPriority)
F.Debug("|cff00ff00UpdatePriority:|r", Cell.hasHighestPriority)
end)
end
end)
-----------------------------------------
-- cross realm send
-----------------------------------------
local function CrossRealmSendCommMessage(prefix, message, playerName, priority, callbackFn)
-- NOTE: unit needs to be in your group, or it will always return true
if UnitIsSameServer(playerName) then
Comm:SendCommMessage(prefix, message, "WHISPER", playerName, priority, callbackFn)
else
if UnitInParty(playerName) then
Comm:SendCommMessage(prefix, playerName..":"..message, "PARTY", nil, priority, callbackFn)
elseif UnitInRaid(playerName) then
Comm:SendCommMessage(prefix, playerName..":"..message, "RAID", nil, priority, callbackFn)
end
end
end
-----------------------------------------
-- Send / Receive Raid Debuffs and Layouts
-----------------------------------------
local function filterFunc(self, event, msg, player, arg1, arg2, arg3, flag, channelId, ...)
local newMsg = ""
local type = msg:match("%[Cell:(.+): .+]")
if type == "Debuffs" then
local bossName, instanceName, playerName = msg:match("%[Cell:Debuffs: (.+) %((.+)%) %- ([^%s]+%-[^%s]+)%]")
if bossName and instanceName and playerName then
newMsg = "|Hgarrmission:cell-debuffs|h|cFFFF0066["..L[type]..": "..bossName.." ("..instanceName..") - "..playerName.."]|h|r"
else
instanceName, playerName = msg:match("%[Cell:Debuffs: (.+) %- ([^%s]+%-[^%s]+)%]")
if instanceName and playerName then
newMsg = "|Hgarrmission:cell-debuffs|h|cFFFF0066["..L[type]..": "..instanceName.." - "..playerName.."]|h|r"
end
end
elseif type == "Layout" then
local layoutName, playerName = msg:match("%[Cell:Layout: (.+) %- ([^%s]+%-[^%s]+)%]")
if layoutName and playerName then
if layoutName == "default" then
-- NOTE: convert "default"
layoutName = _G.DEFAULT
end
newMsg = "|Hgarrmission:cell-layout|h|cFFFF0066["..L[type]..": "..layoutName.." - "..playerName.."]|h|r"
end
end
if newMsg ~= "" then
return false, newMsg, player, arg1, arg2, arg3, flag, channelId, ...
end
end
ChatFrame_AddMessageEventFilter("CHAT_MSG_CHANNEL", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_YELL", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_GUILD", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_OFFICER", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_PARTY", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_PARTY_LEADER", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_RAID", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_RAID_LEADER", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_SAY", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER_INFORM", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER_INFORM", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_INSTANCE_CHAT", filterFunc)
ChatFrame_AddMessageEventFilter("CHAT_MSG_INSTANCE_CHAT_LEADER", filterFunc)
local isRequesting
-- NOTE: received
Comm:RegisterComm("CELL_SEND", function(prefix, message, channel, sender)
if not isRequesting then return end
if channel ~= "WHISPER" then
local target
target, message = strsplit(":", message, 2)
if target ~= Cell.vars.playerNameFull then
return
end
end
local receivedData = Deserialize(message)
if Cell.frames.receivingFrame then
if receivedData then
Cell.frames.receivingFrame:ShowImport(true, receivedData, function()
isRequesting = false
end)
else
Cell.frames.receivingFrame:ShowImport(false, nil, function()
isRequesting = false
end)
end
end
end)
-- NOTE: progress received
Comm:RegisterComm("CELL_SEND_PROG", function(prefix, message, channel, sender)
if not isRequesting then return end
if channel ~= "WHISPER" then
local target
target, message = strsplit(":", message, 2)
if target ~= Cell.vars.playerNameFull then
return
end
end
local done, total = strsplit("|", message)
done, total = tonumber(done), tonumber(total)
if Cell.frames.receivingFrame then
Cell.frames.receivingFrame:ShowProgress(done, total)
end
end)
-- NOTE: request received
Comm:RegisterComm("CELL_REQ", function(prefix, message, channel, requester)
if channel ~= "WHISPER" then
local target
target, message = strsplit(":", message, 2)
if target ~= Cell.vars.playerNameFull then
return
end
end
-- check request
local type, name1, name2 = strsplit(":", message)
local requestData
-- print(type, name1, name2)
if type == "Debuffs" then
local instanceId, bossId = F.GetInstanceAndBossId(name1, name2)
if not instanceId then return end -- invalid instanceName
requestData = {
["type"] = "Debuffs",
["instanceId"] = instanceId,
["bossId"] = bossId,
["version"] = Cell.versionNum
}
-- check db
if not bossId then -- all bosses
if CellDB["raidDebuffs"][instanceId] then
requestData["data"] = CellDB["raidDebuffs"][instanceId]
end
else
if CellDB["raidDebuffs"][instanceId] and CellDB["raidDebuffs"][instanceId][bossId] then
requestData["data"] = CellDB["raidDebuffs"][instanceId][bossId]
end
end
elseif type == "Layout" then
if name1 == _G.DEFAULT then
-- NOTE: convert "DEFAULT"
name1 = "default"
end
if name1 and CellDB["layouts"][name1] then
requestData = {
["type"] = "Layout",
["name"] = name1,
["version"] = Cell.versionNum,
["data"] = CellDB["layouts"][name1]
}
end
end
-- texplore(requestData)
if not requestData then return end
CrossRealmSendCommMessage("CELL_SEND", Serialize(requestData), requester, "BULK", function(arg, done, total)
-- send progress
CrossRealmSendCommMessage("CELL_SEND_PROG", done.."|"..total, requester, "ALERT")
end)
end)
local function ShowReceivingFrame(type, playerName, name1, name2)
if not Cell.frames.receivingFrame then
Cell.frames.receivingFrame = Cell.CreateReceivingFrame(Cell.frames.mainFrame)
Cell.frames.receivingFrame:SetOnCancel(function(b)
isRequesting = false
end)
end
Cell.frames.receivingFrame:SetOnRequest(function(b)
isRequesting = true
--! send request
CrossRealmSendCommMessage("CELL_REQ", type..":"..name1..":"..(name2 or ""), playerName, "ALERT")
end)
Cell.frames.receivingFrame:ShowFrame(type, playerName, name1, name2)
end
hooksecurefunc("SetItemRef", function(link, text)
if isRequesting then return end
if link == "garrmission:cell-debuffs" then
local bossName, instanceName, playerName = text:match("|Hgarrmission:cell%-debuffs|h|cFFFF0066%[.+: (.+) %((.+)%) %- ([^%s]+%-[^%s]+)%]|h|r")
if bossName and instanceName and playerName then
ShowReceivingFrame("Debuffs", playerName, instanceName, bossName)
else
instanceName, playerName = text:match("|Hgarrmission:cell%-debuffs|h|cFFFF0066%[.+: (.+) %- ([^%s]+%-[^%s]+)%]|h|r")
ShowReceivingFrame("Debuffs", playerName, instanceName)
end
elseif link == "garrmission:cell-layout" then
local layoutName, playerName = text:match("|Hgarrmission:cell%-layout|h|cFFFF0066%[.+: (.+) %- ([^%s]+%-[^%s]+)%]|h|r")
ShowReceivingFrame("Layout", playerName, layoutName)
end
end)