-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsync.lua
384 lines (321 loc) · 11.7 KB
/
sync.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
local _addonName, _addon = ...;
local ADDONPREFIX = "HL"; -- DO NOT CHANGE! I'm a retard and lenghts are hardcoded now...
local IGNORE_SELF = true;
local FULL_UPDATE_WAIT = 20;
local playerName = GetUnitName("player");
local realmName = GetRealmName();
local frame = CreateFrame("Frame");
local OP_ADD_TARGET = "20";
local OP_REMOVE_TARGET = "21";
local OP_REQUEST_UPDATE = "10";
local OP_MULTIPART_UPDATE = "11";
local lastFullUpdateSent = {
["CHANNEL"] = 0,
["ADDON"] = 0
};
local fullUpdateQueued = {
["CHANNEL"] = 0,
["ADDON"] = 0
};
local recvBuffer = {};
local handlers = {};
---------------------------------------------
-- Helper functions
---------------------------------------------
--- Turn number into 2 digit left padded string
-- @param num The number to format, must be 0-99
-- @return the 2 digit string
local function PaddedLen(num)
local str = tostring(num);
if str:len() == 1 then
return "0"..str;
end
return str;
end
--- Split string into parts with given length
-- @param istr The input string to split
-- @param partLen Maximum length of single parts
-- @return the number of parts
-- @return array containing the parts
local function SplitString(istr, partLen)
local splits = 0;
local splitTable = {};
while true do
splits = splits + 1;
if istr:len() <= partLen then
splitTable[splits] = istr;
break;
end
splitTable[splits] = istr:sub(1, partLen);
istr = istr:sub(partLen+1);
end
return splits, splitTable;
end
local ProcessUpdateQueue;
do
local sinceLastUpdate = 0;
--- Process the update send "queue"
function ProcessUpdateQueue(self, tdiff)
sinceLastUpdate = sinceLastUpdate + tdiff;
if sinceLastUpdate < 1 then
return;
end
sinceLastUpdate = 0;
local curTime = GetTime();
if fullUpdateQueued["CHANNEL"] > 0 and fullUpdateQueued["CHANNEL"] < curTime then
fullUpdateQueued["CHANNEL"] = 0;
_addon:SyncSendFullList("CHANNEL");
end
if fullUpdateQueued["ADDON"] > 0 and fullUpdateQueued["ADDON"] < curTime then
fullUpdateQueued["ADDON"] = 0;
_addon:SyncSendFullList("ADDON");
end
if fullUpdateQueued["ADDON"] == 0 and fullUpdateQueued["CHANNEL"] == 0 then
_addon:PrintDebug("sync unset OnUpdate");
self:SetScript("OnUpdate", nil);
end
end
end
---------------------------------------------
-- Handler functions
---------------------------------------------
--- Handle OP_REQUEST_UPDATE
-- Sends full list update to source channel.
handlers[OP_REQUEST_UPDATE] = function(msg, author, source)
_addon:PrintDebug("Got OP_REQUEST_UPDATE from " .. author .. ", responding...");
_addon:SyncSendFullList(source);
end
--- Handle OP_ADD_TARGET
-- Adds new entry to list if data is valid.
handlers[OP_ADD_TARGET] = function(msg, author)
local nlen = tonumber(msg:sub(1,2));
local rlen = tonumber(msg:sub(3,4));
if not nlen or not rlen or (nlen+rlen+4+10) ~= msg:len() then
_addon:PrintDebug("Recieved broken OP_ADD_TARGET: " .. msg);
if rlen and nlen then
_addon:PrintDebug("Expected length " .. (nlen+rlen+4+10) .. " got " .. msg:len());
end
return;
end
local addedTime = tonumber(msg:sub(msg:len()-9));
if addedTime == nil then
_addon:PrintDebug("Got invalid entry from " .. author .. ": Time is nil");
end
local nresult, name = _addon:FormatPlayerName( msg:sub(5,4+nlen) );
local rresult, reason = _addon:FormatReason( msg:sub(5+nlen, msg:len()-10) );
if nresult == 0 and rresult == 0 then
_addon:PrintDebug(author .. " OP_ADD_TARGET: " .. name .. " | " .. reason);
_addon:AddToList(name, reason, author, addedTime);
else
_addon:PrintDebug("Got invalid entry from " .. author .. ": " .. name .. " | " .. reason);
end
end
--- Handle OP_REMOVE_TARGET
-- Remove entry from list if data is valid.
handlers[OP_REMOVE_TARGET] = function(msg, author)
local nlen = tonumber(msg:sub(1,2));
if not nlen or (nlen+2) ~= msg:len() then
_addon:PrintDebug("Recieved broken OP_REMOVE_TARGET: " .. msg);
if nlen then
_addon:PrintDebug("Expected length " .. (nlen+2) .. " got " .. msg:len());
end
return;
end
local name = msg:sub(3,nlen+2);
_addon:PrintDebug("Attempting to delete entry " .. name .. " because OP_REMOVE_TARGET from " .. author);
_addon:RemoveFromList(name, author);
end
--- Handle OP_MULTIPART_UPDATE
-- Recieve multipart updates, parse entries when last part was recieved.
-- Replaces ALL entries of the author with the newly recieved list.
handlers[OP_MULTIPART_UPDATE] = function(msg, author)
local part = tonumber(msg:sub(1,2));
local maxpart = tonumber(msg:sub(3,4));
if not part or not maxpart or msg:len() < 5 or maxpart < part then
_addon:PrintDebug("Recieved broken OP_MULTIPART_UPDATE: " .. msg);
return;
end
if maxpart > 99 then
_addon:PrintDebug("Ignore stupid part count from " .. author);
return;
end
if part == 1 then
if recvBuffer[author] == nil then
recvBuffer[author] = {
["msg"] = msg:sub(5),
["recievedCount"] = 1,
["partCount"] = maxpart
};
else
recvBuffer[author].msg = msg:sub(5);
recvBuffer[author].recievedCount = 1;
recvBuffer[author].partCount = maxpart;
end
else
if recvBuffer[author] == nil then
return;
end
recvBuffer[author].msg = recvBuffer[author].msg .. msg:sub(5);
recvBuffer[author].recievedCount = recvBuffer[author].recievedCount + 1;
end
_addon:PrintDebug("Recieved OP_MULTIPART_UPDATE from " .. author .. " (" .. recvBuffer[author].recievedCount .. "/" .. recvBuffer[author].partCount .. ")");
if recvBuffer[author].recievedCount == recvBuffer[author].partCount then
local roff = 1;
local parsedData = {};
local nlen, rlen, name, reason, addedTime;
local msg = recvBuffer[author].msg;
local msglen = msg:len();
recvBuffer[author] = nil;
while true do
if roff >= msglen then
break;
end
nlen = tonumber(msg:sub(roff, roff+1));
rlen = tonumber(msg:sub(roff+2, roff+3));
roff = roff+4;
if not nlen or nlen == 0 or not rlen or rlen == 0 then
_addon:PrintDebug("Multipart update from " .. author .. " was malformed!");
_addon:PrintDebug(msg);
return;
end
name = msg:sub(roff, roff+nlen-1);
roff = roff+nlen;
reason = msg:sub(roff, roff+rlen-1);
roff = roff+rlen;
addedTime = tonumber(msg:sub(roff, roff+9));
roff = roff+10;
parsedData[name] = {reason, addedTime};
end
_addon:ClearForeignData(author, true);
_addon:PrintDebug("Adding OP_MULTIPART_UPDATE entries from " .. author .. ":");
for k, data in pairs(parsedData) do
if data[2] == nil then
_addon:PrintDebug("INVALID ENTRY: Time is nil");
end
nlen, name = _addon:FormatPlayerName(k);
rlen, reason = _addon:FormatReason(data[1]);
if nlen == 0 and rlen == 0 then
_addon:AddToList(name, reason, author, data[2]);
else
_addon:PrintDebug("INVALID ENTRY: " .. name .. " | " .. reason);
end
end
end
end
---------------------------------------------
-- Events
---------------------------------------------
--- Handle CHAT_MSG_ADDON events
function frame.CHAT_MSG_ADDON(prefix, msg, distType, sender)
if not Hitlist_settings.syncGuild or prefix ~= ADDONPREFIX or distType ~= "GUILD" then
return;
end
sender = _addon.RemoveServerDash(sender);
if IGNORE_SELF and sender == playerName then
return;
end
local OPC = msg:sub(1,2);
if handlers[OPC] ~= nil then
handlers[OPC](msg:sub(3), sender, "ADDON");
return;
end
_addon:PrintDebug("Invalid OPC from " .. sender .. " recieved: " .. OPC);
end
--- Handle CHAT_MSG_CHANNEL events
function frame.CHAT_MSG_CHANNEL(msg, author, lang, arg4, arg5, arg6, arg7, channelNum, channelName)
if not Hitlist_settings.syncChannel or channelNum ~= _addon.syncChannelId or msg:sub(1,2) ~= ADDONPREFIX then
return;
end
author = _addon.RemoveServerDash(author);
if IGNORE_SELF and author == playerName then
return;
end
local OPC = msg:sub(3,4);
if handlers[OPC] ~= nil then
handlers[OPC](msg:sub(5), author, "CHANNEL");
return;
end
_addon:PrintDebug("Invalid OPC from " .. author .. " recieved: " .. OPC);
end
frame:SetScript("OnEvent", function(self, event, ...) frame[event](...); end);
frame:RegisterEvent("CHAT_MSG_ADDON");
frame:RegisterEvent("CHAT_MSG_CHANNEL");
---------------------------------------------
-- Addon functions
---------------------------------------------
--- Send message out in selected channel
-- Only works if relevant sync options are on.
-- @param msg The message to send
-- @param target If not nil only send in "ADDON" or "CHANNEL"
local function SendMessageOut(msg, target)
if Hitlist_settings.syncGuild and (target == nil or target == "ADDON") then
ChatThrottleLib:SendAddonMessage("NORMAL", ADDONPREFIX, msg, "GUILD");
end
if Hitlist_settings.syncChannel and (target == nil or target == "CHANNEL") then
if _addon.syncChannelId == nil then
_addon:PrintDebug("Tried to send via custom channel but we aren't in one!");
return;
end
msg = ADDONPREFIX .. msg;
ChatThrottleLib:SendChatMessage("NORMAL", ADDONPREFIX, msg, "CHANNEL", nil, _addon.syncChannelId);
end
end
--- Send add to list (OP_ADD_TARGET)
-- Makes others add the entry to their list.
-- @param name The name of the target
-- @param reason The reason for adding
-- @param timeAdded Time entry was added locally (it was just now when this happens lol, but whatever)
function _addon:SyncAddedEntry(name, reason, timeAdded)
_addon:PrintDebug("Sending OP_ADD_TARGET");
SendMessageOut(OP_ADD_TARGET .. PaddedLen(name:len()) .. PaddedLen(reason:len()) .. name .. reason .. timeAdded);
end
--- Send remove target (OP_REMOVE_TARGET)
-- Makes others remove the entry from their list too, if they have it from this character!
-- @param name The name of the target that was removed
-- TODO: Maybe need to find a way to ID by account and not char.
function _addon:SyncRemovedEntry(name)
_addon:PrintDebug("Sending OP_REMOVE_TARGET");
SendMessageOut(OP_REMOVE_TARGET .. PaddedLen(name:len()) .. name);
end
--- Send update request (OP_REQUEST_UPDATE)
-- Request a full list update.
-- @param target The channel ("ADDON" or "CHANNEL") to request it in, both if nil
function _addon:SyncRequestFullList(target)
_addon:PrintDebug("Sending OP_REQUEST_UPDATE");
SendMessageOut(OP_REQUEST_UPDATE, target);
end
--- Send full update (OP_MULTIPART_UPDATE)
-- Sending may be delayed if update was already sent recently for the target channel.
-- @param target The channel ("ADDON" or "CHANNEL") to send it in
function _addon:SyncSendFullList(target)
_addon:PrintDebug("Sending OP_MULTIPART_UPDATE");
if fullUpdateQueued[target] > 0 then
_addon:PrintDebug("MPU send already queued");
return
end
local curTime = GetTime()
if lastFullUpdateSent[target] + FULL_UPDATE_WAIT > curTime then
local wait = FULL_UPDATE_WAIT - (curTime - lastFullUpdateSent[target]);
_addon:PrintDebug("last MPU was recent, queue to send in " .. wait .. " sec");
fullUpdateQueued[target] = curTime + wait;
frame:SetScript("OnUpdate", ProcessUpdateQueue);
lastFullUpdateSent[target] = 0;
return;
end
local msg = "";
for name, v in pairs(Hitlist_data[realmName].entries) do
if Hitlist_data[realmName].localNames[v.by] then
msg = msg .. PaddedLen(name:len()) .. PaddedLen(v.reason:len()) .. name .. v.reason .. v.added;
end
end
if msg == "" then
_addon:PrintDebug("Nothing to send!");
return;
end
local splitcount, msgsplits = SplitString(msg, 247);
_addon:PrintDebug("Sending update in " .. splitcount .. " parts");
for i=1, splitcount, 1 do
SendMessageOut(OP_MULTIPART_UPDATE .. PaddedLen(i) .. PaddedLen(splitcount) .. msgsplits[i], target);
end
lastFullUpdateSent[target] = curTime;
end