local MDT = MDT local L = MDT.L local Compresser = LibStub:GetLibrary("LibCompress") local Encoder = Compresser:GetAddonEncodeTable() local Serializer = LibStub:GetLibrary("AceSerializer-3.0") local LibDeflate = LibStub:GetLibrary("LibDeflate") local configForDeflate = { [1] = { level = 1 }, [2] = { level = 2 }, [3] = { level = 3 }, [4] = { level = 4 }, [5] = { level = 5 }, [6] = { level = 6 }, [7] = { level = 7 }, [8] = { level = 8 }, [9] = { level = 9 }, } MDTcommsObject = LibStub("AceAddon-3.0"):NewAddon("MDTCommsObject", "AceComm-3.0", "AceSerializer-3.0") -- Lua APIs local string_char, tremove, tinsert = string.char, table.remove, table.insert local pairs, type, unpack = pairs, type, unpack local bit_band, bit_lshift, bit_rshift = bit.band, bit.lshift, bit.rshift --Based on code from WeakAuras2, all credit goes to the authors local bytetoB64 = { [0] = "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "(", ")" } local B64tobyte = { a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7, i = 8, j = 9, k = 10, l = 11, m = 12, n = 13, o = 14, p = 15, q = 16, r = 17, s = 18, t = 19, u = 20, v = 21, w = 22, x = 23, y = 24, z = 25, A = 26, B = 27, C = 28, D = 29, E = 30, F = 31, G = 32, H = 33, I = 34, J = 35, K = 36, L = 37, M = 38, N = 39, O = 40, P = 41, Q = 42, R = 43, S = 44, T = 45, U = 46, V = 47, W = 48, X = 49, Y = 50, Z = 51, ["0"] = 52, ["1"] = 53, ["2"] = 54, ["3"] = 55, ["4"] = 56, ["5"] = 57, ["6"] = 58, ["7"] = 59, ["8"] = 60, ["9"] = 61, ["("] = 62, [")"] = 63 } -- This code is based on the Encode7Bit algorithm from LibCompress -- Credit goes to Galmok (galmok@gmail.com) local decodeB64Table = {} local function decodeB64(str) local bit8 = decodeB64Table local decoded_size = 0 local ch local i = 1 local bitfield_len = 0 local bitfield = 0 local l = #str while true do if bitfield_len >= 8 then decoded_size = decoded_size + 1 bit8[decoded_size] = string_char(bit_band(bitfield, 255)) bitfield = bit_rshift(bitfield, 8) bitfield_len = bitfield_len - 8 end ch = B64tobyte[str:sub(i, i)] bitfield = bitfield + bit_lshift(ch or 0, bitfield_len) bitfield_len = bitfield_len + 6 if i > l then break end i = i + 1 end return table.concat(bit8, "", 1, decoded_size) end function MDT:TableToString(inTable, forChat, level) local serialized = Serializer:Serialize(inTable) local compressed = LibDeflate:CompressDeflate(serialized, configForDeflate[level]) -- prepend with "!" so that we know that it is not a legacy compression -- also this way, old versions will error out due to the "bad" encoding local encoded = "!" if (forChat) then encoded = encoded .. LibDeflate:EncodeForPrint(compressed) else encoded = encoded .. LibDeflate:EncodeForWoWAddonChannel(compressed) end return encoded end function MDT:StringToTable(inString, fromChat) -- if gsub strips off a ! at the beginning then we know that this is not a legacy encoding local encoded, usesDeflate = inString:gsub("^%!", "") local decoded if (fromChat) then if usesDeflate == 1 then decoded = LibDeflate:DecodeForPrint(encoded) else decoded = decodeB64(encoded) end else decoded = LibDeflate:DecodeForWoWAddonChannel(encoded) end if not decoded then return "Error decoding." end local decompressed, errorMsg = nil, "unknown compression method" if usesDeflate == 1 then decompressed = LibDeflate:DecompressDeflate(decoded) else decompressed, errorMsg = Compresser:Decompress(decoded) end if not (decompressed) then return "Error decompressing: " .. errorMsg end local success, deserialized = Serializer:Deserialize(decompressed) if not (success) then return "Error deserializing " .. deserialized end return deserialized end local function filterFunc(_, event, msg, player, l, cs, t, flag, channelId, ...) if flag == "GM" or flag == "DEV" or (event == "CHAT_MSG_CHANNEL" and type(channelId) == "number" and channelId > 0) then return end local newMsg = "" local remaining = msg local done repeat local start, finish, characterName, displayName = remaining:find("%[MythicDungeonTools: ([^%s]+) %- ([^%]]+)%]") local startLive, finishLive, characterNameLive, displayNameLive = remaining:find("%[MDTLive: ([^%s]+) %- ([^%]]+)%]") if (characterName and displayName) then characterName = characterName:gsub("|c[Ff][Ff]......", ""):gsub("|r", "") displayName = displayName:gsub("|c[Ff][Ff]......", ""):gsub("|r", "") newMsg = newMsg .. remaining:sub(1, start - 1) newMsg = "|cfff49d38|Hgarrmission:mdt-" .. characterName .. "|h[" .. displayName .. "]|h|r" remaining = remaining:sub(finish + 1) elseif (characterNameLive and displayNameLive) then characterNameLive = characterNameLive:gsub("|c[Ff][Ff]......", ""):gsub("|r", "") displayNameLive = displayNameLive:gsub("|c[Ff][Ff]......", ""):gsub("|r", "") newMsg = newMsg .. remaining:sub(1, startLive - 1) newMsg = newMsg .. "|Hgarrmission:mdtlive-" .. characterNameLive .. "|h[" .. "|cFF00FF00Live Session: |cfff49d38" .. "" .. displayNameLive .. "]|h|r" remaining = remaining:sub(finishLive + 1) else done = true end until (done) if newMsg ~= "" then return false, newMsg, player, l, cs, t, flag, channelId, ... end end local presetCommPrefix = "MDTPreset" MDT.liveSessionPrefixes = { ["enabled"] = "MDTLiveEnabled", ["request"] = "MDTLiveReq", ["ping"] = "MDTLivePing", ["obj"] = "MDTLiveObj", ["objOff"] = "MDTLiveObjOff", ["objChg"] = "MDTLiveObjChg", ["cmd"] = "MDTLiveCmd", ["note"] = "MDTLiveNote", ["preset"] = "MDTLivePreset", ["pull"] = "MDTLivePull", ["week"] = "MDTLiveWeek", ["free"] = "MDTLiveFree", ["bora"] = "MDTLiveBora", ["mdi"] = "MDTLiveMDI", ["reqPre"] = "MDTLiveReqPre", ["corrupted"] = "MDTLiveCor", ["difficulty"] = "MDTLiveLvl", ["poiAssignment"] = "MDTPOIAssignment", } MDT.dataCollectionPrefixes = { ["request"] = "MDTDataReq", ["distribute"] = "MDTDataDist", } function MDTcommsObject:OnEnable() self:RegisterComm(presetCommPrefix) for _, prefix in pairs(MDT.liveSessionPrefixes) do self:RegisterComm(prefix) end for _, prefix in pairs(MDT.dataCollectionPrefixes) do self:RegisterComm(prefix) end MDT.transmissionCache = {} 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) end --handle preset chat link clicks hooksecurefunc("SetItemRef", function(link, text) if (link and link:sub(0, 19) == "garrmission:mdtlive") then local sender = link:sub(21, string.len(link)) local name, realm = string.match(sender, "(.*)+(.*)") sender = name .. "-" .. realm --ignore importing the live preset when sender is player, open MDT only local playerName, playerRealm = UnitFullName("player") playerName = playerName .. "-" .. playerRealm if sender == playerName then MDT:ShowInterface(true) else MDT:ShowInterface(true) MDT:LiveSession_Enable() end return elseif (link and link:sub(0, 15) == "garrmission:mdt") then local sender = link:sub(17, string.len(link)) local name, realm = string.match(sender, "(.*)+(.*)") if (not name) or (not realm) then print(string.format(L["receiveErrorUpdate"], sender)) return end sender = name .. "-" .. realm local preset = MDT.transmissionCache[sender] if preset then MDT:ShowInterface(true) MDT:OpenChatImportPresetDialog(sender, preset) end return end end) function MDTcommsObject:OnCommReceived(prefix, message, distribution, sender) --[[ Sender has no realm name attached when sender is from the same realm as the player UnitFullName("Nnoggie") returns no realm while UnitFullName("player") does UnitFullName("Nnoggie-TarrenMill") returns realm even if you are not on the same realm as Nnoggie We append our realm if there is no realm ]] local name, realm = UnitFullName(sender) if not name then return end if not realm or string.len(realm) < 3 then local _, r = UnitFullName("player") realm = r end local fullName = name .. "-" .. realm --standard preset transmission --we cache the preset here already --the user still decides if he wants to click the chat link and add the preset to his db if prefix == presetCommPrefix then local preset = MDT:StringToTable(message, false) MDT.transmissionCache[fullName] = preset --live session preset if MDT.liveSessionActive and MDT.liveSessionAcceptingPreset and preset.uid == MDT.livePresetUID then if MDT:ValidateImportPreset(preset) then MDT:ImportPreset(preset, true) MDT.liveSessionAcceptingPreset = false MDT.main_frame.SendingStatusBar:Hide() if MDT.main_frame.LoadingSpinner then MDT.main_frame.LoadingSpinner:Hide() MDT.main_frame.LoadingSpinner.Anim:Stop() end MDT.liveSessionRequested = false end end end if prefix == MDT.dataCollectionPrefixes.request then MDT.DataCollection:DistributeData() end if prefix == MDT.dataCollectionPrefixes.distribute then if sender == UnitFullName("player") then return end local package = MDT:StringToTable(message, false) print("Received data package from " .. fullName) MDT.DataCollection:MergeReceiveData(package) end if prefix == MDT.liveSessionPrefixes.enabled then if MDT.liveSessionRequested == true then MDT:LiveSession_SessionFound(fullName, message) end end --pulls if prefix == MDT.liveSessionPrefixes.pull then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local pulls = MDT:StringToTable(message, false) preset.value.pulls = pulls if not preset.value.pulls[preset.value.currentPull] then preset.value.currentPull = #preset.value.pulls preset.value.selection = { #preset.value.pulls } end if preset == MDT:GetCurrentPreset() then MDT:ReloadPullButtons() MDT:SetSelectionToPull(MDT:GetCurrentPull()) MDT:POI_UpdateAll() --for corrupted spires MDT:UpdateProgressbar() end end end --corrupted if prefix == MDT.liveSessionPrefixes.corrupted then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local offsets = MDT:StringToTable(message, false) --only reposition if no blip is currently moving if not MDT.draggedBlip then preset.value.riftOffsets = offsets MDT:UpdateMap() end end end --difficulty if prefix == MDT.liveSessionPrefixes.difficulty then if MDT.liveSessionActive then local db = MDT:GetDB() local difficulty = tonumber(message) if difficulty and difficulty ~= db.currentDifficulty then local updateSeasonal if ((difficulty >= 10 and db.currentDifficulty < 10) or (difficulty < 10 and db.currentDifficulty >= 10)) then updateSeasonal = true end db.currentDifficulty = difficulty MDT.main_frame.sidePanel.DifficultySlider:SetValue(difficulty) MDT:UpdateProgressbar() if MDT.EnemyInfoFrame and MDT.EnemyInfoFrame.frame:IsShown() then MDT:UpdateEnemyInfoData() end MDT:ReloadPullButtons() if updateSeasonal then MDT:DungeonEnemies_UpdateSeasonalAffix() MDT.main_frame.sidePanel.difficultyWarning:Toggle(difficulty) MDT:POI_UpdateAll() MDT:KillAllAnimatedLines() MDT:DrawAllAnimatedLines() end end end end --week if prefix == MDT.liveSessionPrefixes.week then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local week = tonumber(message) if preset.week ~= week then preset.week = week local teeming = MDT:IsPresetTeeming(preset) preset.value.teeming = teeming if preset == MDT:GetCurrentPreset() then local affixDropdown = MDT.main_frame.sidePanel.affixDropdown affixDropdown:SetValue(week) if not MDT:GetCurrentAffixWeek() then MDT.main_frame.sidePanel.affixWeekWarning.image:Hide() MDT.main_frame.sidePanel.affixWeekWarning:SetDisabled(true) elseif MDT:GetCurrentAffixWeek() == week then MDT.main_frame.sidePanel.affixWeekWarning.image:Hide() MDT.main_frame.sidePanel.affixWeekWarning:SetDisabled(true) else MDT.main_frame.sidePanel.affixWeekWarning.image:Show() MDT.main_frame.sidePanel.affixWeekWarning:SetDisabled(false) end MDT:DungeonEnemies_UpdateTeeming() MDT:DungeonEnemies_UpdateInspiring() MDT:UpdateFreeholdSelector(week) MDT:DungeonEnemies_UpdateBlacktoothEvent(week) MDT:DungeonEnemies_UpdateSeasonalAffix() MDT:DungeonEnemies_UpdateBoralusFaction(preset.faction) MDT:POI_UpdateAll() MDT:UpdateProgressbar() MDT:ReloadPullButtons() MDT:KillAllAnimatedLines() MDT:DrawAllAnimatedLines() end end end end if prefix == MDT.liveSessionPrefixes.poiAssignment then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local sublevel, poiIdx, value = unpack(MDT:StringToTable(message, false)) preset.value.poiAssignments = preset.value.poiAssignments or {} preset.value.poiAssignments[sublevel] = preset.value.poiAssignments[sublevel] or {} preset.value.poiAssignments[sublevel][poiIdx] = value MDT:UpdateMap() if sender ~= UnitFullName("player") and MDT:GetCurrentSubLevel() == sublevel then local poiFrame = MDT:POI_GetFrameForPOI(poiIdx) if poiFrame then UIFrameFlash(poiFrame, 0.5, 1, 1, true, 1, 0); end end end end --live session messages that ignore concurrency from here on, we ignore our own messages if sender == UnitFullName("player") then return end if prefix == MDT.liveSessionPrefixes.request then if MDT.liveSessionActive then MDT:LiveSession_NotifyEnabled() end end --request preset if prefix == MDT.liveSessionPrefixes.reqPre then local playerName, playerRealm = UnitFullName("player") playerName = playerName .. "-" .. playerRealm if playerName == message then MDT:SendToGroup(MDT:IsPlayerInGroup(), true, MDT:GetCurrentLivePreset()) end end --ping if prefix == MDT.liveSessionPrefixes.ping then local currentUID = MDT:GetCurrentPreset().uid if MDT.liveSessionActive and (currentUID and currentUID == MDT.livePresetUID) then local x, y, sublevel = string.match(message, "(.*):(.*):(.*)") x = tonumber(x) y = tonumber(y) sublevel = tonumber(sublevel) local scale = MDT:GetScale() if sublevel == MDT:GetCurrentSubLevel() then MDT:PingMap(x * scale, y * scale) end end end --preset objects if prefix == MDT.liveSessionPrefixes.obj then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local obj = MDT:StringToTable(message, false) MDT:StorePresetObject(obj, true, preset) if preset == MDT:GetCurrentPreset() then local scale = MDT:GetScale() local currentPreset = MDT:GetCurrentPreset() local currentSublevel = MDT:GetCurrentSubLevel() MDT:DrawPresetObject(obj, nil, scale, currentPreset, currentSublevel) end end end --preset object offsets if prefix == MDT.liveSessionPrefixes.objOff then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local objIdx, x, y = string.match(message, "(.*):(.*):(.*)") objIdx = tonumber(objIdx) x = tonumber(x) y = tonumber(y) MDT:UpdatePresetObjectOffsets(objIdx, x, y, preset, true) if preset == MDT:GetCurrentPreset() then MDT:DrawAllPresetObjects() end end end --preset object changed (deletions, partial deletions) if prefix == MDT.liveSessionPrefixes.objChg then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local changedObjects = MDT:StringToTable(message, false) for objIdx, obj in pairs(changedObjects) do preset.objects[objIdx] = obj end if preset == MDT:GetCurrentPreset() then MDT:DrawAllPresetObjects() end end end --various commands if prefix == MDT.liveSessionPrefixes.cmd then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() if message == "deletePresetObjects" then MDT:DeletePresetObjects(preset, true) end if message == "undo" then MDT:PresetObjectStepBack(preset, true, true) end if message == "redo" then MDT:PresetObjectStepForward(preset, true, true) end if message == "clear" then MDT:ClearPreset(preset, true) end end end --note text update, delete, move if prefix == MDT.liveSessionPrefixes.note then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local action, noteIdx, text, y = string.match(message, "(.*):(.*):(.*):(.*)") noteIdx = tonumber(noteIdx) if action == "text" then preset.objects[noteIdx].d[5] = text elseif action == "delete" then tremove(preset.objects, noteIdx) elseif action == "move" then local x = tonumber(text) y = tonumber(y) preset.objects[noteIdx].d[1] = x preset.objects[noteIdx].d[2] = y end if preset == MDT:GetCurrentPreset() then MDT:DrawAllPresetObjects() end end end --preset if prefix == MDT.liveSessionPrefixes.preset then if MDT.liveSessionActive then local preset = MDT:StringToTable(message, false) MDT.transmissionCache[fullName] = preset if MDT:ValidateImportPreset(preset) then MDT.livePresetUID = preset.uid MDT:ImportPreset(preset, true) end end end --freehold if prefix == MDT.liveSessionPrefixes.free then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local value, week = string.match(message, "(.*):(.*)") value = value == "T" and true or false week = tonumber(week) preset.freeholdCrew = (value and week) or nil if preset == MDT:GetCurrentPreset() then MDT:DungeonEnemies_UpdateFreeholdCrew(preset.freeholdCrew) MDT:UpdateFreeholdSelector(week) MDT:ReloadPullButtons() MDT:UpdateProgressbar() end end end --Siege of Boralus if prefix == MDT.liveSessionPrefixes.bora then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local faction = tonumber(message) preset.faction = faction if preset == MDT:GetCurrentPreset() then MDT:UpdateBoralusSelector() MDT:ReloadPullButtons() MDT:UpdateProgressbar() end end end --MDI if prefix == MDT.liveSessionPrefixes.mdi then if MDT.liveSessionActive then local preset = MDT:GetCurrentLivePreset() local updateUI = preset == MDT:GetCurrentPreset() local action, data = string.match(message, "(.*):(.*)") data = tonumber(data) if action == "toggle" then MDT:GetDB().MDI.enabled = data == 1 or false MDT:DisplayMDISelector() elseif action == "beguiling" then preset.mdi.beguiling = data if updateUI then MDT.MDISelector.BeguilingDropDown:SetValue(preset.mdi.beguiling) MDT:DungeonEnemies_UpdateSeasonalAffix() MDT:DungeonEnemies_UpdateBoralusFaction(preset.faction) MDT:UpdateProgressbar() MDT:ReloadPullButtons() MDT:POI_UpdateAll() MDT:KillAllAnimatedLines() MDT:DrawAllAnimatedLines() end elseif action == "freehold" then preset.mdi.freehold = data if updateUI then MDT.MDISelector.FreeholdDropDown:SetValue(preset.mdi.freehold) if preset.mdi.freeholdJoined then MDT:DungeonEnemies_UpdateFreeholdCrew(preset.mdi.freehold) end MDT:DungeonEnemies_UpdateBlacktoothEvent() MDT:UpdateProgressbar() MDT:ReloadPullButtons() end elseif action == "join" then preset.mdi.freeholdJoined = data == 1 or false if updateUI then MDT:DungeonEnemies_UpdateFreeholdCrew() MDT:ReloadPullButtons() MDT:UpdateProgressbar() end end end end end ---MakeSendingStatusBar ---Creates a bar that indicates sending progress when sharing presets with your group ---Called once from initFrames() function MDT:MakeSendingStatusBar(f) f.SendingStatusBar = CreateFrame("StatusBar", nil, f) local statusbar = f.SendingStatusBar -- statusbar:SetMinMaxValues(0, 1) statusbar:SetPoint("LEFT", f.bottomPanel, "LEFT", 5, 0) statusbar:SetWidth(200) statusbar:SetHeight(20) statusbar:SetStatusBarTexture("Interface\\TARGETINGFRAME\\UI-StatusBar") statusbar:GetStatusBarTexture():SetHorizTile(false) statusbar:GetStatusBarTexture():SetVertTile(false) statusbar:SetStatusBarColor(0.26, 0.42, 1) statusbar.bg = statusbar:CreateTexture(nil, "BACKGROUND", nil, 0) statusbar.bg:SetTexture("Interface\\TARGETINGFRAME\\UI-StatusBar") statusbar.bg:SetAllPoints(true) statusbar.bg:SetVertexColor(0.26, 0.42, 1) statusbar.value = statusbar:CreateFontString(nil, "OVERLAY") statusbar.value:SetPoint("CENTER", statusbar, "CENTER", 0, 0) statusbar.value:SetFontObject("GameFontNormalSmall") statusbar.value:SetJustifyH("CENTER") statusbar.value:SetJustifyV("CENTER") statusbar.value:SetShadowOffset(1, -1) statusbar.value:SetTextColor(1, 1, 1) statusbar:Hide() if IsAddOnLoaded("ElvUI") then local E, L, V, P, G = unpack(ElvUI) statusbar:SetStatusBarTexture(E.media.normTex) end end --callback for SendCommMessage local function displaySendingProgress(userArgs, bytesSent, bytesToSend) MDT.main_frame.SendingStatusBar:Show() MDT.main_frame.SendingStatusBar:SetValue(bytesSent / bytesToSend) MDT.main_frame.SendingStatusBar.value:SetText(string.format(L["Sending: %.1f"], bytesSent / bytesToSend * 100) .. "%") --done sending if bytesSent == bytesToSend then local distribution = userArgs[1] local preset = userArgs[2] local silent = userArgs[3] --restore "Send" and "Live" button if MDT.liveSessionActive then MDT.main_frame.LiveSessionButton:SetText(L["*Live*"]) else MDT.main_frame.LiveSessionButton:SetText(L["Live"]) MDT.main_frame.LiveSessionButton.text:SetTextColor(1, 0.8196, 0) MDT.main_frame.LinkToChatButton:SetDisabled(false) MDT.main_frame.LinkToChatButton.text:SetTextColor(1, 0.8196, 0) end MDT.main_frame.LinkToChatButton:SetText(L["Share"]) MDT.main_frame.LiveSessionButton:SetDisabled(false) MDT.main_frame.SendingStatusBar:Hide() --output chat link if not silent then local prefix = "[MythicDungeonTools: " local dungeon = MDT:GetDungeonName(preset.value.currentDungeonIdx) local presetName = preset.text local name, realm = UnitFullName("player") --UnitFullName("player") will always return a players name with a capitalised first letter, regardless of whether --or not that is actually the case, while UnitFullName("Nnoggie") will return the player name with case respected. --This causes a subtle bug for (the few) players who's name does not begin with a capital, where chat links do not --work, because line 243 in OnCommReceived respects the case of the name, but here in the sending code we do not. --As a result, the entry in MDT.transmissionCache is indexed with case respected, but read on line 225 of this file --without respect for case (due to us sending it here, without respect for case). The fix is to subsequently call --GetUnitName(name) on the name, in order to get the correct case. name = UnitFullName(name) local fullName = name .. "+" .. realm SendChatMessage(prefix .. fullName .. " - " .. dungeon .. ": " .. presetName .. "]", distribution) end end end ---generates a unique random 11 digit number in base64 and assigns it to a preset if it does not have one yet ---credit to WeakAuras2 function MDT:SetUniqueID(preset) if not preset.uid then local s = {} for i = 1, 11 do tinsert(s, bytetoB64[math.random(0, 63)]) end preset.uid = table.concat(s) end end ---SendToGroup ---Send current preset to group/raid function MDT:SendToGroup(distribution, silent, preset) MDT:SetThrottleValues() preset = preset or MDT:GetCurrentPreset() --set unique id MDT:SetUniqueID(preset) --gotta encode mdi mode / difficulty into preset local db = MDT:GetDB() preset.mdiEnabled = db.MDI.enabled preset.difficulty = db.currentDifficulty local export = MDT:TableToString(preset, false, 5) MDTcommsObject:SendCommMessage("MDTPreset", export, distribution, nil, "BULK", displaySendingProgress, { distribution, preset, silent }) end ---GetPresetSize ---Returns the number of characters the string version of the preset contains function MDT:GetPresetSize(forChat, level) local preset = MDT:GetCurrentPreset() local export = MDT:TableToString(preset, forChat, level) return string.len(export) end function MDT:SetThrottleValues() if not _G.ChatThrottleLib then return end --4000/16000 is fine but we go safe with 2000/10000 _G.ChatThrottleLib.MAX_CPS = 2000 _G.ChatThrottleLib.BURST = 10000 end