/// <summary> /// Adds an objective to the notifications collection, and then removes the objective 10 seconds later /// </summary> private void DisplayNotification(WvWObjectiveViewModel objectiveData) { if (this.CanShowNotification(objectiveData)) { Task.Factory.StartNew(() => { logger.Debug("Adding notification for \"{0}\" in {1}", objectiveData.Name, objectiveData.Map); Threading.InvokeOnUI(() => this.WvWNotifications.Add(objectiveData)); // For 10 seconds, loop and sleep, with checks to see if notifications have been disabled for (int i = 0; i < 40; i++) { System.Threading.Thread.Sleep(250); if (!this.CanShowNotification(objectiveData)) { logger.Debug("Removing notification for \"{0}\" in {1}", objectiveData.Name, objectiveData.Map); Threading.InvokeOnUI(() => this.WvWNotifications.Remove(objectiveData)); } } logger.Debug("Removing notification for \"{0}\" in {1}", objectiveData.Name, objectiveData.Map); // TODO: I hate having this here, but due to a limitation in WPF, there's no reasonable way around this at this time // This makes it so that the notifications can fade out before they are removed from the notification window Threading.InvokeOnUI(() => objectiveData.IsRemovingNotification = true); System.Threading.Thread.Sleep(250); Threading.InvokeOnUI(() => { this.WvWNotifications.Remove(objectiveData); objectiveData.IsRemovingNotification = false; }); }, TaskCreationOptions.LongRunning); } }
/// <summary> /// Initializes the collection of world cycles /// </summary> private void InitializeCycles() { lock (refreshTimerLock) { logger.Debug("Initializing world events"); this.cyclesService.LoadTable(); Threading.InvokeOnUI(() => { foreach (var cycle in this.cyclesService.TimeTable.Cycles) { //logger.Debug("Loading localized name for {0}", cycle.ID); //cycle.Name = this.cyclesService.GetLocalizedName(cycle.ID); logger.Debug("Initializing view model for {0}", cycle.ID); this.Cycles.Add(new CycleViewModel(cycle, this.userData, this.CycleNotifications)); // If the user data does not contain this cycle, add it to that collection as well var ens = this.UserData.NotificationSettings.FirstOrDefault(ns => ns.CycleID == cycle.ID); if (ens == null) { this.UserData.NotificationSettings.Add(new CycleNotificationSettings(cycle.ID) { CycleName = cycle.Name }); } else { ens.CycleName = cycle.Name; } } }); } }
/// <summary> /// Initializes the collection of world events /// </summary> private void InitializeWorldEvents() { lock (refreshTimerLock) { logger.Debug("Initializing world events"); this.eventsService.LoadTable(this.UserData.UseAdjustedTimeTable); Threading.InvokeOnUI(() => { foreach (var worldEvent in this.eventsService.EventTimeTable.WorldEvents) { logger.Debug("Loading localized name for {0}", worldEvent.ID); worldEvent.Name = this.eventsService.GetLocalizedName(worldEvent.ID); logger.Debug("Initializing view model for {0}", worldEvent.ID); this.WorldEvents.Add(new EventViewModel(worldEvent, this.userData, this.EventNotifications)); // If the user data does not contain this event, add it to that collection as well var ens = this.UserData.NotificationSettings.FirstOrDefault(ns => ns.EventID == worldEvent.ID); if (ens == null) { this.UserData.NotificationSettings.Add(new EventNotificationSettings(worldEvent.ID) { EventName = worldEvent.Name }); } else { ens.EventName = worldEvent.Name; } } }); } }
/// <summary> /// Recalculates/refreshes all calculated distances /// </summary> private void CalculateDistances() { // Calculate time distances for all objectives, based on the player's position, if the player is in the same map as the objective // Note: these are approximations at best if (this.playerService.HasValidMapId) { var playerPosition = CalcUtil.ConvertToMapPosition(this.playerService.PlayerPosition); Threading.InvokeOnUI(() => { foreach (var objective in this.CurrentObjectives) { if (this.PlayerMap == objective.Map) { if (playerPosition != null && objective.ModelData.MapLocation != null) { objective.DistanceFromPlayer = Math.Round(CalcUtil.CalculateDistance(playerPosition, objective.ModelData.MapLocation, this.UserData.DistanceUnits)); } } else { objective.DistanceFromPlayer = 0; } } }); } }
/// <summary> /// Removes this event from the collection of displayed notifications /// </summary> private void CloseNotification() { // Removing... set flag that this was shown switch (this.NotificationType) { case PriceNotificationType.BuyOrder: this.PriceWatch.IsBuyOrderNotificationShown = true; break; case PriceNotificationType.SellListing: this.PriceWatch.IsSellListingNotificationShown = true; break; default: break; } Task.Factory.StartNew(() => { Threading.InvokeOnUI(() => this.IsRemovingNotification = true); System.Threading.Thread.Sleep(250); Threading.InvokeOnUI(() => { this.displayedNotifications.Remove(this); this.IsRemovingNotification = false; }); }); }
/// <summary> /// Initializes the WvW teams collection /// </summary> private void InitializeWorlds() { lock (objectivesRefreshTimerLock) { logger.Debug("Initializing worlds"); Threading.InvokeOnUI(() => this.Worlds.Clear()); var matchIDs = this.wvwService.GetMatchIDs(); var teamColors = this.wvwService.GetTeamColors(); foreach (var world in this.wvwService.Worlds) { var team = new WvWTeamViewModel(world); if (matchIDs.ContainsKey(team.WorldId)) { team.MatchId = matchIDs[team.WorldId]; } if (teamColors.ContainsKey(team.WorldId)) { team.Color = teamColors[team.WorldId]; } Threading.InvokeOnUI(() => this.Worlds.Add(team)); } // Launch a task to refresh world score // Do this with a background task so we don't slow down initialization (this is not critical) Task.Factory.StartNew(this.RefreshWorldScores); } }
/// <summary> /// Initializes the collection of price watches /// </summary> private void InitializeItemPrices() { // If for some reason there are items with an ID of 0, just remove them now var itemsToRemove = new List <PriceWatch>(this.UserData.PriceWatches.Where(pw => pw.ItemID == 0)); foreach (var emptyItem in itemsToRemove) { this.UserData.PriceWatches.Remove(emptyItem); } if (this.UserData.PriceWatches.Count > 0) { var itemIds = this.UserData.PriceWatches.Select(pw => pw.ItemID); var itemData = this.commerceService.GetItems(itemIds.ToArray()); // Initialize view models Threading.InvokeOnUI(() => { foreach (var priceWatch in this.UserData.PriceWatches) { if (priceWatch.ItemID > 0) { this.ItemPrices.Add(new ItemPriceViewModel(priceWatch, itemData[priceWatch.ItemID], this, this.commerceService)); } else { this.ItemPrices.Add(new ItemPriceViewModel(priceWatch, null, this, this.commerceService)); } } }); } }
/// <summary> /// Primary method for refreshing the current zone /// </summary> /// <param name="state"></param> private void RefreshZone(object state = null) { lock (zoneRefreshTimerLock) { if (this.isStopped) { return; // Immediately return if we are supposed to be stopped } Threading.BeginInvokeOnUI(() => this.ValidMapID = this.playerService.HasValidMapId); if (this.systemService.IsGw2Running && this.playerService.HasValidMapId) { // Check to see if the MapId or Character Name has changed, if so, we need to clear our zone items and add the new ones if (this.CurrentMapID != this.playerService.MapId || this.CharacterName != this.playerService.CharacterName) { logger.Info("Map/Character change detected, resetting zone events. New MapID = {0} | Character Name = {1}", this.playerService.MapId, this.CharacterName); this.CurrentMapID = this.playerService.MapId; this.CharacterName = this.playerService.CharacterName; var continent = this.zoneService.GetContinentByMap(this.CurrentMapID); var map = this.zoneService.GetMap(this.CurrentMapID); Threading.BeginInvokeOnUI(() => { this.ActiveContinent = continent; this.ActiveMap = map; }); var zoneItems = this.zoneService.GetZoneItems(this.playerService.MapId); lock (zoneItemsLock) { Threading.InvokeOnUI(() => { this.ZoneItems.Clear(); this.playerInProximityCounters.Clear(); foreach (var item in zoneItems) { // Ignore dungeons for now if (item.Type != API.Data.Enums.ZoneItemType.Dungeon) { this.ZoneItems.Add(new ZoneItemViewModel(item, this.playerService, this.UserData)); this.playerInProximityCounters.Add(item.ID, 0); } } }); } // Update the current zone name var newZoneName = this.zoneService.GetZoneName(this.CurrentMapID); if (this.zoneNameObject.ZoneName != newZoneName) { Threading.InvokeOnUI(() => this.zoneNameObject.ZoneName = newZoneName); } logger.Info("New Zone Name = {0}", newZoneName); } } this.zoneRefreshTimer.Change(this.ZoneRefreshInterval, Timeout.Infinite); } }
/// <summary> /// Initializes the store of zone items with data /// </summary> public void InitializeStore() { this.zoneService.Initialize(); var continents = this.zoneService.GetContinents(); foreach (var continent in continents) { this.Data.Add(continent.Id, new ContinentZoneItems(continent.Id)); } foreach (var continent in this.Data) { var zoneItems = this.zoneService.GetZoneItemsByContinent(continent.Key); Threading.InvokeOnUI(() => continent.Value.Clear()); foreach (var entity in zoneItems) { var zoneItem = new ZoneItemViewModel(entity, this.playerService, this.zoneUserData); Threading.InvokeOnUI(() => { continent.Value.Add(zoneItem); }); } } Threading.BeginInvokeOnUI(() => this.RaiseDataLoadedEvent()); }
/// <summary> /// Imports all tasks from the given path /// </summary> /// <param name="path">The path to import from</param> public void ImportTasks(string path) { logger.Info("Importing tasks from {0}", path); XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection <PlayerTask>)); object loadedTasks = null; try { using (TextReader reader = new StreamReader(path)) { loadedTasks = deserializer.Deserialize(reader); } Threading.InvokeOnUI(() => { foreach (var task in (ObservableCollection <PlayerTask>)loadedTasks) { task.IsAccountCompleted = false; this.UserData.Tasks.Add(task); this.PlayerTasks.Add(new PlayerTaskViewModel(task, this.zoneService, this, this.container)); } }); logger.Info("Successfully imported tasks from {0}", path); } catch (Exception ex) { logger.Error("Unable to import tasks!"); logger.Error(ex); } }
/// <summary> /// Connection was refused, meaning teamspeak is not running /// </summary> private void TeamspeakService_ConnectionRefused(object sender, EventArgs e) { this.TeamspeakService.ConnectionRefused -= TeamspeakService_ConnectionRefused; Task.Factory.StartNew(() => { var cannotConnectNotification = new TSNotificationViewModel(0, Properties.Resources.StartTeamspeak, TSNotificationType.CannotConnect); Threading.InvokeOnUI(() => this.Notifications.Add(cannotConnectNotification)); // Start a loop attempting to connect once every 5 seconds int sleepTime = 250; // ms int retryInterval = 5000 / sleepTime; int i = 0; while (!this.isShuttingDown && this.TeamspeakService.ConnectionState != TS3.Data.Enums.ConnectionState.Connected) { Thread.Sleep(250); i++; if (i > retryInterval) { Threading.InvokeOnUI(() => this.TeamspeakService.Connect()); i = 0; } } if (!this.isShuttingDown) { this.TeamspeakService.ConnectionRefused += TeamspeakService_ConnectionRefused; Threading.InvokeOnUI(() => this.Notifications.Remove(cannotConnectNotification)); } }, TaskCreationOptions.LongRunning); }
/// <summary> /// Adds an event to the event notifications collection, and then removes the event 10 seconds later /// </summary> private void DisplayEventNotification(EventViewModel eventData) { if (this.UserSettings.AreEventNotificationsEnabled) { Task.Factory.StartNew(() => { logger.Debug("Adding notification for \"{0}\"", eventData.EventName); Threading.InvokeOnUI(() => this.EventNotifications.Add(eventData)); // For 10 seconds, loop and sleep, with checks to see if notifications have been disabled for (int i = 0; i < 40; i++) { System.Threading.Thread.Sleep(250); if (!this.UserSettings.AreEventNotificationsEnabled) { logger.Debug("Removing notification for \"{0}\"", eventData.EventName); Threading.InvokeOnUI(() => this.EventNotifications.Remove(eventData)); } } logger.Debug("Removing notification for \"{0}\"", eventData.EventName); // TODO: I hate having this here, but due to a limitation in WPF, there's no reasonable way around this at this time // This makes it so that the notifications can fade out before they are removed from the notification window Threading.InvokeOnUI(() => eventData.IsRemovingNotification = true); System.Threading.Thread.Sleep(250); Threading.InvokeOnUI(() => { this.EventNotifications.Remove(eventData); eventData.IsRemovingNotification = false; }); }, TaskCreationOptions.LongRunning); } }
/// <summary> /// Initializes the WvW teams collection /// </summary> private void InitializeWorlds() { lock (objectivesRefreshTimerLock) { logger.Debug("Initializing worlds"); Threading.InvokeOnUI(() => this.Worlds.Clear()); var matchIDs = this.wvwService.GetMatchIDs(); var teamColors = this.wvwService.GetTeamColors(); foreach (var world in this.wvwService.Worlds) { var team = new WvWTeamViewModel(world); if (matchIDs.ContainsKey(team.WorldId)) { team.MatchId = matchIDs[team.WorldId]; } if (teamColors.ContainsKey(team.WorldId)) { team.Color = teamColors[team.WorldId]; } Threading.InvokeOnUI(() => this.Worlds.Add(team)); } } }
/// <summary> /// Initializes the collection of world boss events and meta events /// </summary> private void InitializeEventModels() { lock (refreshTimerLock) { logger.Debug("Initializing World Boss events"); Threading.InvokeOnUI(() => { foreach (var worldEvent in this.eventsService.WorldBossEventTimeTable.WorldEvents) { logger.Debug("Initializing view model for {0}", worldEvent.ID); this.WorldBossEvents.Add(new WorldBossEventViewModel(worldEvent, this.userData)); } }); logger.Debug("Initializing Meta Events"); Threading.InvokeOnUI(() => { foreach (var metaEvent in this.eventsService.MetaEventsTable.MetaEvents) { logger.Debug("Initializing view models for {0}", metaEvent.ID); this.MetaEvents.Add(new MetaEventViewModel(metaEvent, this.userData)); } }); } }
/// <summary> /// Adds a new task to the collection of player tasks /// </summary> /// <param name="task">The task to add</param> public void AddOrUpdateTask(PlayerTask task) { // Lock so the refresh thread doesn't use the collection while we are modifying it lock (this.refreshLock) { Threading.InvokeOnUI(() => { var existingTask = this.PlayerTasks.FirstOrDefault(t => t.Task.ID == task.ID); if (existingTask == null) { this.UserData.Tasks.Add(task); this.PlayerTasks.Add(new PlayerTaskViewModel(task, zoneService, this, this.container)); } else { existingTask.Task.Name = task.Name; existingTask.Task.Description = task.Description; existingTask.Task.IsCompletable = task.IsCompletable; existingTask.Task.IsAccountCompleted = task.IsAccountCompleted; existingTask.Task.IsCompletedPerCharacter = task.IsCompletedPerCharacter; existingTask.Task.IsDailyReset = task.IsDailyReset; existingTask.Task.AutoComplete = task.AutoComplete; existingTask.Task.Location = task.Location; existingTask.Task.MapID = task.MapID; existingTask.Task.IconUri = task.IconUri; existingTask.Task.WaypointCode = task.WaypointCode; foreach (var character in task.CharacterCompletions.Keys) { existingTask.Task.CharacterCompletions.Add(character, task.CharacterCompletions[character]); } } }); } }
/// <summary> /// Initializes the All Objectives collection /// </summary> private void InitializeAllObjectivesCollection() { lock (objectivesRefreshTimerLock) { logger.Debug("Initializing objectives"); Threading.InvokeOnUI(() => this.AllObjectives.Clear()); // Determine the current match. If this changes, we don't need to re-initialize since the actual objectives don't change - just the owners change var matchID = this.wvwService.GetMatchId(this.UserData.WorldSelection.ID); var objectives = this.wvwService.GetAllObjectives(matchID); while (objectives.Count() == 0 && this.startCallCount > 0) { // If we started up while in the middle of a reset, the objectives count will return 0, so loop until we get it Thread.Sleep(1000); matchID = this.wvwService.GetMatchId(this.UserData.WorldSelection.ID); objectives = this.wvwService.GetAllObjectives(matchID); } Threading.InvokeOnUI(() => { foreach (var obj in objectives) { logger.Debug("Initializing view model for {0} - {1}", obj.Name, obj.Map); var vm = new WvWObjectiveViewModel(obj, this.UserData, this.Worlds, this.WvWNotifications); this.AllObjectives.Add(vm); } }); } }
/// <summary> /// Handles the New Channel Info event of the Teamspeak Service /// </summary> private void TeamspeakService_ClientChannelChanged(object sender, TS3.Data.ChannelEventArgs e) { Threading.InvokeOnUI(() => { this.ClientChannelName = e.Channel.Name; this.ClientChannelDescription = e.Channel.Description; }); }
/// <summary> /// Closes the browser window /// </summary> public void CloseBrowser() { #if !NO_BROWSER if (this.browser != null && this.browser.IsVisible) { Threading.InvokeOnUI(() => this.browser.Close()); } #endif }
/// <summary> /// Handles the New Server Info event of the Teamspeak Service /// </summary> private void TeamspeakService_NewServerInfo(object sender, TS3.Data.NewServerInfoEventArgs e) { Threading.InvokeOnUI(() => { this.ServerName = e.ServerName; this.ServerAddress = e.ServerAddress; this.ChatMessages.Clear(); this.Notifications.Clear(); }); }
/// <summary> /// Handler for the Client Exited Channel event /// </summary> private void TeamspeakService_ClientExitedChannel(object sender, TS3.Data.ClientEventArgs e) { Task.Factory.StartNew(() => { var notification = new TSNotificationViewModel(e.ClientID, e.ClientName, TSNotificationType.UserExited); Threading.InvokeOnUI(() => this.Notifications.Add(notification)); Thread.Sleep(5000); // Let channel notifications stay for 5 seconds Threading.InvokeOnUI(() => this.Notifications.Remove(notification)); }); }
/// <summary> /// Handles the text message received event /// </summary> private void TeamspeakService_TextMessageReceived(object sender, TS3.Data.TextMessageEventArgs e) { Task.Factory.StartNew(() => { var messageNotification = new TSNotificationViewModel(e.ClientID, e.ClientName, TSNotificationType.Text, e.Message); Threading.InvokeOnUI(() => this.Notifications.Add(messageNotification)); Thread.Sleep(10000); // Let text messages stay for 10 seconds Threading.InvokeOnUI(() => this.Notifications.Remove(messageNotification)); }); }
/// <summary> /// Shuts down the settings view controller /// </summary> public void Shutdown() { if (this.settingsView != null && !this.settingsView.IsClosed) { Threading.InvokeOnUI(() => { this.settingsView.Close(); this.settingsView = null; }); } }
/// <summary> /// Performs actions required when the player enters WvW /// </summary> private void OnPlayerEnteredWvW() { this.isPlayerInWvW = true; if (this.userData.AutoOpenCloseTracker) { Threading.InvokeOnUI(() => { this.DisplayWvWTracker(); }); } }
/// <summary> /// Sets the active path as the path with the given ID /// </summary> /// <param name="pathId">The ID of the path to set as the active path</param> public void SetActivePath(Guid pathId) { if (this.DungeonTimerData.CurrentDungeon != null) { var path = this.DungeonTimerData.CurrentDungeon.Paths.FirstOrDefault(p => p.PathId == pathId); if (path != null) { logger.Trace("Setting active dungeon path: {0}", path.DisplayName); Threading.InvokeOnUI(() => this.DungeonTimerData.CurrentPath = path); } } }
/// <summary> /// Closes all windows and saves the "was previously opened" state for those windows. /// </summary> public void Shutdown() { logger.Debug("Shutting down"); if (this.zoneCompletionAssistantView != null) { Properties.Settings.Default.IsZoneAssistantOpen = this.zoneCompletionAssistantView.IsVisible; Threading.InvokeOnUI(() => this.zoneCompletionAssistantView.Close()); } Properties.Settings.Default.Save(); }
/// <summary> /// Closes all windows and saves the "was previously opened" state for those windows. /// </summary> public void Shutdown() { logger.Debug("Shutting down"); if (this.mapView != null) { Properties.Settings.Default.IsMapOpen = this.mapView.IsVisible; Threading.InvokeOnUI(() => this.mapView.Close()); } Properties.Settings.Default.Save(); }
/// <summary> /// Attempts to determine the path that the player is in, using the given dungeon VM /// </summary> /// <param name="dungeonVm">The dungeon VM to use when determining the current path</param> private void DeterminePath(DungeonViewModel dungeonVm) { foreach (var path in dungeonVm.Paths) { if (this.IsPlayerInPath(path)) { logger.Trace("Dungeon path detected: {0}", path.DisplayName); Threading.InvokeOnUI(() => this.DungeonTimerData.CurrentPath = path); break; } } }
/// <summary> /// Performs actions to handle a match change (either the match ended or the user switched matches) /// </summary> /// <param name="newMatchID">The new match ID</param> private void HandleMatchChange(string newMatchID) { logger.Info("Match change detected: new matchID = {0}", newMatchID); this.matchID = newMatchID; if (matchID == null) { // Unable to retrieve the current match ID, which means a reset is probably occuring // When this happens, clear out the state of everything Threading.InvokeOnUI(() => { foreach (var objective in this.AllObjectives) { objective.PrevWorldOwner = WorldColor.None; objective.WorldOwner = WorldColor.None; objective.FlipTime = DateTime.UtcNow; objective.DistanceFromPlayer = 0; objective.TimerValue = TimeSpan.Zero; objective.IsRIActive = false; } }); } else { // Refresh all team colors var teamColors = this.wvwService.GetTeamColors(); Threading.InvokeOnUI(() => { foreach (var team in this.Worlds) { team.Color = teamColors[team.WorldId]; } }); // Refresh state of all objectives var latestObjectivesData = this.wvwService.GetAllObjectives(matchID); Threading.InvokeOnUI(() => { foreach (var objective in this.AllObjectives) { var latestData = latestObjectivesData.First(obj => obj.ID == objective.ID); objective.ModelData.MatchId = this.matchID; objective.PrevWorldOwner = latestData.WorldOwner; objective.WorldOwner = latestData.WorldOwner; objective.FlipTime = DateTime.UtcNow; objective.DistanceFromPlayer = 0; objective.TimerValue = TimeSpan.Zero; objective.IsRIActive = false; } }); } }
private void SetAllOverlayWindowsVisibility(Visibility visibility) { Threading.InvokeOnUI(() => { foreach (Window window in this.OwnedWindows) { if (window is OverlayWindow && ((OverlayWindow)window).SupportsAutoHide) { window.Visibility = visibility; } } }); }
/// <summary> /// Rebuilds the current objectives collections /// </summary> private void RebuildCurrentObjectivesCollection(WvWMap map) { logger.Debug("Building objectives collection"); Threading.InvokeOnUI(() => { this.CurrentObjectives.Clear(); foreach (var objective in this.AllObjectives.Where(obj => obj.Map == map)) { this.CurrentObjectives.Add(objective); } }); }