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.

578 lines
16 KiB

VUHDO_COMMS_VERSION = 1;
local sReceiveMode = 1;
local sLastCmd = nil;
local sMaxPacketSize = 254;
local sPacketSendInterval = 0.1;
local sTrivialTimeout = 3;
local sUserDialogTimeout = 15;
local sMaxReceiveSize = 100000;
local sFieldSeparator = "!";
local sCommandSeparator = "§";
local sPrefixCommand = "cmd";
local sPrefixRequest = "rqu";
local sPrefixReply = "rpy";
local sCmdAbortComms = sPrefixCommand .. "ABORT";
local sCmdVersionRequest = sPrefixRequest .. "VERSION";
local sCmdVersionReply = sPrefixReply .. "VERSION"; -- CommsVersion, VuhDo version, locale
local sCmdUserYesNoRequest = sPrefixRequest .. "YESNO";
local sCmdUserYesNoReply = sPrefixReply .. "YESNO";
sCmdProfileDataChunk = sPrefixCommand .. "P_DAT";
sCmdProfileDataEnd = sPrefixCommand .. "P_END";
sCmdKeyLayoutDataChunk = sPrefixCommand .. "K_DAT";
sCmdKeyLayoutDataEnd = sPrefixCommand .. "K_END";
local sRequestsInProgress = { --[[ [unitName][replyType] = { endTime, aResumable } ]] };
local sRepliesReceived = { --[[ [unitName][replyType] = replyString ]] };
local sUserData = { --[[ [unitName][requestType] = userData ]] };
local sBlockedSenders = { --[[ [unitName] = true ]] };
local sBusyUnitName = nil;
local sNumChunks = 0;
local sCurrentChunkTag = nil;
local sCurrentEndTag = nil;
local sCurrentQuestionText = nil;
--
function VUHDO_commsSetReceiveModeEnabled(aFlag)
sReceiveMode = aFlag and -1 or 1;
end
--
local function VUHDO_setUserData(aUnitName, aTag, someData)
if (sUserData[aUnitName] == nil) then
sUserData[aUnitName] = {};
end
sUserData[aUnitName][aTag] = someData;
end
--
local function VUHDO_getUserData(aUnitName, aTag)
return (sUserData[aUnitName] or {})[aTag];
end
--
local function VUHDO_setRequestInfo(aUnitName, aReplyType, aTimeout, aResumable)
if (sRequestsInProgress[aUnitName] == nil) then
sRequestsInProgress[aUnitName] = {};
end
if ((aTimeout or 0) > 0) then
sRequestsInProgress[aUnitName][aReplyType] = { GetTime() + aTimeout, aResumable };
else
sRequestsInProgress[aUnitName][aReplyType] = nil;
end
end
--
local function VUHDO_setReplyData(aUnitName, aReplyType, someData)
if (sRepliesReceived[aUnitName] == nil) then
sRepliesReceived[aUnitName] = { };
end
sRepliesReceived[aUnitName][aReplyType] = someData;
end
--
local function VUHDO_getReplyData(aUnitName, aReplyType)
if (sRepliesReceived[aUnitName] == nil) then
return nil;
end
return sRepliesReceived[aUnitName][aReplyType];
end
--
function VUHDO_removeCommsData(aUnitName)
if (aUnitName ~= nil) then
sUserData[aUnitName] = nil;
sRequestsInProgress[aUnitName] = nil;
sRepliesReceived[aUnitName] = nil;
if (sBusyUnitName == aUnitName) then
sBusyUnitName = nil;
sNumChunks = 0;
end
else
table.wipe(sUserData);
table.wipe(sRequestsInProgress);
table.wipe(sRepliesReceived);
sBusyUnitName = nil;
sNumChunks = 0;
end
end
--
function VUHDO_getCommsData(aUnitName)
if (aUnitName ~= nil) then
VUHDO_xMsg("Dumping comms data for", aUnitName);
VUHDO_xMsg("sUserData", VUHDO_tableToString(sUserData[aUnitName]));
VUHDO_xMsg("sRequestsInProgress", VUHDO_tableToString(sRequestsInProgress[aUnitName]));
VUHDO_xMsg("sRepliesReceived", VUHDO_tableToString(sRepliesReceived[aUnitName]));
if (sBusyUnitName == aUnitName) then
VUHDO_xMsg("sBusyUnitName", sBusyUnitName);
VUHDO_xMsg("sNumChunks", sNumChunks);
end
else
VUHDO_xMsg("Dumping comms data");
VUHDO_xMsg("sUserData", (sUserData));
VUHDO_xMsg("sRequestsInProgress", VUHDO_tableToString(sRequestsInProgress));
VUHDO_xMsg("sRepliesReceived", VUHDO_tableToString(sRepliesReceived));
if (sBusyUnitName == aUnitName) then
VUHDO_xMsg("sBusyUnitName", sBusyUnitName);
VUHDO_xMsg("sNumChunks", sNumChunks);
end
end
end
--
local function VUHDO_getFieldsFromReply(aUnitName, aReplyType)
local tUnitReplies = sRepliesReceived[aUnitName];
if (tUnitReplies == nil) then
return nil;
end
local tReply = tUnitReplies[aReplyType];
if (tReply == nil) then
return nil;
end
return strsplit(sFieldSeparator, tReply);
end
--
local function VUHDO_sendMessage(aUnitName, aMessage, aCallbackFn, aCallbackArg)
ChatThrottleLib:SendAddonMessage("BULK", VUHDO_COMMS_PREFIX, aMessage, "WHISPER", aUnitName, nil, aCallbackFn, aCallbackArg);
end
--
local function VUHDO_sendDirectMessage(aUnitName, aMessage)
C_ChatInfo.SendAddonMessage(VUHDO_COMMS_PREFIX, aMessage, "WHISPER", aUnitName);
end
--
function VUHDO_sendAbortMessage(aUnitName)
VUHDO_sendDirectMessage(aUnitName, sCmdAbortComms);
end
--
local function VUHDO_trivialRequest(aUnitName, aRequest, aReplyType, aTimeout, aResumable)
VUHDO_sendMessage(aUnitName, aRequest, nil);
VUHDO_setRequestInfo(aUnitName, aReplyType, aTimeout, aResumable);
end
--
local function VUHDO_buildMessage(aCommand, ...)
local tData = aCommand .. sCommandSeparator;
for tCnt = 1, select('#', ...) do
tData = tData .. select(tCnt, ...) .. (tCnt < select('#', ...) and sFieldSeparator or "");
end
return tData;
end
--
local function VUHDO_allDataSentCallback()
VUHDO_Msg("Done. You may now send again to this player.");
VuhDoLnfShareDialog:Hide();
end
--
local function VUHDO_dataChunkCallback(aProgress)
VuhDoLnfShareDialogTransmitPaneProgressBar:SetProgress(aProgress[1], aProgress[2]);
end
--
local function VUHDO_sendPacketedMessage(aUnitName, someData, aDataCommand, anEndCommand)
local tIndex = 0;
local tChunk;
local tLength;
local tPackets;
local tCurPacket = 0;
local tMaxLength = sMaxPacketSize - #VUHDO_COMMS_PREFIX - #aDataCommand - #sCommandSeparator;
tPackets = floor(#someData / (tMaxLength + 1));
VUHDO_Msg("Sending " .. tPackets .. " packets to " .. aUnitName .. ".");
while (tIndex < #someData) do
if (tIndex + tMaxLength < #someData) then
tLength = tMaxLength;
else
tLength = #someData - tIndex;
end
tChunk = strsub(someData, tIndex + 1, tIndex + tLength);
VUHDO_sendMessage(aUnitName, VUHDO_buildMessage(aDataCommand, tChunk), VUHDO_dataChunkCallback, { tCurPacket, tPackets });
tIndex = tIndex + tLength;
tCurPacket = tCurPacket + 1;
end
VUHDO_sendMessage(aUnitName, anEndCommand, VUHDO_allDataSentCallback);
end
--
local function VUHDO_requestVuhdoVersion(aUnitName, aResumable)
VUHDO_trivialRequest(aUnitName, sCmdVersionRequest, sCmdVersionReply, sTrivialTimeout, aResumable);
end
--
local function VUHDO_isVersionCompatible(aUnitName)
local tIsCompatible = true;
local tCommsVersion, tVuhDoVersion, tLocale = VUHDO_getFieldsFromReply(aUnitName, sCmdVersionReply);
if (tCommsVersion == nil or tVuhDoVersion == nil or tLocale == nil) then
VUHDO_Msg("Aborting: Version check failed.", 1, 0.4, 0.4);
return false;
end
if (VUHDO_COMMS_VERSION ~= tonumber(tCommsVersion)) then
VUHDO_Msg("Aborting: VuhDo comms version mismatch! Please align VuhDo versions.", 1, 0.4, 0.4);
tIsCompatible = false;
end
local tMyVersion, tOtherVersion;
if (tonumber(VUHDO_VERSION) ~= nil and tonumber(tVuhDoVersion) ~= nil) then
tMyVersion, tOtherVersion = tonumber(VUHDO_VERSION), tonumber(tVuhDoVersion);
else
tMyVersion, tOtherVersion = tostring(VUHDO_VERSION), tostring(tVuhDoVersion);
end
if (tMyVersion ~= tOtherVersion) then
VUHDO_Msg("Aborting: VuhDo version mismatch. Please align VuhDo versions! (my: " .. tMyVersion .. " / receiver:" .. tOtherVersion .. ")", 1, 0.4, 0.4);
tIsCompatible = false;
end
if (GetLocale() ~= tLocale) then
VUHDO_Msg("Aborting: Language mismatch. Sender has locale " .. tLocale, 1, 0.4, 0.4);
tIsCompatible = false;
end
if (tIsCompatible) then
VUHDO_Msg("-- VuhDo Version of " .. aUnitName .. " is compatible.");
else
VuhDoLnfShareDialog:Hide();
end
return tIsCompatible;
end
--
local function VUHDO_requestUserYesNoQuestion(aUnitName, aQuestion, aResumable)
local tRequest = VUHDO_buildMessage(sCmdUserYesNoRequest, aQuestion);
VUHDO_trivialRequest(aUnitName, tRequest, sCmdUserYesNoReply, sUserDialogTimeout, aResumable);
end
--
local function VUHDO_confirmedUserYesNoQuestion(aUnitName)
local tAnswer = VUHDO_getFieldsFromReply(aUnitName, sCmdUserYesNoReply);
if (tostring(VUHDO_YES) == tAnswer) then
VUHDO_Msg("-- User " .. aUnitName .. " confirmed transaction.");
return true;
else
VUHDO_Msg("Aborting: User " .. aUnitName .. " declined transaction.", 1, 0.4, 0.4);
VuhDoLnfShareDialog:Hide();
return false;
end
end
--
function VUHDO_compressForSending(aTable)
local compressedString = VUHDO_compressAndPackTable(aTable);
local encodedString = VUHDO_LibCompressEncode:Encode(compressedString);
return encodedString;
end
--
function VUHDO_decompressFromSending(aString)
local decodedString = VUHDO_LibCompressEncode:Decode(aString);
local decompressedTable = VUHDO_decompressIfCompressed(decodedString);
return decompressedTable;
end
--
local function VUHDO_doShareResumeAcceptQuestion(aUnitName)
if (not VUHDO_confirmedUserYesNoQuestion(aUnitName)) then
VUHDO_removeCommsData(aUnitName);
return;
end
local tData = VUHDO_getUserData(aUnitName, sCurrentChunkTag);
VUHDO_sendPacketedMessage(aUnitName, VUHDO_compressForSending(tData), sCurrentChunkTag, sCurrentEndTag);
end
--
local function VUHDO_doShareResumeVersion(aUnitName)
if (not VUHDO_isVersionCompatible(aUnitName)) then
VUHDO_removeCommsData(aUnitName);
return;
end
VUHDO_requestUserYesNoQuestion(aUnitName, sCurrentQuestionText, VUHDO_doShareResumeAcceptQuestion);
end
--
function VUHDO_startShare(aUnitName, aTable, aChunkTag, anEndTag, aQuestionText)
sCurrentChunkTag = aChunkTag;
sCurrentEndTag = anEndTag;
sCurrentQuestionText = aQuestionText;
VUHDO_setUserData(aUnitName, aChunkTag, aTable);
VUHDO_requestVuhdoVersion(aUnitName, VUHDO_doShareResumeVersion);
end
--
local function VUHDO_addReplyData(aUnitName, aType, someNewData)
local tReplyData = VUHDO_getReplyData(aUnitName, aType) or "";
tReplyData = tReplyData .. someNewData;
if (#tReplyData > sMaxReceiveSize) then
VUHDO_Msg("Aborting: Received amount of data from " .. aUnitName .. " exceeds max allowed!", 1, 0.4, 0.4);
sBlockedSenders[aUnitName] = true;
VUHDO_sendMessage(aUnitName, sCmdAbortComms, nil);
VUHDO_removeCommsData(aUnitName);
return;
end
VUHDO_setReplyData(aUnitName, aType, tReplyData);
end
--
local function VUHDO_handleCommandReceived(aSenderName, aCommand)
local tCommandType, tData = strsplit(sCommandSeparator, aCommand, 2);
if (sCmdAbortComms == tCommandType) then
VUHDO_removeCommsData(aSenderName);
VuhDoYesNoFrame:Hide();
VUHDO_Msg("Transaction aborted by " .. aSenderName);
else
if(sCmdProfileDataChunk == tCommandType) then
VUHDO_addReplyData(aSenderName, sCmdProfileDataChunk, tData);
if (sNumChunks % 20 == 0) then
VUHDO_Msg("Receiving profile data: " .. strrep(".", sNumChunks / 20 + 1));
end
sNumChunks = sNumChunks + 1;
elseif(sCmdProfileDataEnd == tCommandType) then
local tProfile = VUHDO_decompressFromSending(VUHDO_getReplyData(aSenderName, sCmdProfileDataChunk));
local tName = tProfile["NAME"];
if (VUHDO_getProfileNamedCompressed(tName) ~= nil) then
local tPos = strfind(tName, ": ", 1, true);
if (tPos ~= nil) then
tName = strsub(tName, tPos + 2);
end
tProfile["NAME"] = VUHDO_createNewProfileName(tName, aSenderName);
end
tinsert(VUHDO_PROFILES, tProfile);
VUHDO_Msg("Transmission complete. Profile \"" .. tProfile["NAME"] .. "\" has been added.");
VUHDO_removeCommsData(aSenderName);
elseif(sCmdKeyLayoutDataChunk == tCommandType) then
VUHDO_addReplyData(aSenderName, sCmdKeyLayoutDataChunk, tData);
elseif(sCmdKeyLayoutDataEnd == tCommandType) then
local tKeyLayout = VUHDO_decompressFromSending(VUHDO_getReplyData(aSenderName, sCmdKeyLayoutDataChunk));
while (VUHDO_SPELL_LAYOUTS[tKeyLayout[1]] ~= nil) do
tKeyLayout[1] = aSenderName .. ": " .. tKeyLayout[1];
end
VUHDO_Msg("Transmission complete. Key Layout \"" .. tKeyLayout[1] .. "\" has been added.");
VUHDO_SPELL_LAYOUTS[tKeyLayout[1]] = tKeyLayout[2];
VUHDO_removeCommsData(aSenderName);
else
VUHDO_Msg("Invalid VuhDo command received from " .. aSenderName .. ". Blocking.");
sBlockedSenders[aSenderName] = true;
end
end
end
--
local function VUHDO_yesNoCommsCallback(aDecision)
local tMessage = VUHDO_buildMessage(sCmdUserYesNoReply, aDecision);
VUHDO_sendMessage(VuhDoYesNoFrame:GetAttribute("senderName"), tMessage, nil);
end
--
local function VUHDO_handleRequestReceived(aSenderName, aRequest)
local tRequestType, tData = strsplit(sCommandSeparator, aRequest, 2);
if (sCmdVersionRequest == tRequestType) then
local tMessage = VUHDO_buildMessage(sCmdVersionReply, VUHDO_COMMS_VERSION, VUHDO_VERSION, GetLocale());
VUHDO_sendMessage(aSenderName, tMessage, nil);
elseif(sCmdUserYesNoRequest == tRequestType) then
VuhDoYesNoFrameText:SetText(tData);
VuhDoYesNoFrame:SetAttribute("callback", VUHDO_yesNoCommsCallback);
VuhDoYesNoFrame:SetAttribute("senderName", aSenderName);
VuhDoYesNoFrame:Show();
else
VUHDO_xMsg("Unknown request ", aRequest, "from", aSenderName);
end
end
--
local function VUHDO_handleReplyReceived(aSenderName, aMessage)
local tReplyType, tData = strsplit(sCommandSeparator, aMessage, 2);
if (tReplyType == nil or tData == nil) then
VUHDO_xMsg("3. Invalid VuhDo message received from", aSenderName, aMessage);
return;
end
if (sRequestsInProgress[aSenderName] == nil or sRequestsInProgress[aSenderName][tReplyType] == nil) then
VUHDO_xMsg("No such reply expected from ", aSenderName, aMessage);
return;
end
VUHDO_setReplyData(aSenderName, tReplyType, tData);
local tResumable = sRequestsInProgress[aSenderName][tReplyType][2];
sRequestsInProgress[aSenderName][tReplyType] = nil;
if (tResumable ~= nil) then
tResumable(aSenderName);
end
end
--
function VUHDO_parseVuhDoMessage(aSenderName, aMessage)
if (sBlockedSenders[aSenderName]) then
if (sReceiveMode < 4) then
VUHDO_Msg("Blocking comms from " .. aSenderName .. ". /reload to reset.", 1, 0.4, 0.4);
sReceiveMode = 4;
end
return;
end
if (not VUHDO_CONFIG["IS_SHARE"]) then
if (sReceiveMode > 0 and sReceiveMode < 4) then
VUHDO_Msg("Blocked comms from " .. aSenderName .. ". VuhDo options => Tools => Share is disabled.");
sReceiveMode = sReceiveMode + 1;
end
return;
end
if (sReceiveMode > 0 and sReceiveMode < 4) then
VUHDO_Msg("Blocked comms from " .. aSenderName .. ". VuhDo options screen must be opened.");
sReceiveMode = sReceiveMode + 1;
return;
elseif(sReceiveMode > 0) then
return; -- Silently fail, no spamming
end
if (strlen(aMessage or "") < 4) then
VUHDO_xMsg("1. Invalid VuhDo message received from", aSenderName, aMessage);
return;
end
local tPrefix = strsub(aMessage, 1, 3);
if ((sBusyUnitName or aSenderName) ~= aSenderName) then
return;
end
sBusyUnitName = aSenderName;
if (sPrefixCommand == tPrefix) then
VUHDO_handleCommandReceived(aSenderName, aMessage);
elseif(sPrefixRequest == tPrefix) then
VUHDO_handleRequestReceived(aSenderName, aMessage);
elseif(sPrefixReply == tPrefix) then
VUHDO_handleReplyReceived(aSenderName, aMessage);
else
VUHDO_xMsg("2. Invalid VuhDo message received from", aSenderName, aMessage);
end
end
--
function VUHDO_updateRequestsInProgress()
for tReceiverName, tSomeUnitRequests in pairs(sRequestsInProgress) do
for tReplyType, tSomeReplyInfos in pairs(tSomeUnitRequests) do
if (GetTime() > tSomeReplyInfos[1]) then
VUHDO_sendMessage(tReceiverName, sCmdAbortComms, nil);
VUHDO_removeCommsData(tReceiverName);
VUHDO_Msg("Aborting: Request to " .. tReceiverName .. " timed out.", 1, 0.4, 0.4);
VuhDoLnfShareDialog:Hide();
end
end
end
end