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 )