internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100) { var corruptedGroups = new List <string>(); var bubbleGroupsLocations = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories); var bubbleGroupsLocationsSorted = bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList(); foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted) { String groupId = null; try { var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation); var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal); var serviceName = groupHeader.Substring(0, groupDelimeter); groupId = groupHeader.Substring(groupDelimeter + 1); var service = ServiceManager.GetByName(serviceName); if (service == null) { Utils.DebugPrint("Service " + serviceName + " not found in AllServices!"); continue; } var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup); if (deserializedBubbleGroup == null) { throw new Exception("DeserializedBubbleGroup is nothing."); } BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup); } catch (Exception ex) { Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex); File.Delete(bubbleGroupLocation); if (groupId != null) { corruptedGroups.Add(groupId); } } } var migrationResaveNeeded = false; var corruptedUnifiedGroups = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); var removeFromRuntime = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); foreach (var group in UnifiedBubbleGroupsDatabase) { var innerGroupCorrupt = false; var innerGroups = new List <BubbleGroup>(); foreach (var innerGroupId in @group.Serializable.GroupIds) { var innerGroup = BubbleGroupManager.Find(innerGroupId); if (innerGroup == null) { Utils.DebugPrint("Unified group, inner group " + innerGroupId + " could not be related."); if (corruptedGroups.Contains(innerGroupId)) { Utils.DebugPrint( "It was detected that this inner group was corrupted and deleted. Will delete unified group."); innerGroupCorrupt = true; corruptedUnifiedGroups.Add(@group); } } else { innerGroups.Add(innerGroup); } } if (!innerGroups.Any()) { Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime."); removeFromRuntime.Add(@group); continue; } if (innerGroupCorrupt) { continue; } var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId); if (primaryGroup == null) { Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId + " could not be related. Removing from runtime."); removeFromRuntime.Add(@group); continue; } var id = @group.Serializable.Id; var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id); if (id == null) { migrationResaveNeeded = true; @group.Serializable.Id = unifiedGroup.ID; } var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId); if (sendingGroup != null) { unifiedGroup.SendingGroup = sendingGroup; } @group.Object = unifiedGroup; BubbleGroupManager.BubbleGroupsAdd(unifiedGroup); } if (removeFromRuntime.Any()) { foreach (var group in removeFromRuntime) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (corruptedUnifiedGroups.Any()) { foreach (var group in corruptedUnifiedGroups) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (migrationResaveNeeded) { Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups."); UnifiedBubbleGroupsDatabase.SaveChanges(); } try { foreach (var groupCache in BubbleGroupCacheManager.Load()) { var associatedGroup = BubbleGroupManager.Find(groupCache.Guid); if (associatedGroup == null) { continue; } var unifiedGroup = associatedGroup as UnifiedBubbleGroup; if (unifiedGroup != null) { associatedGroup = unifiedGroup.PrimaryGroup; } associatedGroup.Title = groupCache.Name; associatedGroup.Photo = groupCache.Photo; associatedGroup.IsPhotoSetInitiallyFromCache = true; if (groupCache.Participants != null) { associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection(); foreach (var participant in associatedGroup.Participants) { participant.IsPhotoSetInitiallyFromCache = true; } } } } catch (Exception ex) { Utils.DebugPrint("Failed to load bubble groups!: " + ex); } BubbleGroupSettingsManager.Load(); }
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = false; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } somethingSynced = true; try { ReSync: var syncTime = Time.GetNowUnixTimestamp(); var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { if (BubbleGroupManager.LastBubbleSentTimestamps.ContainsKey(groupToSync.ID)) { lock (BubbleGroupManager.LastBubbleSentTimestamps) { var lastBubbleSentTime = BubbleGroupManager.LastBubbleSentTimestamps[groupToSync.ID]; if (lastBubbleSentTime >= syncTime) { Utils.DebugPrint("Sync has detected that a bubble was sent during the time the sync was fetched. Redoing sync..."); goto ReSync; } } } Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); // If a purge is issued, then we need to carry over all sending bubbles to the new BubbleGroup being created. // Additionally, we need to remove any sending bubbles that are already on the insert list to avoid duplicates. var sendingBubbles = BubbleManager.FetchAllSending(groupToSync).ToList(); var sendingBubblesFiltered = new List <VisualBubble>(); foreach (var sendingBubble in sendingBubbles) { var hasInsertBubble = syncResult.Inserts.FirstOrDefault(x => x.ID == sendingBubble.ID) != null; if (!hasInsertBubble) { sendingBubblesFiltered.Add(sendingBubble); } } if (sendingBubblesFiltered.Any()) { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, sendingBubblesFiltered.ToArray(), SearchDepth); } } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); } if (!syncResult.NullActionId) { Database.SetActionId(groupToSync, syncResult.NewActionId); } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { // Here we need to replace all the sending or downloading bubbles with the 'real' ones. // If this is never done, then on the UI, a 'sending' will never be set to 'sent', and any download // progress will never be updated. Why? Different memory objects. var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }
internal static async void UpdatePartyParticipants(BubbleGroup bubbleGroup, Action finished = null) { var service = bubbleGroup.Service; if (!ServiceManager.IsRunning(service)) { return; } if (!bubbleGroup.IsParty) { if (finished != null) { finished(); } return; } try { await service.GetBubbleGroupPartyParticipants(bubbleGroup, participants => { // we need to propogate the old participant photos to the new participant list var newParticipants = participants == null ? new List <DisaParticipant>() : participants.ToList(); foreach (var oldParticipant in bubbleGroup.Participants) { var newParticipant = newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, oldParticipant.Address)); if (newParticipant != null) { newParticipant.Photo = oldParticipant.Photo; newParticipant.IsPhotoSetFromService = oldParticipant.IsPhotoSetFromService; } } // move all unknown participants over // if there are more than 100 unknown participants, we'll ignore and force unknown participants to be rebuilt // to prevent an exceedingly large cache if (bubbleGroup.Participants.Count(x => x.Unknown) < 100) { foreach (var unknownParticipant in bubbleGroup.Participants.Where(x => x.Unknown)) { if (newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, unknownParticipant.Address)) == null) { newParticipants.Add(unknownParticipant); } } } bubbleGroup.Participants = new ThreadSafeList <DisaParticipant>(newParticipants); bubbleGroup.IsParticipantsSetFromService = true; if (finished == null) { BubbleGroupEvents.RaiseBubblesUpdated(bubbleGroup); } else { finished(); } }); } catch (Exception ex) { Utils.DebugPrint("Error updating bubble group participants: " + service.Information.ServiceName + " - " + bubbleGroup.Address + ": " + ex); if (finished != null) { finished(); } } }