vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 gGroupCalendar_MessagePrefix0 = "GC2";
2 gGroupCalendar_MessagePrefix = gGroupCalendar_MessagePrefix0.."/";
3  
4 gGroupCalendar_MessagePrefixLength = string.len(gGroupCalendar_MessagePrefix);
5  
6 gGroupCalendar_Channel =
7 {
8 Name = nil,
9 NameLower = nil,
10 ID = nil,
11 Password = nil,
12 AutoPlayer = nil,
13 Status = "Disconnected",
14 StatusMessage = nil,
15 GotTooManyChannelsMessage = false,
16 };
17  
18 gGroupCalendar_Queue =
19 {
20 TasksDelay = 0,
21 TasksElapsed = 0,
22 Tasks = {},
23  
24 RequestsDelay = 0,
25 RequestsElapsed = 0,
26 Requests = {[1] = {}, [2] = {}},
27  
28 InboundDelay = 0,
29 InboundMessages = {},
30 InboundLastSender = nil,
31 InboundTimeSinceLastMessage = 0,
32  
33 OutboundDelay = 0,
34 OutboundMessages = {},
35 OutboundTimeSinceLastMessage = 0,
36  
37 DatabaseUpdates = {},
38 RSVPUpdates = {},
39 };
40  
41 gCalendarNetwork_UserTrustCache =
42 {
43 };
44  
45 gCalendarNetwork_RequestDelay =
46 {
47 Init = 120,
48 ShortInit = 5,
49 SelfUpdateRequest = 1,
50 ExternalUpdateRequest = 30,
51 OwnedNotices = 300, -- Allow time for updates to arrive before we'll start advertising databases
52 JoinChannel2 = 2,
53 OwnedUpdate = 3,
54 ProxyUpdateMin = 6,
55 ProxyUpdateRange = 4,
56  
57 RFUMin = 1,
58 RFURange = 5,
59  
60 OwnedNOURange = 2,
61 ProxyNOUMin = 6,
62 ProxyNOURange = 4,
63  
64 InboundQueue = 0.2,
65 OutboundQueue = 0.2,
66 RequestQueue = 0.2,
67  
68 -- Outbound queue delay 1 - 5 seconds
69  
70 OutboundQueueGapMin = 1, -- Delay for after last inbound message was processed
71 OutboundQueueGapWidth = 4, -- Random delay after the min time
72  
73 -- Request queue delay 1 - 5 seconds
74  
75 RequestQueueGapMin = 1, -- Delay for after last inbound or outbound message was processed
76 RequestQueueGapWidth = 4, -- Random delay after the min time
77  
78 GuildUpdateAutoConfig = 2,
79  
80 CheckDatabaseTrust = 10,
81 GuildRosterUpdate = 1,
82  
83 -- Maximum age for an update is two minutes
84  
85 MaximumUpdateAge = 120,
86 };
87  
88 gGroupCalendar_EnableUpdates = false; -- Don't allow the user to send updates to any databases
89 -- until we're sure that his databases are up-to-date themselves
90 gGroupCalendar_EnableSelfUpdates = false; -- Don't allow the user to send updates for their own databases
91 -- until well after they're sure nobody has updates for them
92  
93 function CalendarNetwork_GetChannelStatus()
94 return gGroupCalendar_Channel.Status;
95 end
96  
97 function CalendarNetwork_SetChannel(pChannel, pPassword)
98 -- Leave the channel and return if the new channel is nil
99  
100 if not pChannel then
101 CalendarNetwork_LeaveChannel();
102 return true;
103 end
104  
105 -- Just return if nothing is actually changing
106  
107 if gGroupCalendar_Channel.Name
108 and strupper(gGroupCalendar_Channel.Name) == strupper(pChannel)
109 and gGroupCalendar_Channel.Password == pPassword
110 and gGroupCalendar_Channel.Status == "Connected" then
111 return false; -- return false to indicate that no change was made
112 end
113  
114 -- Leave the old channel and join the new one
115  
116 CalendarNetwork_LeaveChannel();
117  
118 return CalendarNetwork_JoinChannel(pChannel, pPassword);
119 end
120  
121 function CalendarNetwork_JoinChannel(pChannelName, pPassword)
122 if not pChannelName
123 or pChannelName == ""
124 or gGroupCalendar_Channel.Disconnected then
125 return false;
126 end
127  
128 -- There seems to be some sort of bug with passworded channels getting
129 -- screwed up if the UI is reloaded and the solution appears to be to
130 -- leave and re-join the channel. Therefore, the code below...
131  
132 LeaveChannelByName(pChannelName);
133 CalendarNetwork_LeftChannel();
134  
135 CalendarNetwork_SetChannelStatus("Initializing");
136  
137 CalendarNetwork_QueueTask(
138 CalendarNetwork_JoinChannel2,
139 {mChannelName = pChannelName, mPassword = pPassword},
140 gCalendarNetwork_RequestDelay.JoinChannel2,
141 "JOINCHANNEL2");
142  
143 return true;
144 end
145  
146 function CalendarNetwork_JoinChannelFailed()
147 if gGroupCalendar_Channel.GotTooManyChannelsMessage then
148 CalendarNetwork_SetChannelStatus("Error", GroupCalendar_cTooManyChannels);
149 else
150 CalendarNetwork_SetChannelStatus("Error", GroupCalendar_cJoinChannelFailed);
151 end
152 end
153  
154 function CalendarNetwork_LeftChannel()
155 gGroupCalendar_Channel.Name = nil;
156 gGroupCalendar_Channel.Password = nil;
157 gGroupCalendar_Channel.NameLower = nil;
158 gGroupCalendar_Channel.ID = nil;
159  
160 CalendarNetwork_SetChannelStatus("Disconnected");
161 end
162  
163 function CalendarNetwork_SetChannelStatus(pStatus, pStatusMessage)
164 gGroupCalendar_Channel.Status = pStatus;
165 gGroupCalendar_Channel.StatusMessage = pStatusMessage;
166  
167 GroupCalendar_ChannelChanged();
168 end
169  
170 function CalendarNetwork_SystemMessage(pMessage)
171 if pMessage == ERR_TOO_MANY_CHAT_CHANNELS then
172 gGroupCalendar_Channel.GotTooManyChannelsMessage = true;
173  
174 if gGroupCalendar_Channel.Status == "Error" then
175 CalendarNetwork_SetChannelStatus("Error", pMessage);
176 end
177 elseif pMessage == INSTANCE_SAVED then
178 -- Fetch the raid info so the reset events can be logged on
179 -- the calendar
180  
181 RequestRaidInfo();
182 elseif pMessage == NO_RAID_INSTANCES_SAVED then
183 EventDatabase_RemoveSavedInstanceEvents(gGroupCalendar_UserDatabase);
184 elseif pMessage == RAID_INSTANCE_INFO_HDR then
185 -- Remove existing events since they will be replaced by new incoming info
186  
187 EventDatabase_RemoveSavedInstanceEvents(gGroupCalendar_UserDatabase);
188 else
189 local vStartIndex, vEndIndex, vName, vID, vDays, vHours, vMinutes, vSeconds = string.find(pMessage, "(.+) %(ID=(%x+)%): (%d+)d (%d+)h (%d+)m (%d+)s");
190  
191 if vStartIndex then
192 -- Calculate the number of seconds until the instance resets
193  
194 local vRemainingDateTime60 = tonumber(vDays) * gCalendarSecondsPerDay
195 + Calendar_ConvertHMSToTime60(tonumber(vHours), tonumber(vMinutes), tonumber(vSeconds));
196  
197 local vServerResetDate, vServerResetTime = Calendar_GetServerDateTimeFromSecondsOffset(vRemainingDateTime60);
198  
199 EventDatabase_ScheduleSavedInstanceEvent(gGroupCalendar_UserDatabase, vName, vServerResetDate, vServerResetTime);
200 end
201 end
202 end
203  
204 function CalendarNetwork_JoinChannel2(pParams)
205 if gGroupCalendar_Settings.DebugInit then
206 Calendar_DebugMessage("CalendarNetwork_JoinChannel: "..pParams.mChannelName);
207 end
208  
209 gGroupCalendar_Channel.GotTooManyChannelsMessage = false;
210 gGroupCalendar_Channel.Name = pParams.mChannelName;
211  
212 local vChannelName = GetChannelName(pParams.mChannelName);
213 local vZoneChannel;
214 local vChannelAlreadyExists = false;
215  
216 if vChannelName and vChannelName ~= 0 then
217 if gGroupCalendar_Settings.DebugInit then
218 Calendar_DebugMessage("Found existing channel "..pParams.mChannelName.." ("..vChannelName..")");
219 end
220  
221 vChannelName = pParams.mChannelName;
222 vChannelAlreadyExists = true;
223 else
224 if gGroupCalendar_Settings.DebugInit then
225 Calendar_DebugMessage("Channel "..pParams.mChannelName.." not found ("..vChannelName..") joining channel...");
226 end
227  
228 vZoneChannel, vChannelName = JoinChannelByName(pParams.mChannelName, pParams.mPassword, DEFAULT_CHAT_FRAME:GetID());
229  
230 if not vZoneChannel then
231 CalendarNetwork_JoinChannelFailed();
232 return false;
233 end
234  
235 if not vChannelName then
236 vChannelName = pParams.mChannelName;
237 end
238 end
239  
240 -- Remove the channel from the chat frame so the user doesn't
241 -- have to watch all that data comm. (if we're running debug
242 -- code then leave the setting alone)
243  
244 ChatFrame_RemoveChannel(DEFAULT_CHAT_FRAME, pParams.mChannelName);
245  
246 if gCalendar_DebugFrame then
247 ChatFrame_AddChannel(gCalendar_DebugFrame, pParams.mChannelName);
248 end
249  
250 gGroupCalendar_Channel.Password = pParams.mPassword;
251 gGroupCalendar_Channel.NameLower = strlower(pParams.mChannelName);
252 gGroupCalendar_Channel.ID = GetChannelName(gGroupCalendar_Channel.Name);
253  
254 if not gGroupCalendar_Channel.ID
255 or gGroupCalendar_Channel.ID == 0 then
256 gGroupCalendar_Channel.ID = nil;
257  
258 CalendarNetwork_JoinChannelFailed();
259  
260 return false;
261 end
262  
263 if vChannelAlreadyExists then
264 CalendarNetwork_JoinedChannel();
265 end
266  
267 return true;
268 end
269  
270 function CalendarNetwork_LeaveChannel()
271 if gGroupCalendar_Channel.Name
272 and gGroupCalendar_Channel.ID
273 and gGroupCalendar_Channel.Status ~= "Suspended" then
274 LeaveChannelByName(gGroupCalendar_Channel.Name);
275 CalendarNetwork_LeftChannel();
276 end
277 end
278  
279 function CalendarNetwork_JoinedChannel()
280 CalendarNetwork_SetChannelStatus("Connected");
281  
282 if gGroupCalendar_Settings.DebugInit then
283 Calendar_DebugMessage("CalendarNetwork_JoinChannel succeeded channel "..gGroupCalendar_Channel.Name.." ID "..gGroupCalendar_Channel.ID);
284 end
285  
286 -- Send update requests/notices
287  
288 CalendarNetwork_QueueTask(CalendarNetwork_SendNotices, nil, gCalendarNetwork_RequestDelay.SelfUpdateRequest, "SELFUPDATE");
289 end
290  
291 function CalendarNetwork_ChannelNotice(pChannelMessage, pChannelName, pChannelID, pActualChannelName)
292 -- Decode the channel name if it's in the nn. <channel name> format
293  
294 local vChannelName = pChannelName;
295  
296 for vFoundNumber, vFoundName in string.gfind(pChannelName, "(%d)%. (.+)") do
297 vChannelName = vFoundName;
298 end
299  
300 -- Just leave if it's not a channel we're interested in
301  
302 local vIsDataChannel = strlower(vChannelName) == gGroupCalendar_Channel.NameLower;
303  
304 --
305  
306 if pChannelMessage == "YOU_JOINED" then
307 -- If it's one of the system channels, then shorten the initialization
308 -- timer if one is present
309  
310 if pChannelID == 1 then
311 CalendarNetwork_SystemChannelJoined();
312 end
313  
314 if not vIsDataChannel then
315 return;
316 end
317  
318 if pChannelID > 0 then
319 CalendarNetwork_JoinedChannel();
320 else
321 -- Joining failed
322  
323 CalendarNetwork_JoinChannelFailed();
324 end
325  
326 elseif pChannelMessage == "YOU_LEFT" then
327 if not vIsDataChannel then
328 return;
329 end
330  
331 if not gGroupCalendar_Channel.ID then
332 return;
333 end
334  
335 CalendarNetwork_LeftChannel();
336  
337 elseif pChannelMessage == "WRONG_PASSWORD" then
338 if not vIsDataChannel then
339 return;
340 end
341  
342 CalendarNetwork_SetChannelStatus("Error", GroupCalendar_cWrongPassword);
343 end
344 end
345  
346 function CalendarNetwork_SuspendChannel()
347 if gGroupCalendar_Channel.Status == "Suspended"
348 or not gGroupCalendar_Channel.Name
349 or not gGroupCalendar_Channel.ID then
350 return;
351 end
352  
353 LeaveChannelByName(gGroupCalendar_Channel.Name);
354  
355 gGroupCalendar_Channel.ID = nil;
356 CalendarNetwork_SetChannelStatus("Suspended");
357 end
358  
359 function CalendarNetwork_ResumeChannel()
360 if not gGroupCalendar_Channel.Status ~= "Suspended" then
361 return;
362 end
363  
364 JoinChannelByName(gGroupCalendar_Channel.Name, gGroupCalendar_Channel.Password, DEFAULT_CHAT_FRAME:GetID());
365 gGroupCalendar_Channel.ID = GetChannelName(gGroupCalendar_Channel.Name);
366  
367 ChatFrame_RemoveChannel(DEFAULT_CHAT_FRAME, gGroupCalendar_Channel.Name);
368  
369 CalendarNetwork_SetChannelStatus("Connected");
370 end
371  
372 function CalendarNetwork_UnpackIndexedList(...)
373 local vList = {};
374  
375 for vIndex = 1, arg.n, 2 do
376 vList[tonumber(arg[vIndex])] = arg[vIndex + 1];
377 end
378  
379 return vList;
380 end
381  
382 function CalendarNetwork_GetChannelList()
383 local vChannelList = CalendarNetwork_UnpackIndexedList(GetChannelList());
384  
385 return vChannelList;
386 end
387  
388 function CalendarNetwork_CalendarLoaded()
389 -- Set up a deferred initialization
390  
391 CalendarNetwork_SetChannelStatus("Starting");
392  
393 -- See if there are system channels yet and use
394 -- the shorter delay if there are
395  
396 local vDelay = gCalendarNetwork_RequestDelay.Init;
397  
398 local vChannelList = CalendarNetwork_GetChannelList();
399  
400 if vChannelList[1] then
401 vDelay = gCalendarNetwork_RequestDelay.ShortInit;
402 end
403  
404 --
405  
406 CalendarNetwork_QueueTask(CalendarNetwork_Initialize, nil, vDelay, "INIT");
407 end
408  
409 function CalendarNetwork_SystemChannelJoined()
410 CalendarNetwork_SetTaskDelay("INIT", gCalendarNetwork_RequestDelay.ShortInit);
411 end
412  
413 function CalendarNetwork_ProcessCommandString(pSender, pTrustLevel, pCommandString, pCurrentTimeStamp)
414 local vCommand = CalendarNetwork_ParseCommandString(pCommandString);
415  
416 if not vCommand then
417 if gGroupCalendar_Settings.Debug then
418 Calendar_DebugMessage("ProcessCommandString: Couldn't parse ["..pSender.."]:"..pCommandString);
419 end
420  
421 return false;
422 end
423  
424 return CalendarNetwork_ProcessCommand(pSender, pTrustLevel, vCommand, pCurrentTimeStamp);
425 end
426  
427 function CalendarNetwork_ProcessCommand(pSender, pTrustLevel, pCommand, pCurrentTimeStamp)
428 -- Age out old updates which haven't been seen in a while
429  
430 local vMinimumUpdateTime = pCurrentTimeStamp - gCalendarNetwork_RequestDelay.MaximumUpdateAge;
431  
432 CalendarNetwork_KillOldUpdates("DB", gGroupCalendar_Queue.DatabaseUpdates, vMinimumUpdateTime);
433 CalendarNetwork_KillOldUpdates("RAT", gGroupCalendar_Queue.RSVPUpdates, vMinimumUpdateTime);
434  
435 --
436  
437 local vOpcode = pCommand[1].opcode;
438 local vOperands = pCommand[1].operands;
439  
440 table.remove(pCommand, 1);
441  
442 if vOpcode == "DB" then
443 local vUserName = vOperands[1];
444 local vDatabaseID = tonumber(vOperands[2]);
445 local vRevision = tonumber(vOperands[3]);
446 local vAuthRevision = tonumber(vOperands[4]);
447  
448 -- If the sender is using wildcards, fetch the path from
449 -- the update records
450  
451 if vUserName == "*" then
452 local vDatabaseUpdate = CalendarNetwork_FindDatabaseUpdate(pSender);
453  
454 if not vDatabaseUpdate then
455 return;
456 end
457  
458 vUserName = vDatabaseUpdate.mUserName;
459 vDatabaseID = vDatabaseUpdate.mDatabaseID;
460 vRevision = vDatabaseUpdate.mRevision;
461 vAuthRevision = nil;
462 end
463  
464 --
465  
466 if not vRevision then
467 vRevision = 0;
468 end
469  
470 if CalendarTrust_UserIsTrusted(vUserName) then -- only accept databases for trusted users (or our own)
471 CalendarNetwork_ProcessDatabaseCommand(pSender, pTrustLevel, vUserName, vDatabaseID, vRevision, vAuthRevision, pCommand);
472 else
473 if gGroupCalendar_Settings.DebugTrust then
474 Calendar_DebugMessage("ChannelMessageReceived: User "..vUserName.." is not trusted for DB command");
475 end
476 end
477 elseif vOpcode == "RAT" then
478 local vUserName = vOperands[1];
479 local vDatabaseID = tonumber(vOperands[2]);
480 local vRevision = tonumber(vOperands[3]);
481 local vAuthRevision = tonumber(vOperands[4]);
482  
483 -- If the sender is using wildcards, fetch the path from
484 -- the update records
485  
486 if vUserName == "*" then
487 local vRSVPUpdate = CalendarNetwork_FindRSVPUpdate(pSender);
488  
489 if not vRSVPUpdate then
490 return;
491 end
492  
493 vUserName = vRSVPUpdate.mUserName;
494 vDatabaseID = vRSVPUpdate.mDatabaseID;
495 vRevision = vRSVPUpdate.mRevision;
496 vAuthRevision = nil;
497 end
498  
499 --
500  
501 if not vRevision then
502 vRevision = 0;
503 end
504  
505 if CalendarTrust_UserIsTrustedForRSVPs(vUserName) then -- only accept RSVP for trusted users (or our own)
506 CalendarNetwork_ProcessRSVPCommand(pSender, vUserName, vDatabaseID, vRevision, vAuthRevision, pCommand);
507 else
508 if gGroupCalendar_Settings.DebugTrust then
509 Calendar_DebugMessage("ChannelMessageReceived: User "..vUserName.." is not trusted for RAT command");
510 end
511 end
512 elseif vOpcode == "GLD" then
513 local vGuildName = vOperands[1];
514 local vMinRank = tonumber(vOperands[2]);
515  
516 CalendarNetwork_ProcessGuildCommand(pSender, pTrustLevel, vGuildName, vMinRank, pCommand);
517 elseif vOpcode == "ALL" then
518 CalendarNetwork_ProcessAllCommand(pSender, pTrustLevel, pCommand);
519 elseif vOpcode == "VER" then
520 local vDatabase = EventDatabase_GetDatabase(pSender, true);
521  
522 if vDatabase then
523 vDatabase.AddonVersion = vOperands[1];
524 end
525 else
526 if gGroupCalendar_Settings.Debug then
527 Calendar_DebugMessage("ProcessCommand: Unknown opcode "..vOpcode);
528 end
529 end
530 end
531  
532 function CalendarNetwork_ProcessChangesRFU(pChanges, pDatabaseTag, pPlayerOwned, pPriority, pUserName, pDatabaseID, pRevision, pAuthRevision)
533 if gGroupCalendar_Settings.DebugUpdates then
534 Calendar_DebugMessage(pDatabaseTag.." changes update requested for: "..pUserName..","..pRevision);
535 end
536  
537 -- Cancel a queued RFU for the same database if it's redundant
538  
539 CalendarNetwork_CancelRedundantRFURequest(pDatabaseTag, pUserName, pDatabaseID, pRevision)
540  
541 -- Just bail out if we don't have the requested database
542  
543 if not pChanges then
544 return;
545 end
546  
547 -- Create the request
548  
549 local vRequest;
550 local vFromRevision;
551 local vForceUpdate = false;
552  
553 if pChanges.ID ~= pDatabaseID then
554 if pPlayerOwned
555 or pChanges.ID > pDatabaseID then
556 vFromRevision = 0;
557 vForceUpdate = true;
558 else
559 if gGroupCalendar_Settings.DebugUpdates then
560 Calendar_DebugMessage("Not sending "..pDatabaseTag.." update: Requested database isn't available for "..pUserName..","..pDatabaseID);
561 end
562  
563 return;
564 end
565 else
566 if pPlayerOwned
567 and pAuthRevision
568 and pAuthRevision < pRevision then
569 vFromRevision = pAuthRevision;
570 else
571 vFromRevision = pRevision;
572 end
573 end
574  
575 vRequest = CalendarNetwork_FindUPDRequest(pDatabaseTag, pUserName);
576  
577 if vRequest then
578 if vFromRevision < vRequest.mRevision then
579 if gGroupCalendar_Settings.DebugUpdates then
580 Calendar_DebugMessage("Changing existing "..pDatabaseTag.." request to revision "..vFromRevision.." for "..pUserName);
581 end
582  
583 vRequest.mRevision = vFromRevision;
584 else
585 if gGroupCalendar_Settings.DebugUpdates then
586 Calendar_DebugMessage("Existing request for "..pDatabaseTag.." "..pUserName..","..pRevision);
587 end
588 end
589  
590 CalendarNetwork_SetRequestPriority(vRequest, pPriority);
591 elseif pChanges.Revision > vFromRevision
592 or vForceUpdate then
593 vRequest = {};
594  
595 vRequest.mOpcode = pDatabaseTag.."_UPD";
596 vRequest.mUserName = pUserName;
597 vRequest.mDatabaseID = pChanges.ID;
598 vRequest.mRevision = vFromRevision;
599  
600 -- Determine a delay
601  
602 local vDelay;
603  
604 if pPlayerOwned then
605 vDelay = gCalendarNetwork_RequestDelay.OwnedUpdate;
606 else
607 vDelay = gCalendarNetwork_RequestDelay.ProxyUpdateMin + math.random() * gCalendarNetwork_RequestDelay.ProxyUpdateRange;
608 end
609  
610 CalendarNetwork_QueueRequest(vRequest, vDelay, pPriority);
611 end
612 end
613  
614 function CalendarNetwork_GetDatabaseChanges(pUserName, pCreate)
615 local vDatabase = EventDatabase_GetDatabase(pUserName, pCreate);
616  
617 if vDatabase then
618 return vDatabase, vDatabase.Changes, vDatabase.IsPlayerOwned;
619 else
620 return nil, nil, nil;
621 end
622 end
623  
624 function CalendarNetwork_GetDatabaseRSVPChanges(pUserName, pCreate)
625 local vDatabase = EventDatabase_GetDatabase(pUserName, pCreate);
626  
627 if vDatabase then
628 return vDatabase, vDatabase.RSVPs, vDatabase.IsPlayerOwned;
629 else
630 return nil, nil, nil;
631 end
632 end
633  
634 function CalendarNetwork_ProcessDatabaseCommand(pSender, pTrustLevel, pUserName, pDatabaseID, pRevision, pAuthRevision, pCommand)
635 local vOpcode = pCommand[1].opcode;
636 local vOperands = pCommand[1].operands;
637  
638 table.remove(pCommand, 1);
639  
640 local vDatabase, vChanges, vIsPlayerOwned = CalendarNetwork_GetDatabaseChanges(pUserName, false);
641 local vIsThisPlayerOwned = vIsPlayerOwned and pUserName == gGroupCalendar_PlayerName;
642  
643 if vOpcode ~= "RFU"
644 and vOpcode ~= "RFV"
645 and pTrustLevel < 2 then
646 -- Players with moderate trust levels (ie, fellow guild members) are
647 -- only allowed to requeust updates, not provide updates
648  
649 if gGroupCalendar_Settings.DebugTrust then
650 Calendar_DebugMessage("CalendarNetwork_ProcessDatabaseCommand: Ignoring "..vOpcode.." request from "..pSender);
651 end
652  
653 return;
654 end
655  
656 if vOpcode == "RFU" then
657 -- If the sender is seen transmitting an RFU while he has a database update
658 -- pending then it probably means he d/c'd. Kill the database update in that
659 -- case
660  
661 CalendarNetwork_KillSendersDatabaseUpdates(pSender);
662  
663 --
664  
665 if vChanges then
666 -- Give the owner high-priority to help ensure that he can roam successfully
667  
668 local vPriority;
669  
670 if pSender == pUserName then
671 vPriority = 1;
672 else
673 vPriority = 2;
674 end
675  
676 CalendarNetwork_ProcessChangesRFU(vChanges, "DB", vIsThisPlayerOwned, vPriority, pUserName, pDatabaseID, pRevision, pAuthRevision);
677 end
678 elseif vOpcode == "RFV" then
679 -- If the sender is seen transmitting an RFV while he has a database update
680 -- pending then it probably means he d/c'd. Kill the database update in that
681 -- case
682  
683 CalendarNetwork_KillSendersDatabaseUpdates(pSender);
684  
685 --
686  
687 if vIsThisPlayerOwned then
688 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix.."VER:"..gGroupCalendar_VersionString);
689 end
690 elseif vOpcode == "NOU" then
691 -- If the sender is seen transmitting an NOU while he has a database update
692 -- pending then it probably means he d/c'd. Kill the database update in that
693 -- case
694  
695 CalendarNetwork_KillSendersDatabaseUpdates(pSender);
696  
697 --
698  
699 local vCurrentRevision = 0;
700 local vDatabaseIDChanged = false;
701  
702 if not vIsThisPlayerOwned then
703 CalendarNetwork_CancelRedundantNOURequests(vChanges, "DB", pSender, pUserName, pDatabaseID, pRevision);
704 end
705  
706 if not vDatabase then
707 vDatabase = EventDatabase_AssumeDatabase(pUserName);
708 end
709  
710 if vDatabase then
711 -- Ignore external updates to our own databases
712  
713 if vDatabase.IsPlayerOwned then
714 -- If someone sent out a NOU for one of our databases and it's older than
715 -- ours and ours is now empty, send out a DEL to let them know that it's newer
716 -- and empty now
717  
718 if CalendarChanges_IsEmpty(vChanges) then
719 CalendarNetwork_SendEmptyChanges(vChanges, "DB", pUserName);
720 end
721  
722 return;
723 end
724  
725 -- Ignore the update if the ID is older than the one we have
726 -- unless it's from the owner
727  
728 if vChanges
729 and pDatabaseID < vChanges.ID
730 and pSender ~= vDatabase.UserName then
731 return;
732 end
733  
734 -- Force a RFU if the ID is changing
735  
736 if not vChanges
737 or vChanges.ID ~= pDatabaseID then
738 vDatabaseIDChanged = true;
739 else
740 vCurrentRevision = vChanges.Revision;
741  
742 if not vCurrentRevision then
743 Calendar_ErrorMessage("Changes for "..pUserName.." has nil revision");
744 return;
745 end
746 end
747 end
748  
749 if vDatabaseIDChanged
750 or vCurrentRevision < pRevision then
751 CalendarNetwork_QueueRFURequest(pUserName, "DB", pDatabaseID, pRevision);
752 end
753 elseif vOpcode == "UPD" then
754 -- If the sender is seen transmitting an UPD while he has another database update
755 -- pending then it probably means he d/c'd. Kill the database update in that
756 -- case
757  
758 CalendarNetwork_KillSendersDatabaseUpdates(pSender, pUserName);
759  
760 -- Begin a database update
761  
762 local vSinceRevision = tonumber(vOperands[1]);
763  
764 if not vSinceRevision then
765 if gGroupCalendar_Settings.DebugErrors then
766 Calendar_DebugMessage("GroupCalendar: DB UPD received from "..pSender.." for "..pUserName.." with no SinceRevision");
767 end
768  
769 return;
770 end
771  
772 if not vIsThisPlayerOwned then
773 -- If we're waiting to request this same update, cancel the request
774  
775 CalendarNetwork_CancelRedundantRFURequest("DB", pUserName, pDatabaseID, vSinceRevision);
776  
777 -- If we're waiting to send this same update, cancel the request
778  
779 CalendarNetwork_CancelRedundantUPDRequests(vChanges, "DB", pUserName, pDatabaseID, pRevision, vSinceRevision);
780 end
781  
782 CalendarNetwork_BeginDatabaseUpdate(pSender, pUserName, pDatabaseID, pRevision, vSinceRevision);
783  
784 elseif vOpcode == "DEL" then
785 -- Delete the database since it's empty
786  
787 if not vIsThisPlayerOwned then
788 CalendarNetwork_CancelRedundantUPDRequests(vChanges, "DB", pUserName, pDatabaseID, pRevision, 0);
789 end
790  
791 CalendarNetwork_DeleteDatabase(pSender, pUserName, pDatabaseID, pRevision);
792  
793 elseif vOpcode == "EVT" then
794 -- Event data
795  
796 local vEventID = tonumber(vOperands[1]);
797  
798 CalendarNetwork_InsertEventUpdate(pSender, pUserName, pDatabaseID, pRevision, vEventID, pCommand);
799  
800 elseif vOpcode == "END" then
801 -- End a database update
802  
803 local vSinceRevision = tonumber(vOperands[1]);
804  
805 CalendarNetwork_EndDatabaseUpdate(pSender, pUserName, pDatabaseID, pRevision, vSinceRevision);
806 end
807 end
808  
809 function CalendarNetwork_ProcessRSVPCommand(pSender, pUserName, pDatabaseID, pRevision, pAuthRevision, pCommand)
810 local vOpcode = pCommand[1].opcode;
811 local vOperands = pCommand[1].operands;
812 local vOperandString = pCommand[1].operandString;
813  
814 table.remove(pCommand, 1);
815  
816 local vDatabase, vChanges, vIsPlayerOwned = CalendarNetwork_GetDatabaseRSVPChanges(pUserName, false);
817 local vIsThisPlayerOwned = vIsPlayerOwned and pUserName == gGroupCalendar_PlayerName;
818  
819 if vOpcode == "RFU" then
820 -- If the sender is seen transmitting an RFU while he has a database update
821 -- pending then it probably means he d/c'd. Kill the database update in that
822 -- case
823  
824 CalendarNetwork_KillSendersRSVPUpdates(pSender);
825  
826 --
827  
828 if vChanges then
829 -- Give the owner high-priority to help ensure that he can roam successfully
830  
831 local vPriority;
832  
833 if pSender == pUserName then
834 vPriority = 1;
835 else
836 vPriority = 2;
837 end
838  
839 CalendarNetwork_ProcessChangesRFU(vChanges, "RAT", vIsThisPlayerOwned, vPriority, pUserName, pDatabaseID, pRevision, pAuthRevision);
840 end
841 elseif vOpcode == "NOU" then
842 -- If the sender is seen transmitting a NOU while he has a database update
843 -- pending then it probably means he d/c'd. Kill the database update in that
844 -- case
845  
846 CalendarNetwork_KillSendersRSVPUpdates(pSender);
847  
848 --
849  
850 local vCurrentRevision = 0;
851 local vDatabaseIDChanged = false;
852  
853 if not vIsThisPlayerOwned then
854 CalendarNetwork_CancelRedundantNOURequests(vChanges, "RAT", pSender, pUserName, pDatabaseID, pRevision);
855 end
856  
857 if vDatabase then
858 -- Ignore external updates to our own databases
859  
860 if vDatabase.IsPlayerOwned then
861 -- If someone sent out a NOU for one of our databases and it's older than
862 -- ours and ours is now empty, send out a DEL to let them know that it's newer
863 -- and empty now
864  
865 if CalendarChanges_IsEmpty(vChanges) then
866 CalendarNetwork_SendEmptyChanges(vChanges, "RAT", pUserName);
867 end
868  
869 return;
870 end
871  
872 -- Ignore the update if the ID is older than the one we have
873 -- unless it's from the owner
874  
875 if vChanges
876 and pDatabaseID < vChanges.ID
877 and pSender ~= vDatabase.UserName then
878 return;
879 end
880  
881 -- Purge the existing database if the ID is changing
882  
883 if not vChanges
884 or vChanges.ID ~= pDatabaseID then
885 vDatabaseIDChanged = true;
886 else
887 vCurrentRevision = vChanges.Revision;
888 end
889 end
890  
891 if vDatabaseIDChanged
892 or vCurrentRevision < pRevision then
893 CalendarNetwork_QueueRFURequest(pUserName, "RAT", pDatabaseID, pRevision);
894 end
895 elseif vOpcode == "UPD" then
896 -- If the sender is seen transmitting an UPD while he has a database update
897 -- pending then it probably means he d/c'd. Kill the database update in that
898 -- case
899  
900 CalendarNetwork_KillSendersRSVPUpdates(pSender, pUserName);
901  
902 --
903  
904 -- Begin a RSVP database update
905  
906 local vSinceRevision = tonumber(vOperands[1]);
907  
908 if not vSinceRevision then
909 if gGroupCalendar_Settings.DebugErrors then
910 Calendar_DebugMessage("GroupCalendar: RAT UPD received from "..pSender.." for "..pUserName.." with no SinceRevision");
911 end
912  
913 return;
914 end
915  
916 if not vIsThisPlayerOwned then
917 -- If we're waiting to ask for an update to this database, cancel the request
918  
919 CalendarNetwork_CancelRedundantRFURequest("RAT", pUserName, pDatabaseID, vSinceRevision);
920  
921 -- If we're waiting to send this same update, cancel the request
922  
923 CalendarNetwork_CancelRedundantUPDRequests(vChanges, "RAT", pUserName, pDatabaseID, pRevision, vSinceRevision);
924 end
925  
926 CalendarNetwork_BeginRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, vSinceRevision);
927  
928 elseif vOpcode == "DEL" then
929 -- Delete the database since it's empty
930  
931 if not vIsThisPlayerOwned then
932 CalendarNetwork_CancelRedundantUPDRequests(vChanges, "RAT", pUserName, pDatabaseID, pRevision, 0);
933 end
934  
935 CalendarNetwork_DeleteRSVPs(pSender, pUserName, pDatabaseID, pRevision);
936  
937 elseif vOpcode == "EVT" then
938 -- Event data
939  
940 CalendarNetwork_InsertRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, vOperandString);
941  
942 elseif vOpcode == "END" then
943 -- End a RSVP update
944  
945 local vSinceRevision = tonumber(vOperands[1]);
946  
947 CalendarNetwork_EndRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, vSinceRevision);
948 end
949 end
950  
951 function CalendarNetwork_ProcessGuildCommand(pSender, pTrustLevel, pGuildName, pMinRank, pCommand)
952 -- Ignore guild commands if they're not directed at the player's guild
953 -- or not at the player's rank
954  
955 if pGuildName ~= gGroupCalendar_PlayerGuild
956 or gGroupCalendar_PlayerGuildRank > pMinRank then
957 return;
958 end
959  
960 -- Pass it on to the ALL handler for further processing
961  
962 CalendarNetwork_ProcessAllCommand(pSender, pTrustLevel, pCommand);
963 end
964  
965 function CalendarNetwork_ProcessAllCommand(pSender, pTrustLevel, pCommand)
966 local vOpcode = pCommand[1].opcode;
967 local vOperands = pCommand[1].operands;
968  
969 table.remove(pCommand, 1);
970  
971 if vOpcode == "RFU" then
972 CalendarNetwork_SendAllRevisionNotices();
973 elseif vOpcode == "RFV" then
974 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix.."VER:"..gGroupCalendar_VersionString);
975 end
976 end
977  
978 function CalendarNetwork_DatabaseIsNewer(pDatabaseID1, pRevision1, pDatabaseID2, pRevision2)
979 if pDatabaseID2 > pDatabaseID1 then
980 return true;
981 elseif pDatabaseID2 < pDatabaseID1 then
982 return false;
983 else
984 return pRevision2 > pRevision1;
985 end
986 end
987  
988 function CalendarNetwork_UpdateIsBetterThanOurs(pSender, pUserName, pDatabaseID, pRevision, pDatabase, pChanges)
989 -- Updates for our own databases are always worse if
990 -- we're on the toon
991  
992 if pDatabase.IsPlayerOwned and pUserName == gGroupCalendar_PlayerName then
993 return false;
994 end
995  
996 -- Updates from the owner are always better than ours
997 -- as are updates when our list is empty
998  
999 if string.lower(pSender) == string.lower(pUserName)
1000 or not pChanges then
1001 return true;
1002 end
1003  
1004 -- If the revision is higher than what we have it's better
1005  
1006 return CalendarNetwork_DatabaseIsNewer(pChanges.ID, pChanges.Revision, pDatabaseID, pRevision);
1007 end
1008  
1009 function CalendarNetwork_DeleteDatabase(pSender, pUserName, pDatabaseID, pRevision)
1010 -- Get the database
1011  
1012 local vDatabase = EventDatabase_GetDatabase(pUserName, false);
1013  
1014 -- Nothing to do if we don't even have the database
1015  
1016 if not vDatabase then
1017 return;
1018 end
1019  
1020 -- Bail out if our stuff is better
1021  
1022 if not CalendarNetwork_UpdateIsBetterThanOurs(pSender, pUserName, pDatabaseID, pRevision, vDatabase, vDatabase.Changes) then
1023 return;
1024 end
1025  
1026 -- Empty the specified changelist
1027  
1028 EventDatabase_PurgeDatabase(vDatabase, pDatabaseID);
1029 vDatabase.Changes.Revision = pRevision;
1030 end
1031  
1032 function CalendarNetwork_BeginDatabaseUpdate(pSender, pUserName, pDatabaseID, pRevision, pSinceRevision)
1033 -- If the same sender has a pending update already in progress, kill it
1034  
1035 CalendarNetwork_KillSendersDatabaseUpdates(pSender, pUserName);
1036  
1037 -- Get the database
1038  
1039 local vDatabase = EventDatabase_GetDatabase(pUserName, false);
1040  
1041 -- Ignore updates for our own databases
1042  
1043 if vDatabase
1044 and vDatabase.IsPlayerOwned
1045 and not gGroupCalendar_Settings.AllowSelfUpdate then
1046 return;
1047 end
1048  
1049 local vChanges;
1050  
1051 if vDatabase then
1052 vChanges = vDatabase.Changes;
1053 else
1054 vChanges = nil;
1055 end
1056  
1057 -- Ignore the update if it isn't from the owner and
1058 -- our database ID is newer
1059  
1060 if string.lower(pSender) ~= string.lower(pUserName)
1061 and vChanges then
1062 if (vChanges.ID > pDatabaseID)
1063 or (vChanges.ID == pDatabaseID
1064 and vChanges.Revision >= pRevision) then
1065 return;
1066 end
1067 end
1068  
1069 -- If it's from the owner and isn't the same revision as ours,
1070 -- then delete the database. If the update is from revision
1071 -- zero go ahead with the update otherwise ignore it and request
1072 -- and update from zero
1073  
1074 if string.lower(pSender) == string.lower(pUserName)
1075 and vChanges
1076 and vChanges.ID ~= pDatabaseID then
1077 EventDatabase_PurgeDatabase(vDatabase, pDatabaseID);
1078  
1079 if pSinceRevision ~= 0 then
1080 -- Request an update and exit
1081  
1082 CalendarNetwork_QueueRFURequest(pUserName, "DB", pDatabaseID, 0)
1083 return;
1084 end
1085 end
1086  
1087 -- See if there's another update for the same database already in progress
1088  
1089 local vDatabaseUpdate = CalendarNetwork_FindDatabaseUpdate(nil, pUserName, nil);
1090  
1091 if vDatabaseUpdate then
1092 -- If the new update is from the owner or
1093 -- the old one isn't from the owner and is a higher revision,
1094 -- then cancel the old update
1095  
1096 if string.lower(pSender) == string.lower(pUserName)
1097 or (vDatabaseUpdate.mSender ~= vDatabaseUpdate.mUserName
1098 and CalendarNetwork_DatabaseIsNewer(vDatabaseUpdate.mDatabaseID, vDatabaseUpdate.mRevision, pDatabaseID, pRevision)) then
1099 CalendarNetwork_CancelDatabaseUpdate(nil, pUserName, nil);
1100  
1101 -- Otherwise cancel this one
1102  
1103 else
1104 return;
1105 end
1106 end
1107  
1108 if gGroupCalendar_Settings.DebugUpdates then
1109 Calendar_DebugMessage("CalendarNetwork_BeginDatabaseUpdate: "..pUserName..","..pRevision.." since revision "..pSinceRevision.." from "..pSender);
1110 end
1111  
1112 -- Can't accept updates which don't cover the last revision we received
1113  
1114 if vChanges
1115 and pSinceRevision > vChanges.Revision then
1116 -- Ask for another copy of the update starting with the revision we need
1117  
1118 CalendarNetwork_QueueRFURequest(pUserName, "DB", pDatabaseID, vChanges.Revision)
1119 return;
1120 end
1121  
1122 -- Create the database update record
1123  
1124 vDatabaseUpdate = {};
1125  
1126 vDatabaseUpdate.mSender = pSender;
1127 vDatabaseUpdate.mUserName = pUserName;
1128 vDatabaseUpdate.mDatabaseID = pDatabaseID;
1129 vDatabaseUpdate.mRevision = pRevision;
1130 vDatabaseUpdate.mSinceRevision = pSinceRevision;
1131 vDatabaseUpdate.mChanges = {};
1132 vDatabaseUpdate.mLastMessageTime = Calendar_GetCurrentLocalDateTimeStamp();
1133  
1134 table.insert(gGroupCalendar_Queue.DatabaseUpdates, vDatabaseUpdate);
1135 end
1136  
1137 function CalendarNetwork_InsertEventUpdate(pSender, pUserName, pDatabaseID, pRevision, pEventID, pCommand)
1138 local vDatabaseUpdate = CalendarNetwork_FindDatabaseUpdate(pSender, pUserName, pDatabaseID);
1139  
1140 if not vDatabaseUpdate then
1141 return;
1142 end
1143  
1144 -- Bump the update time
1145  
1146 vDatabaseUpdate.mLastMessageTime = Calendar_GetCurrentLocalDateTimeStamp();
1147  
1148 --
1149  
1150 local vChanges = vDatabaseUpdate.mChanges[pRevision];
1151  
1152 if not vChanges then
1153 vChanges = {};
1154 vDatabaseUpdate.mChanges[pRevision] = vChanges;
1155 end
1156  
1157 -- Reconstruct the change string
1158  
1159 local vChangeString = "EVT:"..pEventID;
1160  
1161 for vIndex, vCommand in pCommand do
1162 vChangeString = vChangeString.."/"..vCommand.opcode;
1163  
1164 if vCommand.operandString and vCommand.operandString ~= "" then
1165 vChangeString = vChangeString..":"..vCommand.operandString;
1166 end
1167 end
1168  
1169 -- Save the string
1170  
1171 table.insert(vChanges, vChangeString);
1172 end
1173  
1174 function CalendarNetwork_EndDatabaseUpdate(pSender, pUserName, pDatabaseID, pRevision, pSinceRevision)
1175 local vDatabaseUpdate = CalendarNetwork_FindDatabaseUpdate(pSender, pUserName, pDatabaseID, true);
1176  
1177 if not vDatabaseUpdate then
1178 return;
1179 end
1180  
1181 -- Sanity check: make sure the sinceRevision field matches the original UPD message
1182  
1183 if vDatabaseUpdate.mSinceRevision ~= pSinceRevision then
1184 return;
1185 end
1186  
1187 -- The update was received successfully.
1188 -- Copy the changes to the change list
1189  
1190 if gGroupCalendar_Settings.DebugUpdates then
1191 Calendar_DebugMessage("CalendarNetwork_EndDatabaseUpdate: Process update for "..pUserName..","..pDatabaseID..","..pRevision.." since revision "..pSinceRevision.." from "..pSender);
1192 end
1193  
1194 local vDatabase = EventDatabase_GetDatabase(pUserName, true);
1195 local vDatabaseChanges = vDatabase.Changes;
1196 local vReconstructDatabase = false;
1197  
1198 -- If the update is for one of our own databases process it
1199 -- separately
1200  
1201 if vDatabase.IsPlayerOwned then
1202 CalendarNetwork_AskProcessSelfUpdate(vDatabaseUpdate);
1203 return;
1204 end
1205  
1206 -- Process the update
1207  
1208 CalendarNetwork_ProcessDatabaseUpdate(vDatabaseUpdate, false);
1209 end
1210  
1211 function CalendarNetwork_ProcessDatabaseUpdate(pDatabaseUpdate, pForceReconstruct)
1212 local vIsOwnerUpdate = pDatabaseUpdate.mSender == pDatabaseUpdate.mUserName;
1213 local vDatabase = EventDatabase_GetDatabase(pDatabaseUpdate.mUserName, true);
1214 local vDatabaseChanges = vDatabase.Changes;
1215 local vReconstructDatabase = pForceReconstruct;
1216  
1217 if not vDatabaseChanges
1218 or vDatabaseChanges.ID ~= pDatabaseUpdate.mDatabaseID then
1219 vDatabase.Changes = CalendarChanges_New(pDatabaseUpdate.mDatabaseID);
1220 vDatabaseChanges = vDatabase.Changes;
1221 end
1222  
1223 for vRevision = pDatabaseUpdate.mSinceRevision + 1, pDatabaseUpdate.mRevision do
1224 local vChanges = pDatabaseUpdate.mChanges[vRevision];
1225  
1226 -- If the revision is newer than what we have, insert the new data
1227  
1228 if vRevision > vDatabaseChanges.Revision then
1229 CalendarChanges_SetChangeList(vDatabaseChanges, vRevision, vChanges);
1230  
1231 if vChanges then
1232 EventDatabase_ExecuteChangeList(vDatabase, vChanges, true);
1233 end
1234  
1235 -- If the revision overlaps what we have and the update is from
1236 -- the owner, then compare the data to make sure it's intact. If
1237 -- it doesn't match then update the changes and flag the database
1238 -- for reconstruction
1239  
1240 elseif vIsOwnerUpdate then
1241 -- If we're not reconstructing then compare the owner's changes to what
1242 -- we've gotten before and see if they match. Switch to reconstruction
1243 -- mode if there's a discrepancy
1244  
1245 if not vReconstructDatabase then
1246 local vChangeList = CalendarChanges_GetChangeList(vDatabaseChanges, vRevision);
1247  
1248 if ((vChanges ~= nil) ~= (vChangeList ~= nil))
1249 or (vChangeList ~= nil and table.getn(vChangeList) ~= table.getn(vChanges)) then
1250 vReconstructDatabase = true;
1251  
1252 if gGroupCalendar_Settings.DebugReconstruct then
1253 Calendar_DebugMessage("Reconstructing "..vDatabase.UserName.." because changes for revision "..vRevision.." are different lengths");
1254 end
1255 elseif vChanges ~= nil then
1256 for vChangeIndex, vChange in vChanges do
1257 local vOldChange = vChangeList[vChangeIndex];
1258  
1259 if vOldChange ~= vChange then
1260 vReconstructDatabase = true;
1261  
1262 if gGroupCalendar_Settings.DebugReconstruct then
1263 Calendar_DebugMessage("Reconstructing "..vDatabase.UserName.." because change "..vChangeIndex.." for revision "..vRevision.." doesn't match");
1264 Calendar_DebugMessage("Previously: "..vOldChange);
1265 Calendar_DebugMessage("Now: "..vChange);
1266 end
1267 break;
1268 end
1269 end
1270 end
1271 end
1272  
1273 -- Just copy the changes over if we're in re-construction mode
1274  
1275 if vReconstructDatabase then
1276 CalendarChanges_SetChangeList(vDatabaseChanges, vRevision, vChanges);
1277 end
1278 end
1279 end
1280  
1281 -- Make sure the current revision stamp matches the update
1282  
1283 vDatabaseChanges.Revision = pDatabaseUpdate.mRevision;
1284  
1285 -- Update AuthRevision if the update came from the owner
1286  
1287 if vIsOwnerUpdate
1288 and pDatabaseUpdate.mSinceRevision <= vDatabaseChanges.AuthRevision then
1289 vDatabaseChanges.AuthRevision = pDatabaseUpdate.mRevision;
1290 end
1291  
1292 if vReconstructDatabase then
1293 EventDatabase_ReconstructDatabase(vDatabase);
1294 else
1295 GroupCalendar_MajorDatabaseChange(vDatabase);
1296 end
1297 end
1298  
1299 StaticPopupDialogs["CONFIRM_CALENDAR_SELF_UPDATE"] = {
1300 text = TEXT(GroupCalendar_cConfirmSelfUpdateMsg),
1301 button1 = TEXT(GroupCalendar_cUpdate),
1302 button2 = TEXT(CANCEL),
1303 OnAccept = function() CalendarNetwork_ProcessSelfUpdate(); end,
1304 OnCancel = function() CalendarNetwork_RejectSelfUpdate(); end,
1305 timeout = 0,
1306 whileDead = 1,
1307 hideOnEscape = 1,
1308 showAlert = 1,
1309 };
1310  
1311 gGroupCalendar_DidAskSelfUpdate = false;
1312 gGroupCalendar_SelfDatabaseUpdate = nil;
1313  
1314 function CalendarNetwork_AskProcessSelfUpdate(pDatabaseUpdate)
1315 -- Only ask once
1316  
1317 if gGroupCalendar_DidAskSelfUpdate then
1318 return;
1319 end
1320  
1321 gGroupCalendar_DidAskSelfUpdate = true;
1322  
1323 gGroupCalendar_SelfDatabaseUpdate = pDatabaseUpdate;
1324  
1325 -- Format the message
1326  
1327 local vMessage = Calendar_FormatNamed(GroupCalendar_cConfirmSelfUpdateParamFormat, pDatabaseUpdate);
1328  
1329 -- Show the dialog
1330  
1331 StaticPopup_Show("CONFIRM_CALENDAR_SELF_UPDATE", vMessage);
1332 end
1333  
1334 StaticPopupDialogs["CONFIRM_CALENDAR_SELF_RSVP_UPDATE"] = {
1335 text = TEXT(GroupCalendar_cConfirmSelfUpdateMsg),
1336 button1 = TEXT(GroupCalendar_cUpdate),
1337 button2 = TEXT(CANCEL),
1338 OnAccept = function() CalendarNetwork_ProcessSelfRSVPUpdate(); end,
1339 OnCancel = function() CalendarNetwork_RejectSelfRSVPUpdate(); end,
1340 timeout = 0,
1341 whileDead = 1,
1342 hideOnEscape = 1,
1343 showAlert = 1,
1344 };
1345  
1346 gGroupCalendar_DidAskSelfRSVPUpdate = false;
1347 gGroupCalendar_SelfRSVPUpdate = nil;
1348  
1349 function CalendarNetwork_AskProcessSelfRSVPUpdate(pRSVPUpdate)
1350 -- Only ask once
1351  
1352 if gGroupCalendar_DidAskSelfRSVPUpdate then
1353 return;
1354 end
1355  
1356 gGroupCalendar_DidAskSelfRSVPUpdate = true;
1357 gGroupCalendar_SelfRSVPUpdate = pRSVPUpdate;
1358  
1359 -- Format the message
1360  
1361 local vMessage = Calendar_FormatNamed(GroupCalendar_cConfirmSelfRSVPUpdateParamFormat, pRSVPUpdate);
1362  
1363 -- Show the dialog
1364  
1365 StaticPopup_Show("CONFIRM_CALENDAR_SELF_RSVP_UPDATE", vMessage);
1366 end
1367  
1368 function CalendarNetwork_ProcessSelfUpdate()
1369 CalendarNetwork_ProcessDatabaseUpdate(gGroupCalendar_SelfDatabaseUpdate, true);
1370 end
1371  
1372 function CalendarNetwork_RejectSelfUpdate()
1373 local vDatabase = EventDatabase_GetDatabase(gGroupCalendar_SelfDatabaseUpdate.mUserName, true);
1374  
1375 if not vDatabase then
1376 return;
1377 end
1378  
1379 EventDatabase_RebuildDatabase(vDatabase);
1380 end
1381  
1382 function CalendarNetwork_CancelDatabaseUpdate(pSender, pUserName, pDatabaseID)
1383 CalendarNetwork_FindDatabaseUpdate(pSender, pUserName, pDatabaseID, true);
1384 end
1385  
1386 function CalendarNetwork_FindOpenEventChangeRecord(pChanges, pEventID)
1387 for vIndex, vChange in pChanges do
1388 if vChange.mEventID == pEventID
1389 and vChange.mOpen then
1390 return vChange;
1391 end
1392 end
1393  
1394 return nil;
1395 end
1396  
1397 function CalendarNetwork_KillSendersDatabaseUpdates(pSender)
1398 local vDatabaseUpdate = CalendarNetwork_FindDatabaseUpdate(pSender, nil, nil, true);
1399  
1400 if not vDatabaseUpdate
1401 or vDatabaseUpdate.mUserName == pDontRequestForUserName then
1402 return;
1403 end
1404  
1405 CalendarNetwork_RequestUpdateForUser(vDatabaseUpdate.mUserName, "DB");
1406 end
1407  
1408 function CalendarNetwork_FindDatabaseUpdate(pSender, pUserName, pDatabaseID, pDelete)
1409 for vIndex, vDatabaseUpdate in gGroupCalendar_Queue.DatabaseUpdates do
1410 if (pSender == nil or vDatabaseUpdate.mSender == pSender)
1411 and (pUserName == nil or vDatabaseUpdate.mUserName == pUserName)
1412 and (pDatabaseID == nil or vDatabaseUpdate.mDatabaseID == pDatabaseID) then
1413 -- Delete the update if requested
1414  
1415 if pDelete then
1416 table.remove(gGroupCalendar_Queue.DatabaseUpdates, vIndex);
1417 end
1418  
1419 return vDatabaseUpdate;
1420 end
1421 end
1422  
1423 return nil;
1424 end
1425  
1426 function CalendarNetwork_DeleteRSVPs(pSender, pUserName, pDatabaseID, pRevision)
1427 -- Get the database
1428  
1429 local vDatabase = EventDatabase_GetDatabase(pUserName, false);
1430  
1431 -- Nothing to do if we don't even have the database
1432  
1433 if not vDatabase then
1434 return;
1435 end
1436  
1437 -- Bail out if our stuff is better
1438  
1439 if not CalendarNetwork_UpdateIsBetterThanOurs(pSender, pUserName, pDatabaseID, pRevision, vDatabase, vDatabase.RSVPs) then
1440 return;
1441 end
1442  
1443 -- Empty the specified changelist
1444  
1445 EventDatabase_PurgeRSVPs(vDatabase, pDatabaseID);
1446 vDatabase.RSVPs.Revision = pRevision;
1447 end
1448  
1449 function CalendarNetwork_BeginRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, pSinceRevision)
1450 -- If the same sender has a pending update already in progress, kill it
1451  
1452 CalendarNetwork_KillSendersRSVPUpdates(pSender, pUserName);
1453  
1454 -- Get the database
1455  
1456 local vDatabase = EventDatabase_GetDatabase(pUserName, false);
1457  
1458 -- Don't listen to updates for our own databases
1459  
1460 if vDatabase
1461 and vDatabase.IsPlayerOwned
1462 and not gGroupCalendar_Settings.AllowSelfUpdate then
1463 return;
1464 end
1465  
1466 local vChanges;
1467  
1468 if vDatabase then
1469 vChanges = vDatabase.RSVPs;
1470 else
1471 vChanges = nil;
1472 end
1473  
1474 -- Ignore the update if it isn't from the owner and
1475 -- our ID is newer
1476  
1477 if string.lower(pSender) ~= string.lower(pUserName)
1478 and vChanges then
1479 if (vChanges.ID > pDatabaseID)
1480 or (vChanges.ID == pDatabaseID
1481 and vChanges.Revision >= pRevision) then
1482 return;
1483 end
1484 end
1485  
1486 -- If it's from the owner and isn't the same revision as ours,
1487 -- then delete the database. If the update is from revision
1488 -- zero go ahead with the update otherwise ignore it and request
1489 -- and update from zero
1490  
1491 if string.lower(pSender) == string.lower(pUserName)
1492 and vChanges
1493 and vChanges.ID ~= pDatabaseID then
1494 EventDatabase_PurgeRSVPs(vDatabase, pDatabaseID);
1495  
1496 if pSinceRevision ~= 0 then
1497 -- Request an update and exit
1498  
1499 CalendarNetwork_QueueRFURequest(pUserName, "RAT", pDatabaseID, 0)
1500 return;
1501 end
1502 end
1503  
1504 -- See if there's another update for the same database already in progress
1505  
1506 local vRSVPUpdate = CalendarNetwork_FindRSVPUpdate(nil, pUserName, nil);
1507  
1508 if vRSVPUpdate then
1509 -- If the new update is from the owner or
1510 -- the old one isn't from the owner and is a higher revision,
1511 -- then cancel the old update
1512  
1513 if string.lower(pSender) == string.lower(pUserName)
1514 or (vRSVPUpdate.mSender ~= vRSVPUpdate.mUserName
1515 and CalendarNetwork_DatabaseIsNewer(vRSVPUpdate.mDatabaseID, vRSVPUpdate.mRevision, pDatabaseID, pRevision)) then
1516 CalendarNetwork_CancelRSVPUpdate(nil, pUserName, nil);
1517  
1518 -- Otherwise cancel this one
1519  
1520 else
1521 return;
1522 end
1523 end
1524  
1525 if gGroupCalendar_Settings.DebugUpdates then
1526 Calendar_DebugMessage("CalendarNetwork_BeginRSVPUpdate: "..pUserName..","..pRevision.." since revision "..pSinceRevision.." from "..pSender);
1527 end
1528  
1529 -- Can't accept updates which don't cover the last revision we received
1530  
1531 if vChanges
1532 and pSinceRevision > vChanges.Revision then
1533 -- Ask for another copy of the update starting with the revision we need
1534  
1535 CalendarNetwork_QueueRFURequest(pUserName, "RAT", pDatabaseID, vChanges.Revision);
1536 return;
1537 end
1538  
1539 -- Create the update record
1540  
1541 vRSVPUpdate = {};
1542  
1543 vRSVPUpdate.mSender = pSender;
1544 vRSVPUpdate.mUserName = pUserName;
1545 vRSVPUpdate.mDatabaseID = pDatabaseID;
1546 vRSVPUpdate.mRevision = pRevision;
1547 vRSVPUpdate.mSinceRevision = pSinceRevision;
1548 vRSVPUpdate.mChanges = {};
1549 vRSVPUpdate.mLastMessageTime = Calendar_GetCurrentLocalDateTimeStamp();
1550  
1551 table.insert(gGroupCalendar_Queue.RSVPUpdates, vRSVPUpdate);
1552 end
1553  
1554 function CalendarNetwork_InsertRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, pEventFields)
1555 local vRSVPUpdate = CalendarNetwork_FindRSVPUpdate(pSender, pUserName, pDatabaseID);
1556  
1557 if not vRSVPUpdate then
1558 if gGroupCalendar_Settings.DebugUpdates then
1559 Calendar_DebugMessage("CalendarNetwork_InsertRSVPUpdate: Ignoring update for "..pUserName..","..pDatabaseID..": Update not found");
1560 end
1561 return;
1562 end
1563  
1564 -- Bump the update time
1565  
1566 vRSVPUpdate.mLastMessageTime = Calendar_GetCurrentLocalDateTimeStamp();
1567  
1568 --
1569  
1570 local vChanges = vRSVPUpdate.mChanges[pRevision];
1571  
1572 if not vChanges then
1573 vChanges = {};
1574 vRSVPUpdate.mChanges[pRevision] = vChanges;
1575 end
1576  
1577 -- Process the event command
1578  
1579 local vChangeString = "EVT:"..pEventFields;
1580  
1581 table.insert(vChanges, vChangeString);
1582 end
1583  
1584 function CalendarNetwork_EndRSVPUpdate(pSender, pUserName, pDatabaseID, pRevision, pSinceRevision)
1585 local vRSVPUpdate = CalendarNetwork_FindRSVPUpdate(pSender, pUserName, pDatabaseID, true);
1586  
1587 if not vRSVPUpdate then
1588 return;
1589 end
1590  
1591 -- Sanity check: make sure the sinceRevision field matches the original UPD message
1592  
1593 if vRSVPUpdate.mSinceRevision ~= pSinceRevision then
1594 return;
1595 end
1596  
1597 -- The update was received successfully.
1598 -- Process the commands in the update
1599  
1600 if gGroupCalendar_Settings.DebugUpdates then
1601 Calendar_DebugMessage("CalendarNetwork_EndRSVPUpdate: Process update for "..pUserName..","..pRevision.." since revision "..pSinceRevision.." from "..pSender);
1602 end
1603  
1604 local vDatabase = EventDatabase_GetDatabase(pUserName, true);
1605 local vRSVPChanges = vDatabase.RSVPs;
1606 local vReconstructRSVPs = false;
1607  
1608 -- If the update is for one of our own databases process it
1609 -- separately
1610  
1611 if vDatabase.IsPlayerOwned then
1612 CalendarNetwork_AskProcessSelfRSVPUpdate(vRSVPUpdate);
1613 return;
1614 end
1615  
1616 -- Process the update
1617  
1618 CalendarNetwork_ProcessRSVPUpdate(vRSVPUpdate, false);
1619 end
1620  
1621 function CalendarNetwork_ProcessRSVPUpdate(pRSVPUpdate, pForceReconstruct)
1622 local vIsOwnerUpdate = pRSVPUpdate.mSender == pRSVPUpdate.mUserName;
1623 local vDatabase = EventDatabase_GetDatabase(pRSVPUpdate.mUserName, true);
1624 local vRSVPChanges = vDatabase.RSVPs;
1625  
1626 if not vRSVPChanges
1627 or vRSVPChanges.ID ~= pRSVPUpdate.mDatabaseID then
1628 vDatabase.RSVPs = CalendarChanges_New(pRSVPUpdate.mDatabaseID);
1629 vRSVPChanges = vDatabase.RSVPs;
1630 end
1631  
1632 for vRevision = pRSVPUpdate.mSinceRevision + 1, pRSVPUpdate.mRevision do
1633 local vChanges = pRSVPUpdate.mChanges[vRevision];
1634  
1635 -- If the revision is newer than what we have, insert the new data
1636  
1637 if vRevision > vRSVPChanges.Revision then
1638 CalendarChanges_SetChangeList(vRSVPChanges, vRevision, vChanges);
1639 CalendarChanges_Close(vRSVPChanges, vRevision);
1640  
1641 if vChanges then
1642 EventDatabase_ExecuteRSVPChangeList(vDatabase, vChanges, true);
1643 end
1644 elseif vIsOwnerUpdate and vRevision > vRSVPChanges.AuthRevision then
1645 if gGroupCalendar_Settings.DebugUpdates then
1646 Calendar_DebugMessage("CalendarNetwork_EndRSVPUpdate: Ignoring owner update for "..vRevision..": Not implemented");
1647 end
1648 else
1649 if gGroupCalendar_Settings.DebugUpdates then
1650 Calendar_DebugMessage("CalendarNetwork_EndRSVPUpdate: Ignoring revision "..vRevision..": Already exists");
1651 end
1652 end
1653 end
1654  
1655 vRSVPChanges.Revision = pRSVPUpdate.mRevision;
1656  
1657 -- Update AuthRevision if the update came from the owner
1658  
1659 if vIsOwnerUpdate
1660 and pRSVPUpdate.mSinceRevision <= vRSVPChanges.AuthRevision then
1661 vRSVPChanges.AuthRevision = pRSVPUpdate.mRevision;
1662 end
1663 end
1664  
1665 function CalendarNetwork_ProcessSelfRSVPUpdate()
1666 CalendarNetwork_ProcessRSVPUpdate(gGroupCalendar_SelfRSVPUpdate, true);
1667 end
1668  
1669 function CalendarNetwork_RejectSelfRSVPUpdate()
1670 local vDatabase = EventDatabase_GetDatabase(gGroupCalendar_SelfRSVPUpdate.mUserName, true);
1671  
1672 if not vDatabase then
1673 return;
1674 end
1675  
1676 EventDatabase_RebuildRSVPs(vDatabase);
1677 end
1678  
1679 function CalendarNetwork_CancelRSVPUpdate(pSender, pUserName, pDatabaseID)
1680 CalendarNetwork_FindRSVPUpdate(pSender, pUserName, pDatabaseID, true);
1681 end
1682  
1683 function CalendarNetwork_KillSendersRSVPUpdates(pSender, pDontRequestForUserName)
1684 local vRSVPUpdate = CalendarNetwork_FindRSVPUpdate(pSender, nil, nil, pDelete);
1685  
1686 if not vRSVPUpdate
1687 or vRSVPUpdate.mUserName == pDontRequestForUserName then
1688 return;
1689 end
1690  
1691 CalendarNetwork_RequestUpdateForUser(vRSVPUpdate.mUserName, "RAT");
1692 end
1693  
1694 function CalendarNetwork_FindRSVPUpdate(pSender, pUserName, pDatabaseID, pDelete)
1695 for vIndex, vRSVPUpdate in gGroupCalendar_Queue.RSVPUpdates do
1696 if (pSender == nil or vRSVPUpdate.mSender == pSender)
1697 and (pUserName == nil or vRSVPUpdate.mUserName == pUserName)
1698 and (pDatabaseID == nil or vRSVPUpdate.mDatabaseID == pDatabaseID) then
1699 -- Delete the update if requested
1700  
1701 if pDelete then
1702 table.remove(gGroupCalendar_Queue.RSVPUpdates,vIndex);
1703 end
1704  
1705 return vRSVPUpdate;
1706 end
1707 end
1708  
1709 return nil;
1710 end
1711  
1712 function CalendarNetwork_KillOldUpdates(pDatabaseTag, pUpdates, pMinimumTime)
1713 local vIndex = 1;
1714 local vNumUpdates = table.getn(pUpdates);
1715  
1716 while vIndex <= vNumUpdates do
1717 local vUpdate = pUpdates[vIndex];
1718  
1719 if vUpdate.mLastMessageTime < pMinimumTime then
1720 table.remove(pUpdates, vIndex);
1721 vNumUpdates = vNumUpdates - 1;
1722  
1723 -- Re-request the update
1724  
1725 CalendarNetwork_RequestUpdateForUser(vUpdate.mUserName, pDatabaseTag, false);
1726 else
1727 vIndex = vIndex + 1;
1728 end
1729 end
1730 end
1731  
1732 function CalendarNetwork_ParseCommandString(pCommandString)
1733  
1734 -- Verify the command begins with the message prefix
1735  
1736 if strsub(pCommandString, 1, gGroupCalendar_MessagePrefixLength) ~= gGroupCalendar_MessagePrefix then
1737 return nil;
1738 end
1739  
1740 local vCommandString = strsub(pCommandString, gGroupCalendar_MessagePrefixLength);
1741  
1742 return CalendarNetwork_ParseCommandSubString(vCommandString)
1743 end
1744  
1745 function CalendarNetwork_ParseCommandSubString(pCommandString)
1746 -- Break the command into parts
1747  
1748 local vCommand = {};
1749  
1750 for vOpcode, vOperands in string.gfind(pCommandString, "/(%w+):*([^/]*)") do
1751 local vOperation = {};
1752  
1753 vOperation.opcode = vOpcode;
1754 vOperation.operandString = vOperands;
1755 vOperation.operands = CalendarNetwork_ParseParameterString(vOperands);
1756  
1757 table.insert(vCommand, vOperation);
1758 end
1759  
1760 return vCommand;
1761 end
1762  
1763 function CalendarNetwork_ParseParameterString(pParameterString)
1764 local vParameters = {};
1765 local vIndex = 0;
1766 local vFound = true;
1767 local vStartIndex = 1;
1768  
1769 while vFound do
1770 local vEndIndex;
1771  
1772 vFound, vEndIndex, vParameter = string.find(pParameterString, "([^,]*),", vStartIndex);
1773  
1774 vIndex = vIndex + 1;
1775  
1776 if not vFound then
1777 vParameters[vIndex] = string.sub(pParameterString, vStartIndex);
1778 break;
1779 end
1780  
1781 vParameters[vIndex] = vParameter;
1782 vStartIndex = vEndIndex + 1;
1783 end
1784  
1785 return vParameters;
1786 end
1787  
1788 function CalendarNetwork_NewEvent(pDatabase, pEvent)
1789 -- Don't record private events in the change history
1790  
1791 if pEvent.mPrivate then
1792 return;
1793 end
1794  
1795 -- Append a change record for the event
1796  
1797 local vChangeList = EventDatabase_GetCurrentChangeList(pDatabase);
1798  
1799 EventDatabase_AppendNewEvent(vChangeList, pEvent, EventDatabase_GetEventPath(pEvent));
1800 end
1801  
1802 function CalendarNetwork_EventChanged(pDatabase, pEvent, pChangedFields)
1803 -- Don't record private events in the change history
1804  
1805 if pEvent.mPrivate then
1806 return;
1807 end
1808  
1809 -- Append a change record for the event
1810  
1811 local vChangeList = EventDatabase_GetCurrentChangeList(pDatabase);
1812  
1813 EventDatabase_AppendEventUpdate(
1814 vChangeList,
1815 pEvent,
1816 EventDatabase_GetEventPath(pEvent),
1817 pChangedFields);
1818 end
1819  
1820 function CalendarNetwork_RemovingEvent(pDatabase, pEvent)
1821 -- Don't record private events in the change history
1822  
1823 if pEvent.mPrivate then
1824 return;
1825 end
1826  
1827 -- Remove any references to the event from the change history
1828  
1829 EventDatabase_RemoveEventChanges(pDatabase, pEvent);
1830  
1831 -- Insert a delete event
1832  
1833 local vChangeList = EventDatabase_GetCurrentChangeList(pDatabase);
1834  
1835 table.insert(vChangeList, EventDatabase_GetEventPath(pEvent).."DEL");
1836 end
1837  
1838 function CalendarNetwork_SendRevisionChanged(pChanges, pLabel, pUserName)
1839 -- Just leave if there's no channel to communicate on
1840 -- or no changes to announce
1841  
1842 if not gGroupCalendar_Channel.ID
1843 or not pChanges then
1844 return;
1845 end
1846  
1847 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..CalendarChanges_GetRevisionPath(pLabel, pUserName, pChanges.ID, pChanges.Revision).."NOU");
1848 end
1849  
1850 function CalendarNetwork_DBRevisionChanged(pDatabase)
1851 CalendarNetwork_SendRevisionChanged(pDatabase.Changes, "DB", pDatabase.UserName);
1852 end
1853  
1854 function CalendarNetwork_RSVPRevisionChanged(pDatabase)
1855 CalendarNetwork_SendRevisionChanged(pDatabase.RSVPs, "RAT", pDatabase.UserName);
1856 end
1857  
1858 function CalendarNetwork_RequestAllUpdate()
1859 -- Just leave if there's no channel to communicate on
1860  
1861 if not gGroupCalendar_Channel.ID then
1862 return;
1863 end
1864  
1865 -- Send the request immmediately since delaying it for channel silence will only
1866 -- increase the number of times that everyone has to transmit their NOU responses
1867  
1868 CalendarNetwork_SendMessage(gGroupCalendar_MessagePrefix.."ALL/RFU");
1869 end
1870  
1871 function CalendarNetwork_RequestGuildUpdate(pGuildName, pMinRank)
1872 -- Just leave if there's no channel to communicate on
1873  
1874 if not gGroupCalendar_Channel.ID then
1875 return;
1876 end
1877  
1878 -- Send the request immmediately since delaying it for channel silence will only
1879 -- increase the number of times that everyone has to transmit their NOU responses
1880  
1881 if pMinRank then
1882 CalendarNetwork_SendMessage(gGroupCalendar_MessagePrefix.."GLD:"..pGuildName..","..pMinRank.."/RFU");
1883 else
1884 CalendarNetwork_SendMessage(gGroupCalendar_MessagePrefix.."GLD:"..pGuildName.."/RFU");
1885 end
1886 end
1887  
1888 function CalendarNetwork_RequestUpdateForUser(pUserName, pDatabaseTag, pRequestImmediately)
1889 local vDatabase = EventDatabase_GetDatabase(pUserName);
1890  
1891 if not vDatabase then
1892 return;
1893 end
1894  
1895 local vChanges;
1896  
1897 if pDatabaseTag == "DB" then
1898 vChanges = vDatabase.Changes;
1899 elseif pDatabaseTag == "RAT" then
1900 vChanges = vDatabase.RSVPs;
1901 else
1902 Calendar_ErrorMessage("CalendarNetwork_RequestUpdateForUser: Unknown database tag "..pDatabaseTag);
1903 return;
1904 end
1905  
1906 if not vChanges then
1907 return;
1908 end
1909  
1910 CalendarNetwork_RequestUpdate(vDatabase, vChanges, pDatabaseTag, pRequestImmediately);
1911 end
1912  
1913 function CalendarNetwork_RequestUpdate(pDatabase, pChanges, pDatabaseTag, pRequestImmediately)
1914 -- Just leave if there's no channel to communicate on
1915  
1916 if not gGroupCalendar_Channel.ID then
1917 return;
1918 end
1919  
1920 local vID, vRevision;
1921  
1922 if pChanges then
1923 vID = pChanges.ID;
1924 vRevision = pChanges.Revision;
1925 else
1926 vID = 0;
1927 vRevision = 0;
1928 end
1929  
1930 if pRequestImmediately then
1931 CalendarNetwork_SendMessage(gGroupCalendar_MessagePrefix..CalendarChanges_GetRevisionPath(pDatabaseTag, pDatabase.UserName, vID, vRevision, 0).."RFU");
1932 else
1933 CalendarNetwork_QueueRFURequest(pDatabase.UserName, pDatabaseTag, vID, vRevision);
1934 end
1935 end
1936  
1937 function CalendarNetwork_SendEmptyChanges(pChanges, pLabel, pUserName)
1938 local vID, vRevision;
1939  
1940 if pChanges then
1941 vID = pChanges.ID;
1942 vRevision = pChanges.Revision;
1943 else
1944 vID = Calendar_GetCurrentDateTimeUT60();
1945 vRevision = 0;
1946 end
1947  
1948 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..CalendarChanges_GetRevisionPath(pLabel, pUserName, vID, vRevision).."DEL");
1949 end
1950  
1951 function CalendarNetwork_SendChanges(pChanges, pLabel, pUserName, pLockdown, pSinceRevision)
1952 if pLockdown then
1953 CalendarChanges_LockdownCurrentChangeList(pChanges);
1954 end
1955  
1956 if CalendarChanges_IsEmpty(pChanges) then
1957 CalendarNetwork_SendEmptyChanges(pChanges, pLabel, pUserName);
1958 return;
1959 end
1960  
1961 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..CalendarChanges_GetRevisionPath(pLabel, pUserName, pChanges.ID, pChanges.Revision).."UPD:"..pSinceRevision);
1962  
1963 for vRevision = pSinceRevision + 1, pChanges.Revision do
1964 local vRevisionPath = CalendarChanges_GetRevisionPath(pLabel, pUserName, pChanges.ID, vRevision);
1965 local vChangeList = pChanges.ChangeList[vRevision];
1966  
1967 if vChangeList then
1968 vChangeList.IsOpen = nil; -- Make sure IsOpen is cleared, a bug may have caused it to remain open
1969  
1970 for vIndex, vChange in vChangeList do
1971 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..vRevisionPath..vChange);
1972 end
1973 end
1974 end
1975  
1976 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..CalendarChanges_GetRevisionPath(pLabel, pUserName, pChanges.ID, pChanges.Revision).."END:"..pSinceRevision);
1977 end
1978  
1979 function CalendarNetwork_QueueOutboundMessage(pMessage)
1980 table.insert(gGroupCalendar_Queue.OutboundMessages, pMessage);
1981  
1982 GroupCalendar_StartUpdateTimer();
1983 end
1984  
1985 function CalendarNetwork_QueueInboundMessage(pSender, pTrustLevel, pMessage)
1986 table.insert(gGroupCalendar_Queue.InboundMessages, {mSender = pSender, mTrustLevel = pTrustLevel, mMessage = pMessage});
1987  
1988 if table.getn(gGroupCalendar_Queue.InboundMessages) == 1 then
1989 gGroupCalendar_Queue.InboundDelay = 0;
1990 end
1991  
1992 GroupCalendar_StartUpdateTimer();
1993 end
1994  
1995 function CalendarNetwork_QueueTask(pTaskFunc, pTaskParam, pDelay, pTaskID)
1996 local vTask = {mTaskFunc = pTaskFunc, mTaskParam = pTaskParam, mDelay = pDelay, mID = pTaskID};
1997  
1998 -- Ignore tasks with duplicate IDs
1999  
2000 if pTaskID then
2001 for vIndex, vTask in gGroupCalendar_Queue.Tasks do
2002 if vTask.mID == pTaskID then
2003 return;
2004 end
2005 end
2006 end
2007  
2008 -- Insert the task
2009  
2010 table.insert(gGroupCalendar_Queue.Tasks, vTask);
2011  
2012 CalendarNetwork_UpdateTaskQueueDelay();
2013  
2014 return true;
2015 end
2016  
2017 function CalendarNetwork_UpdateTaskQueueDelay()
2018 local vDelay = nil;
2019  
2020 for vIndex, vTask in gGroupCalendar_Queue.Tasks do
2021 if not vDelay
2022 or vTask.mDelay < vDelay then
2023 vDelay = vTask.mDelay;
2024 end
2025 end
2026  
2027 gGroupCalendar_Queue.TasksDelay = vDelay;
2028  
2029 if vDelay then
2030 GroupCalendar_StartUpdateTimer();
2031 end
2032 end
2033  
2034 function CalendarNetwork_SetTaskDelay(pTaskID, pDelay)
2035 -- Ignore tasks with duplicate IDs
2036  
2037 for vIndex, vTask in gGroupCalendar_Queue.Tasks do
2038 if vTask.mID == pTaskID then
2039 vTask.mDelay = pDelay;
2040 CalendarNetwork_UpdateTaskQueueDelay();
2041 return;
2042 end
2043 end
2044 end
2045  
2046 function CalendarNetwork_QueueRequest(pRequest, pDelay, pPriority)
2047 pRequest.mDelay = pDelay;
2048  
2049 if gGroupCalendar_Settings.DebugQueues then
2050 Calendar_DebugMessage("CalendarNetwork_QueueRequest: "..pRequest.mOpcode.." in "..pDelay.." seconds");
2051 end
2052  
2053 if not pPriority then
2054 pPriority = 2;
2055 end
2056  
2057 table.insert(gGroupCalendar_Queue.Requests[pPriority], pRequest);
2058  
2059 local vTotalRequests = 0;
2060  
2061 for vPriority, vRequestQueue in gGroupCalendar_Queue.Requests do
2062 vTotalRequests = vTotalRequests + table.getn(gGroupCalendar_Queue.Requests[vPriority]);
2063 end
2064  
2065 if vTotalRequests == 1
2066 or (pRequest.mDelay < gGroupCalendar_Queue.RequestsDelay
2067 and pRequest.mDelay < gCalendarNetwork_RequestDelay.RequestQueue) then
2068 gGroupCalendar_Queue.RequestsDelay = pRequest.mDelay;
2069 end
2070  
2071 GroupCalendar_StartUpdateTimer();
2072  
2073 return true;
2074 end
2075  
2076 function CalendarNetwork_SetRequestPriority(pRequest, pPriority)
2077 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2078 for vIndex, vRequest in vRequests do
2079 if vRequest == pRequest then
2080 if vPriority ~= pPriority then
2081 table.remove(vRequests, vIndex);
2082 table.insert(gGroupCalendar_Queue.Requests[pPriority], vRequest);
2083 end
2084 return;
2085 end
2086 end
2087 end
2088 end
2089  
2090 function CalendarNetwork_QueueUniqueOpcodeRequest(pRequest, pDelay)
2091 -- Remove an existing request with the same opcode
2092  
2093 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2094 for vIndex, vRequest in vRequests do
2095 if vRequest.mOpcode == pRequest.mOpcode then
2096 table.remove(vRequests, vIndex);
2097 break;
2098 end
2099 end
2100 end
2101  
2102 return CalendarNetwork_QueueRequest(pRequest, pDelay);
2103 end
2104  
2105 function CalendarNetwork_QueueUniqueUserRequest(pRequest, pDelay)
2106 -- Remove an existing request with the same opcode and user name
2107  
2108 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2109 for vIndex, vRequest in vRequests do
2110 if vRequest.mOpcode == pRequest.mOpcode
2111 and vRequest.mUserName == pRequest.mUserName then
2112 table.remove(vRequests, vIndex);
2113 break;
2114 end
2115 end
2116 end
2117  
2118 return CalendarNetwork_QueueRequest(pRequest, pDelay);
2119 end
2120  
2121 function CalendarNetwork_ArrayContainsArray(pArray, pSubArray)
2122 for vFieldName, vFieldValue in pSubArray do
2123 if pArray[vFieldName] ~= vFieldValue then
2124 return false;
2125 end
2126 end
2127  
2128 return true;
2129 end
2130  
2131 function CalendarNetwork_FindRequest(pRequest)
2132 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2133 for vIndex, vRequest in vRequests do
2134 if CalendarNetwork_ArrayContainsArray(vRequest, pRequest) then
2135 return vRequest;
2136 end
2137 end
2138 end
2139  
2140 return nil;
2141 end
2142  
2143 function CalendarNetwork_FindUPDRequest(pDatabaseTag, pUserName)
2144 return CalendarNetwork_FindRequest({mOpcode = pDatabaseTag.."_UPD", mUserName = pUserName});
2145 end
2146  
2147 function CalendarNetwork_CancelRedundantRFURequest(pDatabaseTag, pUserName, pDatabaseID, pFromRevision)
2148 local vOpcode = pDatabaseTag.."_RFU";
2149  
2150 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2151 for vIndex, vRequest in vRequests do
2152 if vRequest.mOpcode == vOpcode
2153 and vRequest.mUserName == pUserName then
2154 if vRequest.mDatabaseID == pDatabaseID
2155 and pFromRevision <= vRequest.mRevision then
2156 if gGroupCalendar_Settings.DebugUpdates then
2157 Calendar_DebugMessage("Removing redundant RFU for "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pFromRevision);
2158 end
2159  
2160 table.remove(vRequests, vIndex);
2161 return false; -- Return false to indicate there is no longer an RFU for this user
2162 else
2163 if gGroupCalendar_Settings.DebugUpdates then
2164 Calendar_DebugMessage("Keeping RFU request for "..pDatabaseTag.." "..pUserName..","..vRequest.mDatabaseID..","..vRequest.mRevision);
2165 Calendar_DebugMessage("Better thean "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pFromRevision);
2166 end
2167 end
2168  
2169 if gGroupCalendar_Settings.DebugUpdates then
2170 Calendar_DebugMessage("RFU for "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pFromRevision.." already exists");
2171 end
2172  
2173 return true; -- Return true to indicate there is an RFU for this user
2174 end
2175 end
2176 end
2177  
2178 return false; -- Return false to indicate no RFU for this user was found
2179 end
2180  
2181 function CalendarNetwork_NeedsUpdateTimer()
2182 if gGroupCalendar_Queue.TasksDelay then
2183 return true;
2184 end
2185  
2186 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2187 if table.getn(vRequests) > 0 then
2188 return true;
2189 end
2190 end
2191  
2192 if table.getn(gGroupCalendar_Queue.InboundMessages) > 0 then
2193 return true;
2194 end
2195  
2196 if table.getn(gGroupCalendar_Queue.OutboundMessages) > 0 then
2197 return true;
2198 end
2199  
2200 return false;
2201 end
2202  
2203 function CalendarNetwork_ProcessTaskQueue(pElapsed)
2204 if not gGroupCalendar_Queue.TasksDelay then
2205 return;
2206 end
2207  
2208 gGroupCalendar_Queue.TasksDelay = gGroupCalendar_Queue.TasksDelay - pElapsed;
2209 gGroupCalendar_Queue.TasksElapsed = gGroupCalendar_Queue.TasksElapsed + pElapsed;
2210  
2211 if gGroupCalendar_Queue.TasksDelay <= 0 then
2212 local vNumTasks = table.getn(gGroupCalendar_Queue.Tasks);
2213 local vIndex = 1;
2214  
2215 gGroupCalendar_Queue.TasksDelay = nil;
2216  
2217 local vLatencyStartTime;
2218  
2219 if gGroupCalendar_Settings.DebugLatency then
2220 vLatencyStartTime = GetTime();
2221 end
2222  
2223 while vIndex <= vNumTasks do
2224 local vTask = gGroupCalendar_Queue.Tasks[vIndex];
2225  
2226 vTask.mDelay = vTask.mDelay - gGroupCalendar_Queue.TasksElapsed;
2227  
2228 if vTask.mDelay <= 0 then
2229 table.remove(gGroupCalendar_Queue.Tasks, vIndex);
2230 vNumTasks = vNumTasks - 1;
2231  
2232 -- Perform the task
2233  
2234 vTask.mTaskFunc(vTask.mTaskParam);
2235  
2236 else
2237 if not gGroupCalendar_Queue.TasksDelay
2238 or vTask.mDelay < gGroupCalendar_Queue.TasksDelay then
2239 gGroupCalendar_Queue.TasksDelay = vTask.mDelay;
2240 end
2241  
2242 vIndex = vIndex + 1;
2243 end
2244 end
2245  
2246 if gGroupCalendar_Settings.DebugLatency then
2247 local vElapsed = GetTime() - vLatencyStartTime;
2248  
2249 if vElapsed > 0.1 then
2250 Calendar_DebugMessage("Tasks took "..vElapsed.."s to execute ("..vNumTasks.." tasks)");
2251 end
2252 end
2253  
2254 gGroupCalendar_Queue.TasksElapsed = 0;
2255 end
2256 end
2257  
2258 function CalendarNetwork_ProcessInboundQueue(pElapsed, pCurrentTimeStamp)
2259 local vNumInboundMessages = table.getn(gGroupCalendar_Queue.InboundMessages);
2260  
2261 if vNumInboundMessages > 0 then
2262 local vCollisionDetected = false;
2263  
2264 gGroupCalendar_Queue.InboundDelay = gGroupCalendar_Queue.InboundDelay - pElapsed;
2265  
2266 if gGroupCalendar_Queue.InboundDelay <= 0 then
2267 -- Process one message
2268  
2269 local vMessage = gGroupCalendar_Queue.InboundMessages[1];
2270  
2271 table.remove(gGroupCalendar_Queue.InboundMessages, 1);
2272  
2273 local vLatencyStartTime;
2274  
2275 if gGroupCalendar_Settings.DebugLatency then
2276 vLatencyStartTime = GetTime();
2277 end
2278  
2279 CalendarNetwork_ProcessCommandString(vMessage.mSender, vMessage.mTrustLevel, vMessage.mMessage, pCurrentTimeStamp);
2280  
2281 if gGroupCalendar_Settings.DebugLatency then
2282 local vElapsed = GetTime() - vLatencyStartTime;
2283  
2284 if vElapsed > 0.1 then
2285 Calendar_DebugMessage("Inbound message took "..vElapsed.."s to process");
2286 Calendar_DumpArray("Message", vMessage);
2287 end
2288 end
2289  
2290 gGroupCalendar_Queue.InboundDelay = gCalendarNetwork_RequestDelay.InboundQueue;
2291  
2292 if gGroupCalendar_Queue.InboundLastSender ~= vMessage.mSender then
2293 if gGroupCalendar_Queue.InboundTimeSinceLastMessage < gCalendarNetwork_RequestDelay.OutboundQueueGapMin then
2294 -- Collision between other senders detected
2295  
2296 if gGroupCalendar_Settings.DebugQueues then
2297 Calendar_DebugMessage("Collision detected between "..gGroupCalendar_Queue.InboundLastSender.." and "..vMessage.mSender);
2298 end
2299  
2300 vCollisionDetected = true;
2301 end
2302  
2303 gGroupCalendar_Queue.InboundLastSender = vMessage.mSender;
2304 end
2305  
2306 gGroupCalendar_Queue.InboundTimeSinceLastMessage = 0;
2307 end
2308  
2309 -- Terminate processing while there are any pending inbound messages and delay further
2310 -- processing of the outbound and request queues
2311  
2312 -- If there was a collision and we *weren't* part of it then increase the range to
2313 -- avoid allowing us to become a part of the next collision. This should gradually eliminate
2314 -- transmittors from the collisions until someone successfully takes over the wire.
2315 -- This probably seems really counterintuitive, but the idea is to eliminate players who
2316 -- didn't participate in a collision from attempting to try again. By doing this, each collision
2317 -- should eliminate a significant number of players from the accident, leaving only the actual
2318 -- victims to try again. The next collision will then eliminate several of those players
2319 -- until within a few seconds only one player remains and that player will then have control
2320 -- and finish his transmission.
2321  
2322 local vOutboundQueueGapMin = gCalendarNetwork_RequestDelay.OutboundQueueGapMin;
2323 local vRequestQueueGapMin = gCalendarNetwork_RequestDelay.RequestQueueGapMin;
2324  
2325 if vCollisionDetected
2326 and gGroupCalendar_Queue.OutboundTimeSinceLastMessage > gCalendarNetwork_RequestDelay.OutboundQueueGapMin then
2327 -- Collision detected between other players so use a longer gap to make sure
2328 -- we're not part of the next collision
2329  
2330 vOutboundQueueGapMin = vOutboundQueueGapMin + gCalendarNetwork_RequestDelay.OutboundQueueGapWidth;
2331 vRequestQueueGapMin = vRequestQueueGapMin + gCalendarNetwork_RequestDelay.RequestQueueGapWidth;
2332 end
2333  
2334 vRandom = math.random();
2335  
2336 if gGroupCalendar_Queue.OutboundDelay < vOutboundQueueGapMin then
2337 gGroupCalendar_Queue.OutboundDelay = vOutboundQueueGapMin + vRandom * gCalendarNetwork_RequestDelay.OutboundQueueGapWidth;
2338 end
2339  
2340 if gGroupCalendar_Queue.RequestsDelay < vRequestQueueGapMin then
2341 gGroupCalendar_Queue.RequestsDelay = vRequestQueueGapMin + vRandom * gCalendarNetwork_RequestDelay.RequestQueueGapWidth;
2342 end
2343  
2344 return true;
2345 end
2346  
2347 return false;
2348 end
2349  
2350 function CalendarNetwork_ProcessOutboundQueue(pElapsed)
2351 local vNumOutboundMessages = table.getn(gGroupCalendar_Queue.OutboundMessages);
2352  
2353 if vNumOutboundMessages > 0 then
2354 gGroupCalendar_Queue.OutboundDelay = gGroupCalendar_Queue.OutboundDelay - pElapsed;
2355  
2356 if gGroupCalendar_Queue.OutboundDelay <= 0 then
2357 local vLatencyStartTime;
2358  
2359 if gGroupCalendar_Settings.DebugLatency then
2360 vLatencyStartTime = GetTime();
2361 end
2362  
2363 -- Send one message
2364  
2365 local vMessage = gGroupCalendar_Queue.OutboundMessages[1];
2366  
2367 table.remove(gGroupCalendar_Queue.OutboundMessages, 1);
2368  
2369 CalendarNetwork_SendMessage(vMessage);
2370  
2371 gGroupCalendar_Queue.OutboundDelay = gCalendarNetwork_RequestDelay.OutboundQueue;
2372  
2373 -- Reset the time since last message to figure out if we're part of a collision
2374  
2375 gGroupCalendar_Queue.OutboundTimeSinceLastMessage = 0;
2376  
2377 --
2378  
2379 if gGroupCalendar_Settings.DebugLatency then
2380 local vElapsed = GetTime() - vLatencyStartTime;
2381  
2382 if vElapsed > 0.1 then
2383 Calendar_DebugMessage("Outbound message took "..vElapsed.."s to process");
2384 Calendar_DebugMessage(vMessage);
2385 end
2386 end
2387  
2388 -- Stop processing if this isn't the last outbound message, otherwise
2389 -- go ahead and allow the request queue to be processed
2390  
2391 if vNumOutboundMessages == 1 then
2392 gGroupCalendar_Queue.RequestsDelay = 0;
2393 else
2394 return true;
2395 end
2396 else
2397 -- Terminate processing while there are any pending outbound messages and delay further
2398 -- processing of the request queues
2399  
2400 gGroupCalendar_Queue.RequestsDelay = gCalendarNetwork_RequestDelay.RequestQueueGapMin + math.random() * gCalendarNetwork_RequestDelay.RequestQueueGapWidth;
2401 return true;
2402 end
2403 end
2404  
2405 return false;
2406 end
2407  
2408 GroupCalendar_cUpdateRequestOpcodes =
2409 {
2410 ["DB_UPD"] = true,
2411 ["RAT_UPD"] = true,
2412 ["DB_NOU"] = true,
2413 ["RAT_NOU"] = true
2414 };
2415  
2416 function CalendarNetwork_CanProcessRequest(pRequest)
2417 if not GroupCalendar_cUpdateRequestOpcodes[pRequest.mOpCode] then
2418 return true;
2419 end
2420  
2421 if not gGroupCalendar_EnableUpdates then
2422 return false;
2423 end
2424  
2425 if pRequest.mUserName then
2426 local vDatabase = EventDatabase_GetDatabase(pRequest.mUserName, false);
2427  
2428 if vDatabase
2429 and vDatabase.IsPlayerOwned
2430 and not gGroupCalendar_EnableSelfUpdates then
2431 return false;
2432 end
2433 end
2434  
2435 return true;
2436 end
2437  
2438 function CalendarNetwork_ProcessRequestQueue(pElapsed, pSuppressProcessing)
2439 local vNumRequests = 0;
2440  
2441 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2442 vNumRequests = vNumRequests + table.getn(vRequests);
2443 end
2444  
2445 if vNumRequests > 0 then
2446 gGroupCalendar_Queue.RequestsDelay = gGroupCalendar_Queue.RequestsDelay - pElapsed;
2447 gGroupCalendar_Queue.RequestsElapsed = gGroupCalendar_Queue.RequestsElapsed + pElapsed;
2448  
2449 if gGroupCalendar_Queue.RequestsDelay <= 0 then
2450 -- Process one request
2451  
2452 local vDidProcessRequest = pSuppressProcessing;
2453 local vMinDelayForNextRequest = nil;
2454  
2455 local vLatencyStartTime;
2456 local vTotalRequests = 0;
2457  
2458 if gGroupCalendar_Settings.DebugLatency then
2459 vLatencyStartTime = GetTime();
2460 end
2461  
2462 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2463 local vIndex = 1;
2464 local vNumRequestsThisQueue = table.getn(vRequests);
2465  
2466 vTotalRequests = vTotalRequests + vNumRequestsThisQueue;
2467  
2468 while vIndex <= vNumRequestsThisQueue do
2469 local vRequest = vRequests[vIndex];
2470  
2471 if vRequest.mDelay > 0 then
2472 vRequest.mDelay = vRequest.mDelay - gGroupCalendar_Queue.RequestsElapsed;
2473  
2474 if vRequest.mDelay < 0 then
2475 vRequest.mDelay = 0;
2476 end
2477 end
2478  
2479 if vRequest.mDelay == 0
2480 and not vDidProcessRequest then
2481 table.remove(vRequests, vIndex);
2482 vNumRequestsThisQueue = vNumRequestsThisQueue - 1;
2483  
2484 CalendarNetwork_ProcessRequest(vRequest);
2485  
2486 vDidProcessRequest = true;
2487 else
2488 if not vMinDelayForNextRequest
2489 or vRequest.mDelay < vMinDelayForNextRequest then
2490 vMinDelayForNextRequest = vRequest.mDelay;
2491 end
2492  
2493 vIndex = vIndex + 1;
2494 end
2495 end -- while vIndex
2496 end -- for vPriority
2497  
2498 if gGroupCalendar_Settings.DebugLatency then
2499 local vElapsed = GetTime() - vLatencyStartTime;
2500  
2501 if vElapsed > 0.1 then
2502 Calendar_DebugMessage("Requests took "..vElapsed.."s to process ("..vTotalRequests.." total requests)");
2503 end
2504 end
2505  
2506 if not vMinDelayForNextRequest
2507 or vMinDelayForNextRequest < gCalendarNetwork_RequestDelay.RequestQueue then
2508 vMinDelayForNextRequest = gCalendarNetwork_RequestDelay.RequestQueue;
2509 end
2510  
2511 gGroupCalendar_Queue.RequestsDelay = vMinDelayForNextRequest;
2512 gGroupCalendar_Queue.RequestsElapsed = 0;
2513 end
2514 end
2515 end
2516  
2517 function CalendarNetwork_ProcessQueues(pElapsed)
2518 -- Get the current time stamp
2519  
2520 local vCurrentTimeStamp = Calendar_GetCurrentLocalDateTimeStamp();
2521  
2522 -- Process tasks
2523  
2524 CalendarNetwork_ProcessTaskQueue(pElapsed);
2525  
2526 -- Update the collision detection counters
2527  
2528 gGroupCalendar_Queue.InboundTimeSinceLastMessage = gGroupCalendar_Queue.InboundTimeSinceLastMessage + pElapsed;
2529 gGroupCalendar_Queue.OutboundTimeSinceLastMessage = gGroupCalendar_Queue.OutboundTimeSinceLastMessage + pElapsed;
2530  
2531 -- Process inbound messages
2532  
2533 local vSuppressRequests = false;
2534  
2535 if CalendarNetwork_ProcessInboundQueue(pElapsed, vCurrentTimeStamp) then
2536 vSuppressRequests = true;
2537  
2538 elseif CalendarNetwork_ProcessOutboundQueue(pElapsed) then
2539 vSuppressRequests = true;
2540 end
2541  
2542 -- Process pending requests if there are no outbound messages pending
2543  
2544 CalendarNetwork_ProcessRequestQueue(pElapsed, vSuppressRequests);
2545 end
2546  
2547 function CalendarNetwork_ProcessRequest(pRequest)
2548 local vDatabase;
2549  
2550 if pRequest.mUserName then
2551 vDatabase = EventDatabase_GetDatabase(pRequest.mUserName, false);
2552 else
2553 vDatabase = nil;
2554 end
2555  
2556 if pRequest.mOpcode == "DB_UPD" then
2557 if vDatabase then -- Check for nil since the database may have gotten deleted while in the queue
2558 CalendarNetwork_SendChanges(vDatabase.Changes, "DB", vDatabase.UserName, vDatabase.IsPlayerOwned, pRequest.mRevision);
2559 end
2560  
2561 elseif pRequest.mOpcode == "RAT_UPD" then
2562 if vDatabase then -- Check for nil since the database may have gotten deleted while in the queue
2563 CalendarNetwork_SendChanges(vDatabase.RSVPs, "RAT", vDatabase.UserName, vDatabase.IsPlayerOwned, pRequest.mRevision);
2564 end
2565  
2566 elseif pRequest.mOpcode == "DB_NOU" then
2567 if vDatabase then -- Check for nil since the database may have gotten deleted while in the queue
2568 CalendarNetwork_DBRevisionChanged(vDatabase);
2569 end
2570  
2571 elseif pRequest.mOpcode == "RAT_NOU" then
2572 if vDatabase then -- Check for nil since the database may have gotten deleted while in the queue
2573 CalendarNetwork_RSVPRevisionChanged(vDatabase);
2574 end
2575  
2576 elseif pRequest.mOpcode == "DB_RFU" then
2577 local vCurrentRevision;
2578 local vAuthRevision;
2579  
2580 if vDatabase and vDatabase.Changes then
2581 vCurrentRevision = vDatabase.Changes.Revision;
2582 vAuthRevision = vDatabase.Changes.AuthRevision;
2583 else
2584 vCurrentRevision = 0;
2585 vAuthRevision = nil;
2586 end
2587  
2588 if vCurrentRevision < pRequest.mRevision
2589 or vCurrentRevision == 0 then
2590 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..EventDatabase_GetDBRevisionPath(pRequest.mUserName, pRequest.mDatabaseID, vCurrentRevision, vAuthRevision).."RFU");
2591 end
2592  
2593 elseif pRequest.mOpcode == "RAT_RFU" then
2594 local vCurrentRevision;
2595 local vAuthRevision;
2596  
2597 if vDatabase and vDatabase.RSVPs then
2598 vCurrentRevision = vDatabase.RSVPs.Revision;
2599 vAuthRevision = vDatabase.RSVPs.AuthRevision;
2600 else
2601 vCurrentRevision = 0;
2602 vAuthRevision = nil;
2603 end
2604  
2605 if vCurrentRevision < pRequest.mRevision
2606 or vCurrentRevision == 0 then
2607 CalendarNetwork_QueueOutboundMessage(gGroupCalendar_MessagePrefix..EventDatabase_GetRSVPRevisionPath(pRequest.mUserName, pRequest.mDatabaseID, vCurrentRevision, vAuthRevision).."RFU");
2608 end
2609  
2610 elseif pRequest.mOpcode == "AUTOCONFIG" then
2611 CalendarNetwork_DoAutoConfig(pRequest.mCheckDatabaseTrust);
2612  
2613 elseif pRequest.mOpcode == "DBTRUST" then
2614 EventDatabase_CheckDatabaseTrust();
2615  
2616 elseif pRequest.mOpcode == "OWNEDNOTICES" then
2617 gGroupCalendar_EnableUpdates = true;
2618 gGroupCalendar_EnableSelfUpdates = true;
2619 CalendarNetwork_SendOwnedDatabaseUpdateNotices();
2620 end
2621 end
2622  
2623 function CalendarNetwork_Initialize()
2624 gGroupCalendar_Initialized = true;
2625  
2626 if gGroupCalendar_Settings.DebugInit then
2627 Calendar_DebugMessage("GroupCalendar Initializing: Starting initialization");
2628 end
2629  
2630 CalendarNetwork_PlayerGuildChanged();
2631  
2632 -- Go ahead and do manual channel configuration now, automatic
2633 -- config will be handled once the player guild gets set and
2634 -- the roster gets loaded
2635  
2636 if not gGroupCalendar_PlayerSettings.Channel.AutoConfig then
2637 if gGroupCalendar_Settings.DebugInit then
2638 Calendar_DebugMessage("GroupCalendar INIT: Starting up data channel (manual configuration)");
2639 end
2640  
2641 if gGroupCalendar_PlayerSettings.Channel.Name then
2642 CalendarNetwork_SetChannel(
2643 gGroupCalendar_PlayerSettings.Channel.Name,
2644 gGroupCalendar_PlayerSettings.Channel.Password);
2645 else
2646 CalendarNetwork_SetChannelStatus("Disconnected");
2647 Calendar_DebugMessage("GroupCalendar channel is not set");
2648 end
2649 end
2650  
2651 Calendar_GetPrimaryTradeskills();
2652 end
2653  
2654 function CalendarNetwork_SendNotices()
2655 -- If trust isn't available yet just defer the notices
2656  
2657 if not CalendarTrust_TrustCheckingAvailable() then
2658 gGroupCalendar_SendNoticesOnRosterUpdate = true;
2659 return;
2660 end
2661  
2662 --
2663  
2664 if gGroupCalendar_Settings.DebugInit then
2665 Calendar_DebugMessage("Sending notices");
2666 end
2667  
2668 CalendarNetwork_RequestAllDatabaseUpdates();
2669  
2670 -- Request databases for trusted players who haven't sent one yet
2671  
2672 if not gGroupCalendar_PlayerSettings.Security.TrustAnyone then
2673 CalendarNetwork_RequestMissingDatabases();
2674 end
2675  
2676 -- Schedule a request to send out owned database notices in two minutes
2677  
2678 CalendarNetwork_QueueUniqueOpcodeRequest({mOpcode = "OWNEDNOTICES"}, gCalendarNetwork_RequestDelay.OwnedNotices);
2679 end
2680  
2681 function CalendarNetwork_RequestAllDatabaseUpdates()
2682 -- Immediately send a notice of our version
2683  
2684 CalendarNetwork_SendMessage(gGroupCalendar_MessagePrefix.."VER:"..gGroupCalendar_VersionString);
2685  
2686 -- Immediately request updates to our own databases
2687  
2688 for vRealmName, vDatabase in gGroupCalendar_Database.Databases do
2689 if EventDatabase_DatabaseIsVisible(vDatabase)
2690 and vDatabase.IsPlayerOwned then
2691 CalendarNetwork_RequestUpdate(vDatabase, vDatabase.Changes, "DB", true);
2692 CalendarNetwork_RequestUpdate(vDatabase, vDatabase.RSVPs, "RAT", true);
2693 end
2694 end
2695  
2696 -- Request updates to all other databases after a delay
2697  
2698 CalendarNetwork_QueueTask(CalendarNetwork_RequestExternalDatabaseUpdates, nil, gCalendarNetwork_RequestDelay.ExternalUpdateRequest, "EXTERNALUPDATE");
2699 end
2700  
2701 function CalendarNetwork_RequestExternalDatabaseUpdates()
2702 if gGroupCalendar_PlayerSettings.Security.TrustAnyone then
2703 CalendarNetwork_RequestAllUpdate();
2704  
2705 elseif gGroupCalendar_PlayerSettings.Security.TrustGuildies
2706 and gGroupCalendar_PlayerGuild then
2707 CalendarNetwork_RequestGuildUpdate(gGroupCalendar_PlayerGuild, gGroupCalendar_PlayerSettings.Security.MinTrustedRank);
2708 end
2709  
2710 if not gGroupCalendar_PlayerSettings.Security.TrustAnyone then
2711 for vRealmName, vDatabase in gGroupCalendar_Database.Databases do
2712 if EventDatabase_DatabaseIsVisible(vDatabase) then
2713 if not vDatabase.IsPlayerOwned
2714 and (not gGroupCalendar_PlayerSettings.Security.TrustGuildies
2715 or vDatabase.Guild ~= gGroupCalendar_PlayerGuild) then
2716 CalendarNetwork_RequestUpdate(vDatabase, vDatabase.Changes, "DB", false);
2717 CalendarNetwork_RequestUpdate(vDatabase, vDatabase.RSVPs, "RAT", false);
2718 end
2719 end
2720 end
2721 end
2722 end
2723  
2724 function CalendarNetwork_SendOwnedDatabaseUpdateNotices()
2725 for vRealmName, vDatabase in gGroupCalendar_Database.Databases do
2726 if EventDatabase_DatabaseIsVisible(vDatabase)
2727 and vDatabase.IsPlayerOwned then
2728 if not CalendarChanges_IsEmpty(vDatabase.Changes) then
2729 CalendarNetwork_DBRevisionChanged(vDatabase);
2730 end
2731  
2732 if not CalendarChanges_IsEmpty(vDatabase.RSVPs) then
2733 CalendarNetwork_RSVPRevisionChanged(vDatabase);
2734 end
2735 end
2736 end
2737 end
2738  
2739 gCalendarNetwork_GuildMemberRankCache = nil;
2740  
2741 function CalendarNetwork_FlushCaches()
2742 gCalendarNetwork_GuildMemberRankCache = nil;
2743 gCalendarNetwork_UserTrustCache = {};
2744 end
2745  
2746 function CalendarNetwork_GetGuildRosterCache()
2747 if gCalendarNetwork_GuildMemberRankCache then
2748 return gCalendarNetwork_GuildMemberRankCache;
2749 end
2750  
2751 -- Clear the cache
2752  
2753 gCalendarNetwork_GuildMemberRankCache = {};
2754  
2755 -- Scan the roster and collect the info
2756  
2757 local vNumGuildMembers = GetNumGuildMembers(true);
2758  
2759 for vIndex = 1, vNumGuildMembers do
2760 local vName, vRank, vRankIndex, vLevel, vClass, vZone, vNote, vOfficerNote, vOnline = GetGuildRosterInfo(vIndex);
2761  
2762 if vName then -- Have to check for name in case a guild member gets booted while querying the roster
2763 local vMemberInfo =
2764 {
2765 Name = vName,
2766 RankIndex = vRankIndex,
2767 Level = vLevel,
2768 Class = vClass,
2769 Zone = vZone,
2770 OfficerNote = vOfficerNote,
2771 Online = vOnline
2772 };
2773  
2774 gCalendarNetwork_GuildMemberRankCache[strupper(vName)] = vMemberInfo;
2775 end
2776 end
2777  
2778 -- Dump any cached trust info
2779  
2780 gCalendarNetwork_UserTrustCache = {};
2781  
2782 return gCalendarNetwork_GuildMemberRankCache;
2783 end
2784  
2785 local gGroupCalendar_SentLoadGuildRoster = false;
2786  
2787 function CalendarNetwork_LoadGuildRosterTask()
2788 if IsInGuild() then
2789 GuildRoster();
2790 end
2791  
2792 -- Schedule another task to load the roster again
2793 -- in four minutes
2794  
2795 CalendarNetwork_QueueTask(
2796 CalendarNetwork_LoadGuildRosterTask, nil,
2797 240, "GUILDROSTER");
2798 end
2799  
2800 function CalendarNetwork_LoadGuildRoster()
2801 if not IsInGuild()
2802 or GetNumGuildMembers() > 0
2803 or gGroupCalendar_SentLoadGuildRoster then
2804 return;
2805 end
2806  
2807 gGroupCalendar_SentLoadGuildRoster = true;
2808  
2809 if gGroupCalendar_Settings.DebugInit then
2810 Calendar_DebugMessage("CalendarNetwork_LoadGuildRoster: Loading");
2811 end
2812  
2813 CalendarNetwork_LoadGuildRosterTask();
2814 end
2815  
2816 function CalendarNetwork_UserIsInSameGuild(pUserName)
2817 if not IsInGuild() then
2818 return false, nil;
2819 end
2820  
2821 -- Build the roster
2822  
2823 if GetNumGuildMembers() == 0 then
2824 CalendarNetwork_LoadGuildRoster();
2825 return false, nil; -- have to return false for now since we don't really know
2826 end
2827  
2828 -- Search for the member
2829  
2830 local vUpperUserName = strupper(pUserName);
2831 local vRosterCache = CalendarNetwork_GetGuildRosterCache();
2832 local vMemberInfo = vRosterCache[vUpperUserName];
2833  
2834 if not vMemberInfo then
2835 return false, nil;
2836 end
2837  
2838 return true, vMemberInfo.RankIndex;
2839 end
2840  
2841 function CalendarNetwork_SendAllRevisionNotices()
2842 -- Use the same delay for all the requests so that the requests will all go out together
2843 -- once they start getting processed
2844  
2845 local vDelay = gCalendarNetwork_RequestDelay.ProxyNOUMin + math.random() * gCalendarNetwork_RequestDelay.ProxyNOURange;
2846 local vOwnedDelay = math.random() * gCalendarNetwork_RequestDelay.OwnedNOURange;
2847  
2848 for vRealmName, vDatabase in gGroupCalendar_Database.Databases do
2849 if EventDatabase_DatabaseIsVisible(vDatabase) then
2850 local vRequest = {mOpcode = "DB_NOU", mUserName = vDatabase.UserName};
2851  
2852 if vDatabase.IsPlayerOwned then
2853 CalendarNetwork_QueueUniqueUserRequest(vRequest, vOwnedDelay);
2854 else
2855 CalendarNetwork_QueueUniqueUserRequest(vRequest, vDelay);
2856 end
2857  
2858 local vRequest = {mOpcode = "RAT_NOU", mUserName = vDatabase.UserName};
2859  
2860 if vDatabase.IsPlayerOwned then
2861 CalendarNetwork_QueueUniqueUserRequest(vRequest, vOwnedDelay);
2862 else
2863 CalendarNetwork_QueueUniqueUserRequest(vRequest, vDelay);
2864 end
2865 end
2866 end
2867 end
2868  
2869 function CalendarNetwork_CancelRedundantUPDRequests(pChanges, pDatabaseTag, pUserName, pDatabaseID, pRevision, pSinceRevision)
2870 local vOpcode = pDatabaseTag.."_UPD";
2871  
2872 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2873 for vIndex, vRequest in vRequests do
2874 if vRequest.mOpcode == vOpcode
2875 and vRequest.mUserName == pUserName then
2876 -- Theirs is better if:
2877 -- - Their database ID is higher
2878 -- - Their database ID is the same and
2879 -- - Their fromRevision is lower or the same
2880 -- - Their toRevision is higher or the same
2881  
2882 if pDatabaseID > vRequest.mDatabaseID
2883 or (pDatabaseID == vRequest.mDatabaseID
2884 and pSinceRevision <= vRequest.mRevision
2885 and (not pChanges or pRevision >= pChanges.Revision)) then
2886 if gGroupCalendar_Settings.DebugQueues then
2887 Calendar_DebugMessage("Removing UPD request for "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pSinceRevision.." to "..pRevision);
2888 end
2889  
2890 table.remove(vRequests, vIndex);
2891 else
2892 if gGroupCalendar_Settings.DebugQueues then
2893 Calendar_DebugMessage("Keeping UPD request for "..pDatabaseTag.." "..pUserName..","..vRequest.mDatabaseID..","..vRequest.mRevision.." to "..pChanges.Revision);
2894 Calendar_DebugMessage("Better thean "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pSinceRevision.." to "..pRevision);
2895 end
2896 end
2897  
2898 return;
2899 end
2900 end -- for vIndex
2901 end -- for vPriority
2902 end
2903  
2904 function CalendarNetwork_CancelRedundantNOURequests(pChanges, pDatabaseTag, pSender, pUserName, pDatabaseID, pRevision)
2905 local vOpcode = pDatabaseTag.."_NOU";
2906  
2907 for vPriority, vRequests in gGroupCalendar_Queue.Requests do
2908 for vIndex, vRequest in vRequests do
2909 if vRequest.mOpcode == vOpcode
2910 and vRequest.mUserName == pUserName then
2911 -- Cancel our update if the revision they're advertising is equal
2912 -- to or better than ours or if they're the owner
2913  
2914 local vTheirsIsBetter;
2915  
2916 if pSender == pUserName then
2917 vTheirsIsBetter = true;
2918 else
2919 vTheirsIsBetter = not pChanges
2920 or pDatabaseID > pChanges.ID
2921 or (pDatabaseID == pChanges.ID and pRevision >= pChanges.Revision);
2922 end
2923  
2924 if vTheirsIsBetter then
2925 if gGroupCalendar_Settings.DebugQueues then
2926 Calendar_DebugMessage("Removing NOU request for "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pRevision);
2927 end
2928  
2929 table.remove(vRequests, vIndex);
2930 else
2931 if gGroupCalendar_Settings.DebugQueues then
2932 Calendar_DebugMessage("Keeping NOU request for "..pDatabaseTag.." "..pUserName..","..pChanges.ID..","..pChanges.Revision);
2933 Calendar_DebugMessage("Better than "..pDatabaseTag.." "..pUserName..","..pDatabaseID..","..pRevision);
2934 end
2935 end
2936  
2937 return;
2938 end
2939 end -- for vIndex
2940 end -- for vPriority
2941 end
2942  
2943 function CalendarNetwork_SendMessage(pMessage)
2944 -- Just leave if there's no channel to communicate on
2945  
2946 if not gGroupCalendar_Channel.ID then
2947 return;
2948 end
2949  
2950 local vSavedAutoClearAFK = GetCVar("autoClearAFK");
2951 SetCVar("autoClearAFK", 0);
2952  
2953 SendChatMessage(Calendar_EscapeChatString(pMessage), "CHANNEL", nil, gGroupCalendar_Channel.ID);
2954  
2955 SetCVar("autoClearAFK", vSavedAutoClearAFK);
2956 end
2957  
2958 function CalendarNetwork_ChannelMessageReceived(pSender, pMessage)
2959 local vTrustLevel = CalendarTrust_GetUserTrustLevel(pSender);
2960  
2961 if vTrustLevel > 0 then
2962 CalendarNetwork_QueueInboundMessage(pSender, vTrustLevel, pMessage);
2963 else
2964 if gGroupCalendar_Settings.DebugTrust then
2965 Calendar_DebugMessage("ChannelMessageReceived: "..pSender.." is not trusted");
2966 end
2967 end
2968 end
2969  
2970 function CalendarNetwork_GetGuildMemberIndex(pPlayerName)
2971 local vUpperUserName = strupper(pPlayerName);
2972 local vNumGuildMembers = GetNumGuildMembers(true);
2973  
2974 for vIndex = 1, vNumGuildMembers do
2975 local vName = GetGuildRosterInfo(vIndex);
2976  
2977 if strupper(vName) == vUpperUserName then
2978 return vIndex;
2979 end
2980 end
2981  
2982 return nil;
2983 end
2984  
2985 function CalendarNetwork_SetAutoConfigData(pPlayerName)
2986 if not CanEditPublicNote()
2987 or not gGroupCalendar_PlayerSettings.Channel.Name then
2988 return false;
2989 end
2990  
2991 local vMemberIndex = CalendarNetwork_GetGuildMemberIndex(pPlayerName);
2992  
2993 if not vMemberIndex then
2994 return false;
2995 end
2996  
2997 CalendarNetwork_RemoveAllAutoConfigData(pPlayerName);
2998  
2999 local vNote = "["..gGroupCalendar_MessagePrefix.."C:"..gGroupCalendar_PlayerSettings.Channel.Name;
3000  
3001 if gGroupCalendar_PlayerSettings.Channel.Password then
3002 vNote = vNote..","..gGroupCalendar_PlayerSettings.Channel.Password
3003 end
3004  
3005 -- Add the trust group
3006  
3007 local vTrustGroup = CalendarTrust_GetCurrentTrustGroup();
3008  
3009 vNote = vNote.."/T:"..vTrustGroup;
3010  
3011 -- Add the trust rank
3012  
3013 if gGroupCalendar_PlayerSettings.Security.TrustGuildies
3014 and gGroupCalendar_PlayerSettings.Security.MinTrustedRank then
3015 vNote = vNote..","..gGroupCalendar_PlayerSettings.Security.MinTrustedRank;
3016 end
3017  
3018 vNote = vNote.."]";
3019  
3020 if gGroupCalendar_Settings.DebugConfig then
3021 Calendar_DebugMessage("Setting auto-config data on player "..pPlayerName);
3022 end
3023  
3024 GuildRosterSetPublicNote(vMemberIndex, vNote);
3025  
3026 return true;
3027 end
3028  
3029 function CalendarNetwork_RemoveAllAutoConfigData(pExcludePlayerName)
3030 -- Remove it from the GuildInfoText
3031  
3032 local vGuildInfoText = GetGuildInfoText();
3033 local vStartIndex, vEndIndex = string.find(vGuildInfoText, "%["..gGroupCalendar_MessagePrefix.."[^%]]+%]");
3034  
3035 if vStartIndex then
3036 vGuildInfoText = string.sub(vGuildInfoText, 1, vStartIndex - 1)..string.sub(vGuildInfoText, vEndIndex + 1);
3037 SetGuildInfoText(vGuildInfoText);
3038 end
3039  
3040 -- Remove it from player notes
3041  
3042 local vIndex = CalendarNetwork_FindAutoConfigData(nil, pExcludePlayerName);
3043  
3044 while vIndex do
3045 CalendarNetwork_RemoveAutoConfigData(vIndex);
3046 vIndex = CalendarNetwork_FindAutoConfigData(vIndex + 1, pExcludePlayerName);
3047 end
3048 end
3049  
3050 function CalendarNetwork_RemoveAutoConfigData(pMemberIndex)
3051 if not pMemberIndex then
3052 Calendar_DebugMessage("CalendarNetwork_RemoveAutoConfigData: nil index");
3053 return;
3054 end
3055  
3056 local vName, vNote = CalendarNetwork_GetGuildPublicNote(pMemberIndex);
3057  
3058 if not vNote then
3059 return false;
3060 end
3061  
3062 if gGroupCalendar_Settings.DebugConfig then
3063 Calendar_DebugMessage("Removing auto-config data from player "..vName);
3064 end
3065  
3066 local vPatternString = "%["..gGroupCalendar_MessagePrefix0.."[^%]]+%]";
3067  
3068 vNote = string.gsub(vNote, vPatternString, "");
3069  
3070 GuildRosterSetPublicNote(pMemberIndex, vNote);
3071  
3072 return true;
3073 end
3074  
3075 function CalendarNetwork_GetGuildPublicNote(pMemberIndex)
3076 local vName, vRank, vRankIndex, vLevel, vClass, vZone, vNote, vOfficerNote, vOnline = GetGuildRosterInfo(pMemberIndex);
3077  
3078 return vName, vNote;
3079 end
3080  
3081 function CalendarNetwork_GetAutoConfigData()
3082 local vGuildInfoText = GetGuildInfoText();
3083 local vConfigSource = GroupCalendar_cGuildInfoConfigSource;
3084  
3085 local vStartIndex, vEndIndex, vConfigString = string.find(vGuildInfoText, "%[("..gGroupCalendar_MessagePrefix.."[^%]]+)%]");
3086  
3087 if not vStartIndex then
3088 local vIndex = CalendarNetwork_FindAutoConfigData();
3089  
3090 if not vIndex then
3091 return nil;
3092 end
3093  
3094 local vName, vNote = CalendarNetwork_GetGuildPublicNote(vIndex);
3095  
3096 if not vNote then
3097 return nil;
3098 end
3099  
3100 vStartIndex, vEndIndex, vConfigString = string.find(vNote, "%[("..gGroupCalendar_MessagePrefix.."[^%]]+)%]");
3101  
3102 if not vStartIndex then
3103 return nil;
3104 end
3105  
3106 vConfigSource = vName;
3107 end
3108  
3109 return CalendarNetwork_ParseCommandString(vConfigString), vConfigSource;
3110 end
3111  
3112 function CalendarNetwork_FindAutoConfigData(pStartingIndex, pExcludePlayerName)
3113 -- Build the roster
3114  
3115 if GetNumGuildMembers() == 0 then
3116 CalendarNetwork_LoadGuildRoster();
3117 return nil;
3118 end
3119  
3120 -- Search for the member
3121  
3122 local vNumGuildMembers = GetNumGuildMembers(true);
3123 local vStartingIndex;
3124  
3125 if pStartingIndex then
3126 vStartingIndex = pStartingIndex;
3127 else
3128 vStartingIndex = 1;
3129 end
3130  
3131 for vIndex = vStartingIndex, vNumGuildMembers do
3132 local vName, vNote = CalendarNetwork_GetGuildPublicNote(vIndex);
3133  
3134 if vNote
3135 and (not pExcludePlayerName or string.lower(vName) ~= string.lower(pExcludePlayerName))
3136 and strfind(vNote, "%["..gGroupCalendar_MessagePrefix0) then
3137 if gGroupCalendar_Settings.DebugConfig then
3138 Calendar_DebugMessage("Found auto-config data on player "..vName);
3139 end
3140  
3141 return vIndex, vName;
3142 end
3143 end
3144  
3145 return nil;
3146 end
3147  
3148 function CalendarNetwork_ScheduleCheckDatabaseTrust(pDelay)
3149 CalendarNetwork_QueueUniqueOpcodeRequest({mOpcode = "DBTRUST"}, pDelay);
3150 end
3151  
3152 function CalendarNetwork_ScheduleAutoConfig(pDelay, pCheckDatabaseTrust)
3153 if gGroupCalendar_Channel.Disconnected then
3154 return;
3155 end
3156  
3157 local vRequest = CalendarNetwork_FindRequest({mOpcode = "AUTOCONFIG"});
3158  
3159 if vRequest then
3160 if gGroupCalendar_Settings.DebugInit then
3161 Calendar_DebugMessage("CalendarNetwork_ScheduleAutoConfig: Rescheduling existing request");
3162 end
3163  
3164 if pDelay < vRequest.mDelay then
3165 vRequest.mDelay = pDelay;
3166 end
3167  
3168 if pCheckDatabaseTrust then
3169 vRequest.mCheckDatabaseTrust = true;
3170 end
3171 else
3172 if gGroupCalendar_Settings.DebugInit then
3173 Calendar_DebugMessage("CalendarNetwork_ScheduleAutoConfig: Scheduling request");
3174 end
3175  
3176 CalendarNetwork_QueueRequest({mOpcode = "AUTOCONFIG", mCheckDatabaseTrust = pCheckDatabaseTrust}, pDelay);
3177 end
3178 end
3179  
3180 function CalendarNetwork_DoAutoConfig(pCheckDatabaseTrust)
3181 if gGroupCalendar_Settings.DebugInit then
3182 Calendar_DebugMessage("CalendarNetwork_DoAutoConfig: Performing auto-config");
3183 end
3184  
3185 local vCommand, vConfigSource = CalendarNetwork_GetAutoConfigData();
3186  
3187 if not vCommand then
3188 CalendarNetwork_SetChannelStatus("Error", GroupCalendar_cAutoConfigNotFound);
3189 return false;
3190 end
3191  
3192 local vChannel = nil;
3193 local vPassword = nil;
3194 local vMinTrustedRank = nil;
3195 local vTrustGroup = nil;
3196  
3197 while vCommand[1] ~= nil do
3198 local vOpcode = vCommand[1].opcode;
3199 local vOperands = vCommand[1].operands;
3200  
3201 table.remove(vCommand, 1);
3202  
3203 if vOpcode == "CHN"
3204 or vOpcode == "C" then
3205 vChannel = vOperands[1];
3206 vPassword = vOperands[2];
3207  
3208 elseif vOpcode == "RNK" then
3209 vMinTrustedRank = tonumber(vOperands[1]);
3210 elseif vOpcode == "T" then
3211 vTrustGroup = tonumber(vOperands[1]);
3212 vMinTrustedRank = tonumber(vOperands[2]);
3213 end
3214 end
3215  
3216 local vAutoPlayerChanged = gGroupCalendar_Channel.AutoPlayer ~= vConfigSource;
3217  
3218 gGroupCalendar_Channel.AutoPlayer = vConfigSource;
3219  
3220 if vAutoPlayerChanged then
3221 GroupCalendar_ChannelChanged(); -- Send out a change notice just so the calendar knows the player changed
3222 end
3223  
3224 CalendarNetwork_SetChannel(vChannel, vPassword);
3225  
3226 -- Update the trust settings
3227  
3228 local vTrustChanged = false;
3229 local vCurrentTrustGroup = CalendarTrust_GetCurrentTrustGroup();
3230  
3231 if vTrustGroup ~= nil then
3232 if vCurrentTrustGroup ~= vTrustGroup then
3233 vTrustChanged = true;
3234 end
3235 else
3236 vTrustGroup = vCurrentTrustGroup;
3237 end
3238  
3239 if vMinTrustedRank ~= nil then
3240 if gGroupCalendar_PlayerSettings.Security.MinTrustedRank ~= vMinTrustedRank then
3241 vTrustChanged = true;
3242 end
3243 else
3244 vMinTrustedRank = gGroupCalendar_PlayerSettings.Security.MinTrustedRank;
3245 end
3246  
3247 if vTrustChanged then
3248 CalendarTrust_SetCurrentTrustGroup(vTrustGroup, vMinTrustedRank);
3249 elseif pCheckDatabaseTrust then
3250 EventDatabase_CheckDatabaseTrust();
3251 end
3252  
3253 return true;
3254 end
3255  
3256 function CalendarTrust_GetCurrentTrustGroup()
3257 if gGroupCalendar_PlayerSettings.Security.TrustAnyone then
3258 return 2;
3259 elseif gGroupCalendar_PlayerSettings.Security.TrustGuildies then
3260 return 1;
3261 else
3262 return 0;
3263 end
3264 end
3265  
3266 function CalendarTrust_SetCurrentTrustGroup(pTrustGroup, pMinRank)
3267 if pTrustGroup == 2 then
3268 gGroupCalendar_PlayerSettings.Security.TrustAnyone = true;
3269 gGroupCalendar_PlayerSettings.Security.TrustGuildies = false;
3270 elseif pTrustGroup == 1 then
3271 gGroupCalendar_PlayerSettings.Security.TrustAnyone = false;
3272 gGroupCalendar_PlayerSettings.Security.TrustGuildies = true;
3273 else
3274 gGroupCalendar_PlayerSettings.Security.TrustAnyone = false;
3275 gGroupCalendar_PlayerSettings.Security.TrustGuildies = false;
3276 end
3277  
3278 gGroupCalendar_PlayerSettings.Security.MinTrustedRank = pMinRank;
3279  
3280 CalendarTrust_TrustSettingsChanged();
3281 end
3282  
3283 function CalendarTrust_TrustSettingsChanged()
3284 CalendarNetwork_FlushCaches();
3285  
3286 EventDatabase_CheckDatabaseTrust(); -- Delete databases owned by players we no longer trust
3287 CalendarNetwork_RequestMissingDatabases(); -- Request databases for newly trusted players
3288  
3289 if gGroupCalendar_PlayerSettings.Security.TrustAnyone then
3290 CalendarNetwork_RequestAllUpdate();
3291 else
3292 if gGroupCalendar_PlayerSettings.Security.TrustGuildies
3293 and gGroupCalendar_PlayerGuild then
3294 CalendarNetwork_RequestGuildUpdate(gGroupCalendar_PlayerGuild, gGroupCalendar_PlayerSettings.Security.MinTrustedRank);
3295 end
3296 end
3297  
3298 CalendarNetwork_SendAllRevisionNotices(); -- Send out revision notices since trusted players may want to know now
3299 end
3300  
3301 function CalendarTrust_TrustCheckingAvailable()
3302 if not gGroupCalendar_PlayerSettings.Security.TrustGuildies then
3303 return true;
3304 end
3305  
3306 -- Doesn't matter if they're not in a guild
3307  
3308 if not IsInGuild() then
3309 return true;
3310 end
3311  
3312 -- If trust is guild members only then verify that the roster has been loaded
3313  
3314 return GetNumGuildMembers() > 0;
3315 end
3316  
3317 function CalendarTrust_GetUserTrustLevel(pUserName)
3318 local vUserTrustInfo = gCalendarNetwork_UserTrustCache[pUserName];
3319  
3320 if not vUserTrustInfo then
3321 vUserTrustInfo = {};
3322 vUserTrustInfo.mTrustLevel = CalendarTrust_CalcUserTrust(pUserName);
3323 gCalendarNetwork_UserTrustCache[pUserName] = vUserTrustInfo;
3324 end
3325  
3326 return vUserTrustInfo.mTrustLevel;
3327 end
3328  
3329 function CalendarTrust_UserIsTrusted(pUserName)
3330 return CalendarTrust_GetUserTrustLevel(pUserName) == 2;
3331 end
3332  
3333 function CalendarTrust_UserIsTrustedForRSVPs(pUserName)
3334 return CalendarTrust_GetUserTrustLevel(pUserName) >= 1;
3335 end
3336  
3337 function CalendarTrust_CalcUserTrust(pUserName)
3338 -- If the user is one of our own characters, then trust them completely
3339  
3340 local vDatabase = EventDatabase_GetDatabase(pUserName, false);
3341  
3342 if vDatabase
3343 and vDatabase.IsPlayerOwned then
3344 if gGroupCalendar_Settings.DebugTrust then
3345 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: Implicit trust for "..pUserName);
3346 end
3347  
3348 return 2;
3349 end
3350  
3351 local vPlayerSecurity = gGroupCalendar_PlayerSettings.Security.Player[pUserName];
3352  
3353 -- See if they're explicity allowed/forbidden
3354  
3355 if vPlayerSecurity ~= nil then
3356 if vPlayerSecurity == 1 then
3357 -- Trusted
3358  
3359 if gGroupCalendar_Settings.DebugTrust then
3360 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: Explicit trust for "..pUserName);
3361 end
3362  
3363 return 2;
3364 elseif vPlayerSecurity == 2 then
3365 -- Excluded
3366  
3367 if gGroupCalendar_Settings.DebugTrust then
3368 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: "..pUserName.." explicity excluded");
3369 end
3370  
3371 return 0;
3372 else
3373 Calendar_DebugMessage("GroupCalendar: Unknown player security setting of "..vPlayerSecurity.." for "..pUserName);
3374 end
3375 end
3376  
3377 -- Return true if we'll allow anyone in the channel
3378  
3379 if gGroupCalendar_PlayerSettings.Security.TrustAnyone then
3380 if gGroupCalendar_Settings.DebugTrust then
3381 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: "..pUserName.." trusted (all trusted)");
3382 end
3383  
3384 return 2;
3385 end
3386  
3387 -- Return true if they're in the same guild and of sufficient rank
3388  
3389 if gGroupCalendar_PlayerSettings.Security.TrustGuildies then
3390 local vIsInGuild, vGuildRank = CalendarNetwork_UserIsInSameGuild(pUserName);
3391  
3392 if vIsInGuild then
3393 if not gGroupCalendar_PlayerSettings.Security.MinTrustedRank
3394 or vGuildRank <= gGroupCalendar_PlayerSettings.Security.MinTrustedRank then
3395 if gGroupCalendar_Settings.DebugTrust then
3396 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: "..pUserName.." trusted (guild member)");
3397 end
3398  
3399 return 2;
3400 else
3401 if gGroupCalendar_Settings.DebugTrust then
3402 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: "..pUserName.." partially trusted (guild member)");
3403 end
3404  
3405 return 1;
3406 end
3407 end
3408 end
3409  
3410 -- Failed all tests
3411  
3412 if gGroupCalendar_Settings.DebugTrust then
3413 Calendar_DebugMessage("CalendarTrust_CalcUserTrust: "..pUserName.." not trusted (all tests failed)");
3414 end
3415  
3416 return 0;
3417 end
3418  
3419 function CalendarTrust_GetNumTrustedPlayers(pTrustSetting)
3420 local vNumPlayers = 0;
3421  
3422 for vPlayerName, vPlayerSecurity in gGroupCalendar_PlayerSettings.Security.Player do
3423 if vPlayerSecurity == pTrustSetting then
3424 vNumPlayers = vNumPlayers + 1;
3425 end
3426 end
3427  
3428 return vNumPlayers;
3429 end
3430  
3431 function CalendarTrust_GetIndexedTrustedPlayers(pTrustSetting, pIndex)
3432 local vPlayerIndex = 1;
3433  
3434 for vPlayerName, vPlayerSecurity in gGroupCalendar_PlayerSettings.Security.Player do
3435 if vPlayerSecurity == pTrustSetting then
3436 if vPlayerIndex == pIndex then
3437 return vPlayerName;
3438 end
3439  
3440 vPlayerIndex = vPlayerIndex + 1;
3441 end
3442 end
3443  
3444 return nil;
3445 end
3446  
3447 function CalendarNetwork_RequestMissingDatabases()
3448 -- For each player we explicitly trust, see if there's a database for
3449 -- them yet. If not, request one
3450  
3451 for vPlayerName, vPlayerSecurity in gGroupCalendar_PlayerSettings.Security.Player do
3452 if vPlayerSecurity == 1 then
3453 -- Found a trusted player, see if they have a database
3454  
3455 local vDatabase = EventDatabase_GetDatabase(vPlayerName, false);
3456  
3457 if not vDatabase then
3458 CalendarNetwork_QueueRFURequest(vPlayerName, "DB", 0, 0);
3459 CalendarNetwork_QueueRFURequest(vPlayerName, "RAT", 0, 0);
3460 end
3461 end
3462 end
3463  
3464 return nil;
3465 end
3466  
3467 function CalendarNetwork_QueueRFURequest(pUserName, pDatabaseTag, pDatabaseID, pRevision)
3468 if CalendarNetwork_CancelRedundantRFURequest(pDatabaseTag, pUserName, pDatabaseID, pRevision) then
3469 return;
3470 end
3471  
3472 local vRequest =
3473 {
3474 mOpcode = pDatabaseTag.."_RFU",
3475 mUserName = pUserName,
3476 mDatabaseID = pDatabaseID,
3477 mRevision = pRevision,
3478 }
3479  
3480 CalendarNetwork_QueueRequest(vRequest, gCalendarNetwork_RequestDelay.RFUMin + math.random() * gCalendarNetwork_RequestDelay.RFURange);
3481 end
3482  
3483 function CalendarNetwork_PlayerGuildChanged()
3484 -- Update the guild in the database
3485  
3486 if gGroupCalendar_UserDatabase then
3487 gGroupCalendar_UserDatabase.Guild = gGroupCalendar_PlayerGuild;
3488 end
3489  
3490 -- Just return if we're not initialized yet
3491  
3492 if not gGroupCalendar_Initialized then
3493 EventDatabase_UpdateGuildRankCache();
3494 return;
3495 end
3496  
3497 -- Clear the roster load flag
3498  
3499 gGroupCalendar_SentLoadGuildRoster = false;
3500  
3501 CalendarNetwork_FlushCaches();
3502  
3503 -- If the player is unguilded then simply leave the data
3504 -- channel if it was auto-configured, flush any databases
3505 -- which are no longer trusted and exit
3506  
3507 if not IsInGuild() then
3508 if gGroupCalendar_Settings.DebugInit then
3509 Calendar_DebugMessage("PlayerGuildChanged: Player is now unguilded");
3510 end
3511  
3512 if gGroupCalendar_PlayerSettings.Channel.AutoConfig then
3513 CalendarNetwork_LeaveChannel();
3514 end
3515  
3516 EventDatabase_CheckDatabaseTrust();
3517  
3518 return;
3519 end
3520  
3521 -- The player is in a new guild or has changed guilds, so
3522 -- schedule a roster update if necessary
3523  
3524 if GetNumGuildMembers() > 0 then
3525 if gGroupCalendar_Settings.DebugInit then
3526 Calendar_DebugMessage("PlayerGuildChanged: Roster is already loaded, calling GuildRosterChanged()");
3527 end
3528  
3529 CalendarNetwork_GuildRosterChanged();
3530 end
3531  
3532 -- Force the roster to reload or to start loading
3533  
3534 CalendarNetwork_LoadGuildRosterTask();
3535 end
3536  
3537 function CalendarNetwork_GuildRosterChanged()
3538 if gGroupCalendar_Settings.DebugInit then
3539 Calendar_DebugMessage("CalendarNetwork_GuildRosterChanged: Checking changes");
3540 end
3541  
3542 EventDatabase_UpdateGuildRankCache();
3543  
3544 CalendarNetwork_FlushCaches();
3545  
3546 if gGroupCalendar_PlayerSettings.Channel.AutoConfig then
3547 CalendarNetwork_ScheduleAutoConfig(gCalendarNetwork_RequestDelay.GuildUpdateAutoConfig, true);
3548 else
3549 if gGroupCalendar_Settings.DebugInit then
3550 Calendar_DebugMessage("PlayerGuildChanged: Channel is set for manual config, verifying database trust");
3551 end
3552  
3553 CalendarNetwork_ScheduleCheckDatabaseTrust(5);
3554 end
3555  
3556 -- Start sending notices now if we were waiting for a roster update
3557  
3558 if gGroupCalendar_SendNoticesOnRosterUpdate then
3559 gGroupCalendar_SendNoticesOnRosterUpdate = nil;
3560 CalendarNetwork_SendNotices();
3561 end
3562 end
3563  
3564 function CalendarNetwork_CheckPlayerGuild()
3565 local vPlayerGuild;
3566  
3567 if IsInGuild() then
3568 vPlayerGuild, _, gGroupCalendar_PlayerGuildRank = GetGuildInfo("player");
3569  
3570 -- Just return if the server is lagging and the guild info
3571 -- isn't available yet
3572  
3573 if not vPlayerGuild then
3574 return;
3575 end
3576 else
3577 vPlayerGuild = nil;
3578 gGroupCalendar_PlayerGuildRank = nil;
3579 end
3580  
3581 if gGroupCalendar_PlayerGuild ~= vPlayerGuild then
3582 gGroupCalendar_PlayerGuild = vPlayerGuild;
3583  
3584 CalendarNetwork_PlayerGuildChanged();
3585 end
3586 end