local Plater = _G.Plater local C_Timer = _G.C_Timer local addonName, platerInternal = ... local xpcall = xpcall local GetErrorHandler = platerInternal.GetErrorHandler local DF = DetailsFramework local LibAceSerializer = LibStub:GetLibrary ("AceSerializer-3.0") local LibDeflate = LibStub:GetLibrary ("LibDeflate") local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end local CONST_THROTTLE_HOOK_COMMS = 0.500 --2 comms per second per mod local CONST_COLORNPC_SHARING_CHANNEL = "GUILD" local CONST_PLATER_DATA_TYPE_V2 = "!PLATER:2!" --Plater.FORCE_LIB_COMPRESSION = true -- forced fallback toggle -- tmp helper method for analyzing export/import ---@param o1 any|table First object to compare ---@param o2 any|table Second object to compare function equals(o1, o2) if o1 == o2 then return true end local o1Type = type(o1) local o2Type = type(o2) if o1Type ~= o2Type then print("types", o1, o2, o1Type, o2Type) return false end if o1Type == "number" then local ae = ApproximatelyEqual(o1, o2, 0.001) print("AE", o1, o2) return ae end if o1Type ~= 'table' then print("o1 not table", o1, o2) return false end local keySet = {} for key1, value1 in pairs(o1) do local value2 = o2[key1] if value2 == nil then key1 = tonumber(key1) if key1 then value2 = o2[key1] end end --try sanitizing number indexes if value2 == nil or equals(value1, value2) == false then print("table not equal on", key1, value1, value2) return false end keySet[key1] = true end for key2, _ in pairs(o2) do if not keySet[key2..""] then local key2n = tonumber(key2) if key2n and not keySet[key2n] then print("key missing", key2, type(key2), tonumber(key2)) return false end end end return true end function compareAgainstProfile(import) local existing = DF.table.copy({}, Plater.db.profile) existing.semver = nil existing.version = nil existing.url = nil existing.login_counter = nil local imported = DF.table.copy({}, import) imported.semver = nil imported.version = nil imported.url = nil imported.login_counter = nil print("imported profile is equal to current:", equals(imported, existing)) end function Plater.CreateCommHeader(prefix, encodedString) return LibAceSerializer:Serialize(prefix, UnitName("player"), GetRealmName(), UnitGUID("player"), encodedString) end local function dispatchSendCommEvents() Plater.DispatchCommSendMessageHookEvents() C_Timer.After(CONST_THROTTLE_HOOK_COMMS, dispatchSendCommEvents) end dispatchSendCommEvents() -- can be done immediately local decompressReceivedData = function(data) local dataCompressed = LibDeflate:DecodeForWoWAddonChannel(data) if (dataCompressed) then local dataDecompressed --use native api where available if C_EncodingUtil and C_EncodingUtil.DecompressString then dataDecompressed = C_EncodingUtil.DecompressString(dataCompressed) else dataDecompressed = LibDeflate:DecompressDeflate (dataCompressed) end if (type(dataDecompressed) == "string") then return dataDecompressed end end end function Plater.SendComm(scriptIndex, scriptId, uniqueId, ...) if not Plater.VerifyScriptIdForComm(scriptIndex, scriptId, uniqueId) then return end -- block execution if verification fails --create the payload, the first index is always the hook id local arguments = {uniqueId, ...} --compress the msg local msgEncoded = Plater.CompressData(arguments, "comm") if (not msgEncoded) then return end --create the comm header local header = Plater.CreateCommHeader(Plater.COMM_SCRIPT_MSG, msgEncoded) --send the message if (IsInRaid()) then Plater:SendCommMessage(Plater.COMM_PLATER_PREFIX, header, "RAID") elseif (IsInGroup()) then Plater:SendCommMessage(Plater.COMM_PLATER_PREFIX, header, "PARTY") end return true end --when received a message from a script function Plater.MessageReceivedFromScript(prefix, source, playerRealm, playerGUID, message) local data = Plater.DecompressData(message, "comm") if (not data) then return end local scriptUID = tostring(data[1]) tremove(data, 1) --trigger the event 'Comm Received' Plater.DispatchCommReceivedMessageHookEvent(scriptUID, source, unpack(data)) end --> Plater comm handler platerInternal.Comms.CommHandler = { [Plater.COMM_SCRIPT_GROUP_EXPORTED] = Plater.ScriptReceivedFromGroup, [Plater.COMM_SCRIPT_MSG] = Plater.MessageReceivedFromScript, } function Plater:CommReceived(commPrefix, dataReceived, channel, source) local dataDeserialized = {LibAceSerializer:Deserialize(dataReceived)} local successfulDeserialize = dataDeserialized[1] if (not successfulDeserialize) then Plater:Msg("failed to deserialize a comm received.") return end local prefix = dataDeserialized[2] local unitName = source local realmName = dataDeserialized[4] local unitGUID = dataDeserialized[5] local encodedData = dataDeserialized[6] if (Plater.debugcomm) then local stringDecompressed = decompressReceivedData(encodedData) local data = {strsplit(",", stringDecompressed)} Plater:Msg("Comm Received:", prefix, source, unpack(data)) --dumpt(data) local func = platerInternal.Comms.CommHandler[prefix] print("prefix", prefix, "func", func) end local func = platerInternal.Comms.CommHandler[prefix] if (func) then local runOkay, errorMsg = xpcall(func, GetErrorHandler("Plater COMM error: "), prefix, unitName, realmName, unitGUID, encodedData, channel) if (not runOkay) then --Plater:Msg("error on something") end end end --register the comm Plater:RegisterComm(Plater.COMM_PLATER_PREFIX, "CommReceived") -- ~compress ~zip ~export ~import ~deflate ~serialize function Plater.CompressData (data, dataType) --native API support if (C_EncodingUtil and not Plater.FORCE_LIB_COMPRESSION) then local dataSerialized = C_EncodingUtil.SerializeCBOR(data) if (dataSerialized) then local dataCompressed = C_EncodingUtil.CompressString (dataSerialized, Enum.CompressionMethod.Deflate, Enum.CompressionLevel.OptimizeForSize) if (dataCompressed) then if (dataType == "print") then local dataEncoded = C_EncodingUtil.EncodeBase64(dataCompressed) if dataEncoded then return CONST_PLATER_DATA_TYPE_V2..dataEncoded end elseif (dataType == "comm" and LibDeflate) then local dataEncoded = LibDeflate:EncodeForWoWAddonChannel (dataCompressed) return CONST_PLATER_DATA_TYPE_V2..dataEncoded end end end end if (LibDeflate and LibAceSerializer) then local dataSerialized = LibAceSerializer:Serialize (data) if (dataSerialized) then local dataCompressed --use native api where available if C_EncodingUtil and C_EncodingUtil.CompressString then dataCompressed = C_EncodingUtil.CompressString (dataSerialized, Enum.CompressionMethod.Deflate, Enum.CompressionLevel.OptimizeForSize) else dataCompressed = LibDeflate:CompressDeflate (dataSerialized, {level = 9}) end if (dataCompressed) then if (dataType == "print") then local dataEncoded = LibDeflate:EncodeForPrint (dataCompressed) return dataEncoded elseif (dataType == "comm") then local dataEncoded = LibDeflate:EncodeForWoWAddonChannel (dataCompressed) return dataEncoded end end end end end -- ~compress ~zip ~export ~import ~deflate function Plater.CompressDataWithoutSerialization(data, dataType) --native API support if (C_EncodingUtil and not Plater.FORCE_LIB_COMPRESSION) then local dataCompressed = C_EncodingUtil.CompressString (data, Enum.CompressionMethod.Deflate, Enum.CompressionLevel.OptimizeForSize) if (dataCompressed) then if (dataType == "print") then local dataEncoded = C_EncodingUtil.EncodeBase64(dataCompressed) if dataEncoded then return CONST_PLATER_DATA_TYPE_V2..dataEncoded end elseif (dataType == "comm" and LibDeflate) then local dataEncoded = LibDeflate:EncodeForWoWAddonChannel (dataCompressed) return CONST_PLATER_DATA_TYPE_V2..dataEncoded end end end if (LibDeflate) then local dataCompressed --use native api where available if C_EncodingUtil and C_EncodingUtil.CompressString then dataCompressed = C_EncodingUtil.CompressString (data, Enum.CompressionMethod.Deflate, Enum.CompressionLevel.OptimizeForSize) else dataCompressed = LibDeflate:CompressDeflate (data, {level = 9}) end if (dataCompressed) then if (dataType == "print") then local dataEncoded = LibDeflate:EncodeForPrint(dataCompressed) return dataEncoded elseif (dataType == "comm") then local dataEncoded = LibDeflate:EncodeForWoWAddonChannel(dataCompressed) return dataEncoded end end end end function Plater.DecompressData (data, dataType, silent) --native API support if (string.match(data, '^'..CONST_PLATER_DATA_TYPE_V2)) then if not C_EncodingUtil then Plater:Msg ("Cannot decode v2 data due to missing EncodingUtil in this game version.") end local dataCompressed if (dataType == "print") then dataCompressed = C_EncodingUtil.DecodeBase64(string.gsub(data,CONST_PLATER_DATA_TYPE_V2,'')) if (not dataCompressed) then if not silent then Plater:Msg ("couldn't decode the data.") end return false end elseif (dataType == "comm") then dataCompressed = LibDeflate:DecodeForWoWAddonChannel (data) if (not dataCompressed) then if not silent then Plater:Msg ("couldn't decode the data.") end return false end end local dataSerialized = C_EncodingUtil.DecompressString(dataCompressed) if (not dataSerialized) then if not silent then Plater:Msg ("couldn't uncompress the data.") end return false end local dataDecompressed = C_EncodingUtil.DeserializeCBOR(dataSerialized) if (not dataDecompressed) then if not silent then Plater:Msg ("couldn't unserialize the data.") end return false end if (Plater.TEST_COMPRESS) then -- compare compareAgainstProfile(dataDecompressed) end return dataDecompressed end if (LibDeflate and LibAceSerializer) then local dataCompressed if (dataType == "print") then dataCompressed = LibDeflate:DecodeForPrint (data) if (not dataCompressed) then if not silent then Plater:Msg ("couldn't decode the data.") end return false end elseif (dataType == "comm") then dataCompressed = LibDeflate:DecodeForWoWAddonChannel (data) if (not dataCompressed) then if not silent then Plater:Msg ("couldn't decode the data.") end return false end end local dataSerialized --use native api where available if C_EncodingUtil and C_EncodingUtil.DecompressString then dataSerialized = C_EncodingUtil.DecompressString(dataCompressed) else dataSerialized = LibDeflate:DecompressDeflate (dataCompressed) end if (not dataSerialized) then if not silent then Plater:Msg ("couldn't uncompress the data.") end return false end local okay, dataDecompressed = LibAceSerializer:Deserialize (dataSerialized) if (not okay) then if not silent then Plater:Msg ("couldn't unserialize the data.") end return false end if (Plater.TEST_COMPRESS) then -- compare compareAgainstProfile(dataDecompressed) end return dataDecompressed end end --when an imported line is pasted in the wrong tab --send a message telling which tab is responsible for the data function Plater.SendScriptTypeErrorMsg(data) if (data and type(data) == "table") then if (data.type == "script") then Plater:Msg ("this import look like Script, try importing in the Scripting tab.") elseif (data.type == "hook") then Plater:Msg ("this import look like a Mod, try importing in the Modding tab.") elseif (data[Plater.Export_CastColors]) then Plater:Msg ("this import look like a Cast Colors, try importing in the Cast Colors tab.") elseif (data.NpcColor) then Plater:Msg ("this import looks to be a Npc Colors import, try importing in the Npc Colors tab.") elseif (data.plate_config) then Plater:Msg ("this import looks like a profile, import profiles at the Profiles tab.") end end Plater:Msg ("failed to import the data provided.") end ----------------------------------------------------------------------------------------- --npc color and rename local checkNpcIdIsValid = function(npcId) if (type(npcId) ~= "number") then return false end if (npcId < 1 or npcId > 500000) then return false end return true end local checkSpellIdIsValid = function(spellId) if (type(spellId) ~= "number") then return false end local spellName = GetSpellInfo(spellId) return spellName and true end local checkSpellIdIsValue = function(spellId) if (type(spellId) ~= "number") then return false end if (spellId < 1 or spellId > 600000) then return false end return true end local checkIfHasAssistanceOrIsLeader = function() --check if the player is in a raid group if (IsInRaid()) then --check if the player is the raid leader or an assistant if (UnitIsGroupLeader("player") or UnitIsGroupAssistant("player")) then return true end --check if the player is in a party group elseif (IsInGroup()) then --check if the player is the party leader if (UnitIsGroupLeader("player")) then return true end end return false end local checkReceivedDataIsValid = function(unitName, channel) --check if the data came from the guild channel if (channel ~= CONST_COLORNPC_SHARING_CHANNEL) then return end --check if the unit that sent the data is in the same group as the player --UnitInParty: return if the unit is in the same party group as the player --UnitInRaid: return if the unit is in the same raid group as the player if (not UnitInRaid(unitName) and not UnitInParty(unitName)) then return end return true end local createImportNpcCastConfirmFrame = function() local frame = DF:CreateSimplePanel(UIParent, 380, 130, "Plater Nameplates: Npc or Cast Importer", "PlaterImportNpcOrCastConfirmation") platerInternal.Frames.ImportNpcCastConfirm = frame frame:Hide() frame:SetPoint("center", UIParent, "center", 0, 150) DF:ApplyStandardBackdrop(frame) --create the font strings to show the npc name or the cast name, npcID or spellID, Sender name, and another to show the color local text1 = DF:CreateLabel(frame, "Npc Name:", 12, "white", "GameFontNormal", "white") local text2 = DF:CreateLabel(frame, "Npc ID:", 12, "white", "GameFontNormal", "white") local text3 = DF:CreateLabel(frame, "Npc Zone:", 12, "white", "GameFontNormal", "white") local text4 = DF:CreateLabel(frame, "Sender:", 12, "white", "GameFontNormal", "white") local text5 = DF:CreateLabel(frame, "Color:", 12, "white", "GameFontNormal", "white") text1:SetPoint("topleft", frame, "topleft", 10, -30) text2:SetPoint("topleft", frame, "topleft", 10, -50) text3:SetPoint("topleft", frame, "topleft", 10, -70) text4:SetPoint("topleft", frame, "topleft", 10, -90) text5:SetPoint("topleft", frame, "topleft", 10, -110) frame.Text1 = text1 frame.Text2 = text2 frame.Text3 = text3 frame.Text4 = text4 frame.Text5 = text5 local declineData = function(self, button, scriptObject, senderName) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end frame.AcceptButton = Plater:CreateButton(frame, function()end, 125, 20, "Accept", -1, nil, nil, nil, nil, nil, Plater:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE")) frame.DeclineButton = Plater:CreateButton(frame, declineData, 125, 20, "Decline", -1, nil, nil, nil, nil, nil, Plater:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE")) frame.DeclineButton:SetPoint("bottomleft", frame, "bottomleft", 5, 5) frame.AcceptButton:SetPoint("bottomright", frame, "bottomright", -5, 5) local gradientBottomSide = DF:CreateTexture(frame, {gradient = "vertical", fromColor = DF.IsDragonflight() and {0, 0, 0, 0.50} or {0, 0, 0, 0.25}, toColor = "transparent"}, 1, 70, "artwork", {0, 1, 0, 1}, "gradientBottomSide") gradientBottomSide:SetPoint("bottoms", frame, 1, 0) frame.Flash = Plater.CreateFlash(frame) end local queueToAcceptDataOfNpcsOrCasts = {} --when received a npc rename from another player or a npc color, also cast color, name or script to use function platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() --check if the import window does not exists, and create it if (not platerInternal.Frames.ImportNpcCastConfirm) then createImportNpcCastConfirmFrame() end local frame = platerInternal.Frames.ImportNpcCastConfirm if (frame:IsShown()) then frame.Title:SetText("Plater Nameplates: Npc/Cast Data Import (" .. #queueToAcceptDataOfNpcsOrCasts + 1 .. ")") return else frame.Title:SetText("Plater Nameplates: Npc/Cast Data Import (" .. #queueToAcceptDataOfNpcsOrCasts .. ")") end frame.Text1:SetText("") frame.Text2:SetText("") frame.Text3:SetText("") frame.Text4:SetText("") frame.Text5:SetText("") local nextDataToApprove = tremove(queueToAcceptDataOfNpcsOrCasts) if (nextDataToApprove) then local whichInfo = nextDataToApprove[1] if (whichInfo == "npccolor" or whichInfo == "npcrename" or whichInfo == "resetnpc") then local npcId = nextDataToApprove[2] local npcName = nextDataToApprove[3] local npcZone = nextDataToApprove[4] local senderName = nextDataToApprove[6] npcName = string.gsub(npcName, "@C@", ",") npcZone = string.gsub(npcZone, "@C@", ",") frame.Text1:SetText("From: " .. senderName) if (whichInfo == "npccolor") then local color = nextDataToApprove[5] frame.Text2:SetText("Set Npc: |cFFFFDD00" .. npcName .. "|r Color to: " .. DF:AddColorToText(color, color)) frame.AcceptButton:SetClickFunction(function() --accept the data sent and add to the database platerInternal.Comms.AcceptNpcColor(npcId, npcName, npcZone, color) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) elseif (whichInfo == "npcrename") then local newName = nextDataToApprove[5] newName = string.gsub(newName, "@C@", ",") frame.Text2:SetText("Rename: |cFFFFDD00" .. npcName .. "|r to: |cFFFFDD00" .. newName) frame.AcceptButton:SetClickFunction(function() --accept the data sent and add to the database platerInternal.Comms.AcceptNpcName(npcId, newName, npcZone) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) elseif (whichInfo == "resetnpc") then frame.Text2:SetText("Remove Npc Name and Color Customizations") frame.AcceptButton:SetClickFunction(function() platerInternal.Comms.AcceptNpcReset(npcId) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) end frame.Flash:Play() frame:Show() --play audio: IgPlayerInvite or igPlayerInviteDecline elseif (whichInfo == "castcolor" or whichInfo == "castrename" or whichInfo == "castscript" or whichInfo == "resetcast") then local spellId = nextDataToApprove[2] and tonumber(nextDataToApprove[2]) if (not spellId) then return end local npcName = nextDataToApprove[3] local npcId = nextDataToApprove[4] and tonumber(nextDataToApprove[4]) or 0 local value = nextDataToApprove[5] local senderName = nextDataToApprove[6] local spellName, _, spellIcon = GetSpellInfo(spellId) if (not spellName) then return end frame.Text1:SetText("From: " .. senderName) if (whichInfo == "castcolor") then frame.Text2:SetText("Set Spell '|cFFFFDD00" .. spellName .. "|r' color to: " .. DF:AddColorToText(value, value)) frame.AcceptButton:SetClickFunction(function() platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, value) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) elseif (whichInfo == "castrename") then frame.Text2:SetText("Set Spell '|cFFFFDD00" .. spellName .. "|r' name to: " .. value) frame.AcceptButton:SetClickFunction(function() platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, value) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) elseif (whichInfo == "castscript") then frame.Text2:SetText("Set Spell '|cFFFFDD00" .. spellName .. "|r' to use script: " .. value) frame.AcceptButton:SetClickFunction(function() platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, value) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) elseif (whichInfo == "resetcast") then frame.Text2:SetText("Remove Spell Name, Color and Scripts Customizations") frame.AcceptButton:SetClickFunction(function() platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, value) frame:Hide() platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end) end frame.Flash:Play() frame:Show() end else frame:Hide() end end --@npcId: number --@npcName: string --@npcZone: string --@color: string function platerInternal.Comms.AcceptNpcColor(npcId, npcName, npcZone, color) local npcColorTable = Plater.db.profile.npc_colors[npcId] --{[1] = is enabled, [2] = is script only, [3] = color name} if (npcColorTable) then npcColorTable[1] = true npcColorTable[2] = false npcColorTable[3] = color else --the color isn't in the database yet: add the color into the npc_colors database Plater.db.profile.npc_colors[npcId] = {true, false, color} end --check if the npc_cache has the npc name and zone, if not add them to the database if (not Plater.db.profile.npc_cache[npcId]) then Plater.db.profile.npc_cache[npcId] = {npcName, npcZone} end --refresh the colors Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() end --accept the npc name, this is similar to AcceptNpcColor but the data is different function platerInternal.Comms.AcceptNpcName(npcId, npcName, npcZone) --check if the npc_cache has the npc name and zone, if not add them to the database if (not Plater.db.profile.npc_cache[npcId]) then Plater.db.profile.npc_cache[npcId] = {npcName, npcZone} Plater.TranslateNPCCache() end local npcsRenamed = Plater.db.profile.npcs_renamed if (npcName == "") then npcsRenamed[npcId] = nil else npcsRenamed[npcId] = npcName end Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.UpdateAllPlates() Plater.ForceTickOnAllNameplates() end function platerInternal.Comms.AcceptNpcReset(npcId) --reset npc name Plater.db.profile.npcs_renamed[npcId] = nil --reset npc color local colorDB = Plater.db.profile.npc_colors local npcColorTable = colorDB[npcId] if (npcColorTable) then npcColorTable[1] = false npcColorTable[2] = false npcColorTable[3] = "white" end Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.UpdateAllPlates() Plater.ForceTickOnAllNameplates() end function platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, value) local castColorTable = Plater.db.profile.cast_colors[spellId] --{[1] = is enabled, [2] = color, [3] = renamed cast name} if (whichInfo == "castcolor") then local color = value if (not DF:IsHtmlColor(color)) then return end platerInternal.Data.SetSpellColorData(spellId, color) elseif (whichInfo == "castrename") then platerInternal.Data.SetSpellRenameData(spellId, value) elseif (whichInfo == "castscript") then local scriptName = value local scriptObject = platerInternal.Scripts.GetScriptObjectByName(scriptName) if (scriptObject) then platerInternal.Scripts.RemoveTriggerFromAnyScript(spellId) platerInternal.Scripts.AddSpellToScriptTriggers(scriptObject, spellId) end Plater.WipeAndRecompileAllScripts("script") elseif (whichInfo == "resetcast") then if (castColorTable) then castColorTable[1] = false castColorTable[2] = "white" castColorTable[3] = "" end --refresh the colors Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() --reset script local scriptObject = platerInternal.Scripts.GetDefaultScriptForSpellId(spellId) if (scriptObject) then platerInternal.Scripts.RemoveTriggerFromAnyScript(spellId) platerInternal.Scripts.AddSpellToScriptTriggers(scriptObject, spellId) Plater.WipeAndRecompileAllScripts("script") end end --check if the captured_casts has the spell name and icon, if not add them to the database if (not Plater.db.profile.captured_casts[spellId]) then Plater.db.profile.captured_casts[spellId] = {npcID = npcId, source = npcName} end --refresh the colors Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() PlaterOptionsPanelContainerCastColorManagementColorFrame.RefreshScroll(0) end function platerInternal.Comms.OnReceiveNpcOrCastInfoFromGroup(prefix, unitName, realmName, unitGUID, encodedData, channel) if (not checkReceivedDataIsValid(unitName, channel)) then return end local stringDecompressed = decompressReceivedData(encodedData) if (not stringDecompressed) then return end local data = {strsplit(",", stringDecompressed)} local whichInfo = data[1] local ID = data[2] and tonumber(data[2]) --npcId or spellId if (type(ID) ~= "number" or ID < 1 or ID > 1000000) then return end local autoAccept = data[5] and tonumber(data[5]) local bAutoAccept = (autoAccept == 1) and not Plater.db.profile.opt_out_auto_accept_npc_colors if (bAutoAccept) then if (not UnitIsGroupAssistant(unitName) and not UnitIsGroupLeader(unitName)) then return end end local value = data[6] --all values are strings if (whichInfo == "castcolor" or whichInfo == "castrename" or whichInfo == "castscript" or whichInfo == "resetcast") then local spellId = ID local npcName = data[3] local npcId = data[4] and tonumber(data[4]) or 0 --check integrity of the data if (type(spellId) ~= "number" or type(npcName) ~= "string" or type(npcId) ~= "number" or type(autoAccept) ~= "number" or type(value) ~= "string") then return end if (whichInfo == "castcolor") then local color = value if (not DF:IsHtmlColor(color)) then return end if (bAutoAccept) then platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, color) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"castcolor", spellId, npcName, npcId, color, unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() end return elseif (whichInfo == "castrename") then local customSpellName = value if (bAutoAccept) then platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, customSpellName) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"castrename", spellId, npcName, npcId, customSpellName, unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end elseif (whichInfo == "castscript") then local scriptName = value if (bAutoAccept) then platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, scriptName) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"castscript", spellId, npcName, npcId, scriptName, unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end elseif (whichInfo == "resetcast") then if (bAutoAccept) then platerInternal.Comms.AcceptCastDataFromComm(whichInfo, spellId, npcName, npcId, "") else tinsert(queueToAcceptDataOfNpcsOrCasts, {"resetcast", spellId, npcName, npcId, "", unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end end Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() elseif (whichInfo == "npccolor" or whichInfo == "npcrename" or whichInfo == "resetnpc") then local npcId = ID local npcName = data[3] local npcZone = data[4] --check integrity of the data if (type(npcId) ~= "number" or type(npcName) ~= "string" or type(npcZone) ~= "string" or type(autoAccept) ~= "number" or type(value) ~= "string") then return end if (whichInfo == "npccolor") then local color = value if (not DF:IsHtmlColor(color)) then return end if (bAutoAccept) then platerInternal.Comms.AcceptNpcColor(npcId, npcName, npcZone, color) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"npccolor", npcId, npcName, npcZone, color, unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end elseif (whichInfo == "npcrename") then if (bAutoAccept) then platerInternal.Comms.AcceptNpcName(npcId, value, npcZone) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"npcrename", npcId, npcName, npcZone, value, unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end elseif (whichInfo == "resetnpc") then if (bAutoAccept) then platerInternal.Comms.AcceptNpcReset(npcId) else tinsert(queueToAcceptDataOfNpcsOrCasts, {"resetnpc", npcId, npcName, npcZone, "", unitName}) platerInternal.Frames.ShowImportConfirmationForNpcAndCasts() return end end Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() end end platerInternal.Comms.CommHandler[Plater.COMM_NPC_OR_CAST_CUSTOMIZATION] = platerInternal.Comms.OnReceiveNpcOrCastInfoFromGroup --send npc info to group local routineCheckToSendDataToGroup = function(npcId, autoAccept, whichInfo) if (not IsInGroup()) then Plater:Msg("not in group.") return end if (not IsInGuild()) then Plater:Msg("not in a guild.") return end if (not checkIfHasAssistanceOrIsLeader()) then Plater:Msg("does not have assist or leader.") return end if (whichInfo:find("npc")) then if (not checkNpcIdIsValid(npcId)) then Plater:Msg("npcId invalid.") return end end if (whichInfo:find("cast")) then if (not checkSpellIdIsValid(npcId)) then Plater:Msg("spellId invalid.") return end end if (type(autoAccept) ~= "boolean") then Plater:Msg("autoAccept must be a boolean.") return end return true end --get the npcInfo from the npc_cache, return it if it's valid or nil if it's not local getNpcNameAndZone = function(npcId) local npcInfo = Plater.db.profile.npc_cache[npcId] if (not npcInfo) then return end local npcName = npcInfo[1] local npcZone = npcInfo[2] if (type(npcName) ~= "string" or type(npcZone) ~= "string") then return end return npcName, npcZone end --called from the GameCooltip menu after clicking in the Send To Raid button function platerInternal.Comms.SendNpcInfoToGroup(buttonClicked, npcId, autoAccept, whichInfo) GameCooltip:Hide() if (not routineCheckToSendDataToGroup(npcId, autoAccept, whichInfo)) then return end local npcName, npcZone = getNpcNameAndZone(npcId) if (not npcName) then Plater:Msg("npcInfo not found.") return end npcName = string.gsub(npcName, ",", "@C@") npcZone = string.gsub(npcZone, ",", "@C@") local dataToSend if (whichInfo == "npccolor") then local npcColorTable = Plater.db.profile.npc_colors[npcId] local npcColorName = npcColorTable and npcColorTable[3] if (not npcColorName or type(npcColorName) ~= "string") then Plater:Msg("npc does not have a color.") return end dataToSend = whichInfo .. "," .. npcId .. "," .. npcName .. "," .. npcZone .. "," .. (autoAccept and "1" or "0") .. "," .. npcColorName elseif (whichInfo == "npcrename") then local npcNameRenamed = Plater.db.profile.npcs_renamed[npcId] if (not npcNameRenamed or type(npcNameRenamed) ~= "string") then Plater:Msg("npc does not have a custom name.") return end npcNameRenamed = string.gsub(npcNameRenamed, ",", "@C@") dataToSend = whichInfo .. "," .. npcId .. "," .. npcName .. "," .. npcZone .. "," .. (autoAccept and "1" or "0") .. "," .. npcNameRenamed elseif (whichInfo == "resetnpc") then --reset to the local player as well Plater.db.profile.npcs_renamed[npcId] = nil Plater.db.profile.npc_colors[npcId] = nil PlaterOptionsPanelContainerColorManagement.RefreshScroll(0) Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() dataToSend = whichInfo .. "," .. npcId .. "," .. npcName .. "," .. npcZone .. "," .. (autoAccept and "1" or "0") .. "," .. "reset" end local encodedString = Plater.CompressDataWithoutSerialization(dataToSend, "comm") --send to guild, the receiver will check if the player is in the group Plater:SendCommMessage(Plater.COMM_PLATER_PREFIX, LibAceSerializer:Serialize(Plater.COMM_NPC_OR_CAST_CUSTOMIZATION, UnitName("player"), GetRealmName(), UnitGUID("player"), encodedString), CONST_COLORNPC_SHARING_CHANNEL) end function platerInternal.Comms.SendCastInfoToGroup(buttonClicked, spellId, autoAccept, whichInfo) GameCooltip:Hide() if (not routineCheckToSendDataToGroup(spellId, autoAccept, whichInfo)) then return end local spellName, _, spellIcon = GetSpellInfo(spellId) if (not spellName) then Plater:Msg("spellId invalid.") return end local dataToSend local capturedCasts = PlaterDB.captured_casts local thisCastInfo = capturedCasts[spellId] or {} if (whichInfo == "castcolor") then local castColorTable = Plater.db.profile.cast_colors[spellId] --the index 2 of the castColorTable is the custom color of the cast if (castColorTable and type(castColorTable) == "table" and type(castColorTable[2]) == "string" and castColorTable[2] ~= "white") then dataToSend = whichInfo .. "," .. spellId .. "," .. (thisCastInfo.source or "") .. "," .. (thisCastInfo.npcID or "") .. "," .. (autoAccept and "1" or "0") .. "," .. castColorTable[2] else Plater:Msg("cast does not have a color.") return end elseif (whichInfo == "castrename") then local castColorTable = Plater.db.profile.cast_colors[spellId] --index 3 of the castColorTable is the custom name of the cast if (castColorTable and type(castColorTable) == "table" and type(castColorTable[3]) == "string" and castColorTable[3] ~= "") then dataToSend = whichInfo .. "," .. spellId .. "," .. (thisCastInfo.source or "") .. "," .. (thisCastInfo.npcID or "") .. "," .. (autoAccept and "1" or "0") .. "," .. castColorTable[3] else Plater:Msg("cast does not have a custom name.") return end elseif (whichInfo == "castscript") then local scriptObject = platerInternal.Scripts.GetDefaultScriptForSpellId(spellId) if (not scriptObject) then return end dataToSend = whichInfo .. "," .. spellId .. "," .. (thisCastInfo.source or "") .. "," .. (thisCastInfo.npcID or "") .. "," .. (autoAccept and "1" or "0") .. "," .. scriptObject.Name elseif (whichInfo == "resetcast") then --reset cast information on the local player as well Plater.db.profile.cast_colors[spellId] = nil PlaterOptionsPanelContainerColorManagement.RefreshScroll(0) platerInternal.Scripts.RemoveTriggerFromAnyScript(spellId) Plater.RefreshDBLists() Plater.UpdateAllNameplateColors() Plater.ForceTickOnAllNameplates() dataToSend = whichInfo .. "," .. spellId .. "," .. (thisCastInfo.source or "") .. "," .. (thisCastInfo.npcID or "") .. "," .. (autoAccept and "1" or "0") .. "," .. "reset" end local encodedString = Plater.CompressDataWithoutSerialization(dataToSend, "comm") --send to guild, the receiver will check if the player is in the group Plater:SendCommMessage(Plater.COMM_PLATER_PREFIX, LibAceSerializer:Serialize(Plater.COMM_NPC_OR_CAST_CUSTOMIZATION, UnitName("player"), GetRealmName(), UnitGUID("player"), encodedString), CONST_COLORNPC_SHARING_CHANNEL) end