public NotifyEventArgs(NotifyState AState, int AFirst, int ALast, bool AUpdate) { this.State = AState; this.FirstChanged = AFirst; this.LastChanged = ALast; this.Update = AUpdate; }
// When the NotifyState property changes, perform the relevant logic on the notification TabControl private static void NotifyStatePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { NotifyState oldState = (NotifyState)e.OldValue; NotifyState state = (NotifyState)e.NewValue; NotificationControl control = sender as NotificationControl; if (control == null) { return; } switch (state) { case NotifyState.Collapsed: control.NotifyCollapse(oldState); break; case NotifyState.Docked: control.NotifyDock(oldState); break; case NotifyState.Visible: control.NotifyVisible(oldState); break; } }
/// <summary> /// 判断用户的通知设置 /// </summary> /// <param name="userID">用户</param> /// <param name="notifyType">类型</param> /// <returns></returns> private bool CheckUserNotifySettings(int userID, int notifyType) { NotifyState SysState = AllSettings.Current.NotifySettings.GetNotifySystemState(notifyType); SystemNotifyProvider.Update(); //判断系统设置 switch (SysState) { case NotifyState.AlwaysClose: return(false); case NotifyState.DefaultClose: case NotifyState.DefaultOpen: //判断用户设置 UserNotifySetting userSetting = UserBO.Instance.GetNotifySetting(userID); if (userSetting != null && userSetting.GetNotifyState(notifyType) == NotifyState.DefaultClose) { return(false); } break; } return(true); }
void EnterState(NotifyState ns) { if (ns != currs) { OnLeave(currs); InitState(ns); } }
private void OnEnter(NotifyState ns) { switch (ns) { case NotifyState.eWidth: obj.SetHint(true); break; } }
private void OnLeave(NotifyState ns) { switch (ns) { case NotifyState.eFail: obj.SetHint(false); break; } }
/// <summary> /// Depending on NotifyState, translate the passed height /// </summary> public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length != 2 || !(values[0] is double) || !(values[1] is NotifyState)) { return(0); } double height = (double)values[0]; NotifyState state = (NotifyState)values[1]; return(NotificationControl.GetHeight(height, state)); }
// helper method to collapse the notification window private void NotifyCollapse(NotifyState oldValue) { if (oldValue == NotifyState.Visible) { layer1.Visibility = Visibility.Collapsed; } else if (oldValue == NotifyState.Docked) { NotifyVisible(oldValue); layer1.Visibility = Visibility.Collapsed; } }
/// <summary> /// Helper method to get height based on the current state of the notification window. /// When state is Docked, then use SplitterHeight, else use smaller 5.0 /// </summary> /// <param name="height"></param> /// <param name="state"></param> /// <returns></returns> internal static double GetHeight(double height, NotifyState state) { if (state == NotifyState.Docked) { return(height); } else { // reduce the splitter height return(5.0); } }
// helper method to dock the notification window private void NotifyDock(NotifyState oldValue) { if (oldValue == NotifyState.Collapsed) { NotifyVisible(oldValue); } layer0.RowDefinitions.Add(row1CloneForLayer0); layer0.Margin = new Thickness(0, 0, 0, -1); bottomPanel.Children.Remove(tabPanel); border.Child = tabPanel; // remove the rotation in the image pinImage.RenderTransform = null; // add (arbitrary not null) Tag to gridSplitter so that style trigger displays the splitter gridSplitter.Tag = gridSplitter; }
// helper method to make visible the notification window private void NotifyVisible(NotifyState oldValue) { if (oldValue == NotifyState.Docked) { layer0.RowDefinitions.Remove(row1CloneForLayer0); layer0.Margin = new Thickness(0, 0, 0, 2); border.Child = null; bottomPanel.Children.Add(tabPanel); RotateImage(pinImage); // remove Tag from gridSplitter so that style trigger displays the splitter gridSplitter.Tag = null; } else if (oldValue == NotifyState.Collapsed) { layer1.Visibility = Visibility.Visible; } }
public UserNotifySetting(string values) { this.AllNotify = new NotifySettingItemCollection(); foreach (NotifyType nt in NotifyBO.AllNotifyTypes) { NotifySettingItem item = new NotifySettingItem(); item.NotifyType = nt.TypeID; item.OpenState = AllSettings.Current.NotifySettings.GetNotifySystemState(item.NotifyType); this.AllNotify.Add(item); } while (!string.IsNullOrEmpty(values) && values.Length % 4 == 0 && values.Length >= 4) { int value = 0; try { value = int.Parse(values.Substring(0, 4)); } catch { return; } int type = (int)(value / 10); NotifyState state = (NotifyState)(value % 10); foreach (NotifySettingItem item in this.AllNotify) { if (item.OpenState == NotifyState.AlwaysOpen || item.OpenState == NotifyState.AlwaysClose) { continue; } if (item.NotifyType == type) { item.OpenState = state; } } values = values.Substring(4); } }
private void OnSendEMailLogComplete(MailUserToken userToken, NotifyState notifyState) { if (userToken == null) return; NotifyContent ntfContext = userToken.NtfContext; ntfContext.NotifyState = notifyState; bool res = MonitorDataAccessor.Instance().InsertSendEmailOprLog(DateTime.Now.ToString("yyyy-MM-dd"), CommandTextParser.GetJsonSerialization<NotifyContent>(ntfContext), SystemHelper.GetUtcTicksByDateTime(DateTime.Now)); //写日志 }
private void OnSendEMailLogComplete(MailUserToken userToken,NotifyState notifyState) { if (this.WriteSendEMailLogEvent != null) { this.WriteSendEMailLogEvent.Invoke(userToken, notifyState); } }
// Events that we want to update as soon as possible. Return next time this should be called. private int SendFastRateEvents() { if (reset_notify_state_) { notify_state_ = new NotifyState(); } reset_notify_state_ = false; bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; DispatchToJS(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; DispatchToJS(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { return(kUberSlowTimerMilli); } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_act_combat = ActGlobals.oFormActMain.InCombat; bool in_game_combat = ffxiv_.GetInGameCombat(); if (!notify_state_.in_act_combat.HasValue || in_act_combat != notify_state_.in_act_combat || !notify_state_.in_game_combat.HasValue || in_game_combat != notify_state_.in_game_combat) { notify_state_.in_act_combat = in_act_combat; notify_state_.in_game_combat = in_game_combat; DispatchToJS(new JSEvents.InCombatChangedEvent(in_act_combat, in_game_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = ActGlobals.oFormActMain.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; DispatchToJS(new JSEvents.ZoneChangedEvent(zone_name)); ClearFateWatcherDictionaries(); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. FFXIVProcess.EntityData player = ffxiv_.GetSelfData(); // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) { DispatchToJS(new JSEvents.PlayerDiedEvent()); } } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (!player.Equals(notify_state_.player)) { // Clear the FATE dictionary if we switched characters if (notify_state_.player != null && !player.name.Equals(notify_state_.player.name)) { ClearFateWatcherDictionaries(); } notify_state_.player = player; send = true; } var job = ffxiv_.GetJobSpecificData(player.job); if (job != null) { if (send || !JObject.DeepEquals(job, notify_state_.job_data)) { notify_state_.job_data = job; var ev = new JSEvents.PlayerChangedEvent(player); ev.jobDetail = job; DispatchToJS(ev); } } else if (send) { // No job-specific data. DispatchToJS(new JSEvents.PlayerChangedEvent(player)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List <string> logs; List <string> import_logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; import_logs = import_log_lines_; import_log_lines_ = last_import_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { DispatchToJS(new JSEvents.LogEvent(logs)); logs.Clear(); } last_log_lines_ = logs; last_import_log_lines_ = import_logs; return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
public bool AddNotify(AuthUser oprateUser, Notify notify) { #region 基础参数检查 if (notify == null) { return(false); } if (notify.UserID <= 0) { return(false); } if (notify.UserID == oprateUser.UserID) { return(true); } #endregion UnreadNotifies UnreadNotifies = null; #if !Passport PassportClientConfig setting = Globals.PassportClient; if (setting.EnablePassport) { NotifyType type = AllNotifyTypes[notify.TypeID]; NotifyActionProxy[] proxys = new NotifyActionProxy[notify.Actions.Count]; int i = 0; foreach (NotifyAction action in notify.Actions) { NotifyActionProxy nap = new NotifyActionProxy(); nap.Url = "{clienturl}" + action.Url; nap.Title = action.Title; nap.IsDialog = action.IsDialog; proxys[i] = nap; i++; } ThreadPool.QueueUserWorkItem(delegate(object a) { try { setting.PassportService.Notify_Send(notify.UserID, type.TypeName, notify.Content, notify.DataTable.ToString(), proxys, notify.Keyword); } catch { } }); } else #endif { NotifyState SysState = AllSettings.Current.NotifySettings.GetNotifySystemState(notify.TypeID); SystemNotifyProvider.Update(); //判断系统设置 switch (SysState) { case NotifyState.AlwaysClose: return(false); case NotifyState.DefaultClose: case NotifyState.DefaultOpen: //判断用户设置 UserNotifySetting userSetting = UserBO.Instance.GetNotifySetting(notify.UserID); if (userSetting != null && userSetting.GetNotifyState(notify.TypeID) == NotifyState.DefaultClose) { return(false); } break; } StringTable actions = new StringTable(); if (notify.Actions != null) { foreach (NotifyAction na in notify.Actions) { actions.Add(na.Title, (na.IsDialog ? "*" : "") + na.Url); } } NotifyDao.Instance.AddNotify(notify.UserID, notify.TypeID, notify.Content, notify.Keyword, notify.DataTable.ToString(), 0, actions.ToString(), out UnreadNotifies); AuthUser user; user = UserBO.Instance.GetUserFromCache <AuthUser>(notify.UserID); if (user != null) { user.UnreadNotify = UnreadNotifies; } if (OnUserNotifyCountChanged != null) { OnUserNotifyCountChanged(UnreadNotifies); } RemoveCacheByType(notify.UserID, 0); return(true); } return(true); }
// Events that we want to update as soon as possible. private void SendFastRateEvents() { // Hold this while we're in here to prevent the Renderer or Browser from disappearing from under us. fast_update_timer_semaphore_.Wait(); // Handle startup and shutdown. And do not fire any events until the page has loaded and had a chance to // register its event handlers. if (Overlay == null || Overlay.Renderer == null || Overlay.Renderer.Browser == null || Overlay.Renderer.Browser.IsLoading) { fast_update_timer_semaphore_.Release(); fast_update_timer_.Interval = kSlowTimerMilli; fast_update_timer_.Start(); return; } if (reset_notify_state_) notify_state_ = new NotifyState(); reset_notify_state_ = false; if (!notify_state_.sent_data_dir && Config.Url.Length > 0) { notify_state_.sent_data_dir = true; var web = new System.Net.WebClient(); var data_file_paths = new List<string>(); try { var data_dir_manifest = new Uri(new Uri(Config.Url), "data/manifest.txt"); var manifest_reader = new StringReader(web.DownloadString(data_dir_manifest)); for (var line = manifest_reader.ReadLine(); line != null; line = manifest_reader.ReadLine()) { line = line.Trim(); if (line.Length > 0) data_file_paths.Add(line); } } catch (System.Net.WebException e) { if (e.Status == System.Net.WebExceptionStatus.ProtocolError && e.Response is System.Net.HttpWebResponse && ((System.Net.HttpWebResponse)e.Response).StatusCode == System.Net.HttpStatusCode.NotFound) { // Ignore file not found. } else if (e.InnerException != null && (e.InnerException is FileNotFoundException || e.InnerException is DirectoryNotFoundException)) { // Ignore file not found. } else if (e.InnerException != null && e.InnerException.InnerException != null && (e.InnerException.InnerException is FileNotFoundException || e.InnerException.InnerException is DirectoryNotFoundException)) { // Ignore file not found. } else { LogError("Unable to read manifest file: " + e.Message); } } catch (Exception e) { LogError("Unable to read manifest file: " + e.Message); } if (data_file_paths.Count > 0) { var file_data = new Dictionary<string, string>(); foreach (string data_filename in data_file_paths) { try { var file_path = new Uri(new Uri(Config.Url), "data/" + data_filename); var file_reader = new StringReader(web.DownloadString(file_path)); file_data[data_filename] = file_reader.ReadToEnd(); } catch (Exception e) { LogError("Unable to read data file: " + e.Message); } } OnDataFilesRead(new JSEvents.DataFilesRead(file_data)); } } bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; OnGameExists(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; OnGameActiveChanged(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { fast_update_timer_semaphore_.Release(); fast_update_timer_.Interval = kUberSlowTimerMilli; fast_update_timer_.Start(); return; } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_combat = FFXIV_ACT_Plugin.ACTWrapper.InCombat; if (!notify_state_.in_combat.HasValue || in_combat != notify_state_.in_combat) { notify_state_.in_combat = in_combat; OnInCombatChanged(new JSEvents.InCombatChangedEvent(in_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = FFXIV_ACT_Plugin.ACTWrapper.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; OnZoneChanged(new JSEvents.ZoneChangedEvent(zone_name)); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. Tuple<FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> player_data = ffxiv_.GetSelfData(); var player = player_data != null ? player_data.Item1 : null; var player_cast = player_data != null ? player_data.Item2 : null; // The |target| can be null when no target is selected. Tuple<FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> target_data = ffxiv_.GetTargetData(); var target = target_data != null ? target_data.Item1 : null; var target_cast = target_data != null ? target_data.Item2 : null; // The |focus| can be null when no focus target is selected. Tuple<FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> focus_data = ffxiv_.GetFocusData(); var focus = focus_data != null ? focus_data.Item1 : null; var focus_cast = focus_data != null ? focus_data.Item2 : null; // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) OnPlayerDied(new JSEvents.PlayerDiedEvent()); } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (player != notify_state_.player) { notify_state_.player = player; send = true; } if (player.job == FFXIVProcess.EntityJob.RDM) { var job = ffxiv_.GetRedMage(); if (job != null) { if (send || !job.Equals(notify_state_.rdm)) { notify_state_.rdm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.RedMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.WAR) { var job = ffxiv_.GetWarrior(); if (job != null) { if (send || !job.Equals(notify_state_.war)) { notify_state_.war = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.WarriorDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.DRK) { var job = ffxiv_.GetDarkKnight(); if (job != null) { if (send || !job.Equals(notify_state_.drk)) { notify_state_.drk = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.DarkKnightDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.PLD) { var job = ffxiv_.GetPaladin(); if (job != null) { if (send || !job.Equals(notify_state_.pld)) { notify_state_.pld = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.PaladinDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.BRD) { var job = ffxiv_.GetBard(); if (job != null) { if (send || !job.Equals(notify_state_.brd)) { notify_state_.brd = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.BardDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.NIN) { var job = ffxiv_.GetNinja(); if (job != null) { if (send || !job.Equals(notify_state_.nin)) { notify_state_.nin = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.NinjaDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.BLM || player.job == FFXIVProcess.EntityJob.THM) { var job = ffxiv_.GetBlackMage(); if (job != null) { if (send || !job.Equals(notify_state_.blm)) { notify_state_.blm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.BlackMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.WHM) { var job = ffxiv_.GetWhiteMage(); if (job != null) { if (send || !job.Equals(notify_state_.whm)) { notify_state_.whm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.WhiteMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.SMN || player.job == FFXIVProcess.EntityJob.SCH || player.job == FFXIVProcess.EntityJob.ACN) { var job = ffxiv_.GetSummonerAndScholar(); if (job != null) { if (send || !job.Equals(notify_state_.smn_sch)) { notify_state_.smn_sch = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.SummonerAndScholarDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.MNK || player.job == FFXIVProcess.EntityJob.PGL) { var job = ffxiv_.GetMonk(); if (job != null) { if (send || !job.Equals(notify_state_.mnk)) { notify_state_.mnk = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.MonkDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.MCH) { var job = ffxiv_.GetMachinist(); if (job != null) { if (send || !job.Equals(notify_state_.mch)) { notify_state_.mch = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.MachinistDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.AST) { var job = ffxiv_.GetAstrologian(); if (job != null) { if (send || !job.Equals(notify_state_.ast)) { notify_state_.ast = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.AstrologianDetail(job); OnPlayerChanged(e); } } } else if (send) { // TODO: SAM everything // No job-specific data. OnPlayerChanged(new JSEvents.PlayerChangedEvent(player)); } } // onTargetChangedEvent: Fires when current target or their state changes. if (target != notify_state_.target) { notify_state_.target = target; if (target != null) OnTargetChanged(new JSEvents.TargetChangedEvent(target)); else OnTargetChanged(new JSEvents.TargetChangedEvent(null)); } // onTargetCastingEvent: Fires each tick while the target is casting, and once // with null when not casting. int target_cast_id = target_cast != null ? target_cast.casting_id : 0; if (target_cast_id != 0 || target_cast_id != notify_state_.target_cast_id) { notify_state_.target_cast_id = target_cast_id; // The game considers things to be casting once progress reaches the end for a while, as the server is // resolving lag or something. That breaks our start time tracking, so we just don't consider them to // be casting anymore once it reaches the end. if (target_cast_id != 0 && target_cast.casting_time_progress < target_cast.casting_time_length) { DateTime start = now.AddSeconds(-target_cast.casting_time_progress); // If the start is within the timer interval, assume it's the same cast. Since we sample the game // at a different rate than it ticks, there will be some jitter in the progress that we see, and this // helps avoid it. TimeSpan range = new TimeSpan(0, 0, 0, 0, kFastTimerMilli); if (start + range < notify_state_.target_cast_start || start - range > notify_state_.target_cast_start) notify_state_.target_cast_start = start; TimeSpan progress = now - notify_state_.target_cast_start; OnTargetCasting(new JSEvents.TargetCastingEvent(target_cast.casting_id, progress.TotalSeconds, target_cast.casting_time_length)); } else { notify_state_.target_cast_start = new DateTime(); OnTargetCasting(new JSEvents.TargetCastingEvent(0, 0, 0)); } } // onFocusChangedEvent: Fires when current focus target or their state changes. if (focus != notify_state_.focus) { notify_state_.focus = focus; if (target != null) OnFocusChanged(new JSEvents.FocusChangedEvent(focus)); else OnFocusChanged(new JSEvents.FocusChangedEvent(null)); } // onFocusCastingEvent: Fires each tick while the focus target is casting, and // once with null when not casting. int focus_cast_id = focus_cast != null ? focus_cast.casting_id : 0; if (focus_cast_id != 0 || focus_cast_id != notify_state_.focus_cast_id) { notify_state_.focus_cast_id = focus_cast_id; // The game considers things to be casting once progress reaches the end for a while, as the server is // resolving lag or something. That breaks our start time tracking, so we just don't consider them to // be casting anymore once it reaches the end. if (focus_cast_id != 0 && focus_cast.casting_time_progress < focus_cast.casting_time_length) { DateTime start = now.AddSeconds(-focus_cast.casting_time_progress); // If the start is within the timer interval, assume it's the same cast. Since we sample the game // at a different rate than it ticks, there will be some jitter in the progress that we see, and this // helps avoid it. TimeSpan range = new TimeSpan(0, 0, 0, 0, kFastTimerMilli); if (start + range < notify_state_.focus_cast_start || start - range > notify_state_.focus_cast_start) notify_state_.focus_cast_start = start; TimeSpan progress = now - notify_state_.focus_cast_start; OnFocusCasting(new JSEvents.FocusCastingEvent(focus_cast.casting_id, progress.TotalSeconds, focus_cast.casting_time_length)); } else { notify_state_.focus_cast_start = new DateTime(); OnFocusCasting(new JSEvents.FocusCastingEvent(0, 0, 0)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List<string> logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { OnLogsChanged(new JSEvents.LogEvent(logs)); logs.Clear(); } last_log_lines_ = logs; fight_tracker_.Tick(DateTime.Now); fast_update_timer_semaphore_.Release(); fast_update_timer_.Interval = game_active ? kFastTimerMilli : kSlowTimerMilli; fast_update_timer_.Start(); }
public async Task NotifyIfConnected <TEntity>(string relatedUserSub, TEntity user, NotifyState state) where TEntity : class, IUser { string connectionId = UserConnections.Connections.GetValueOrDefault(relatedUserSub); if (connectionId != null) { await dataHub.Clients.Client(connectionId) .SendCoreAsync(state.ToString(), new object[] { user.UserName }); } }
void InitState(NotifyState ns) { currs = ns; starttime = Time.time; OnEnter(currs); }
// Events that we want to update as soon as possible. Return next time this should be called. private int SendFastRateEvents() { if (reset_notify_state_) { notify_state_ = new NotifyState(); } reset_notify_state_ = false; // Loading dance: // * OverlayPlugin loads addons and initializes event sources. // * OverlayPlugin loads its configuration. // * Event sources are told to load their configuration and start (LoadConfig and Start are called). // * Overlays are initialised and the browser instances are started. At this points the overlays start loading. // * At some point the overlay's JavaScript is executed and OverlayPluginApi is injected. This order isn't // deterministic and depends on what the ACT process is doing at that point in time. During startup the // OverlayPluginApi is usually injected after the overlay is done loading while an overlay that's reloaded or // loaded later on will see the OverlayPluginApi before the page has loaded. // * The overlay JavaScript sets up the initial event handlers and calls the cactbotLoadUser handler through // getUserConfigLocation. These actions are queued by the JS implementation in common.js until OverlayPluginApi // (or the WebSocket) is available. Once it is, the event subscriptions and handler calls are transmitted. // * OverlayPlugin stores the event subscriptions and executes the C# handlers which in this case means // FetchUserFiles is called. That method loads the user files and returns them. The result is now transmitted // back to the overlay that called the handler and the Promise in JS is resolved with the result. // * getUserConfigLocation processes the received information and calls the passed callback. This constructs the // overlay specific objects and registers additional event handlers. Finally, the cactbotRequestState handler // is called. // * OverlayPlugin processes the new event subscriptions and executes the cactbotRequestState handler. // * The next time SendFastRateEvents is called, it resets notify_state_ (since the previous handler set // reset_notify_state_ to true) which causes it to dispatch all state events again. These events are now // dispatched to all subscribed overlays. However, this means that overlays can receive state events multiple // times during startup. If the user has three Cactbot overlays, all of them will call cactbotRequestState and // thus cause this to happen one to three times depending on their timing. This shouldn't cause any issues but // it's a waste of CPU cycles. // * Since this only happens during startup, it's probably not worth fixing though. Not sure. // * Some overlays behave slightly different from the above explanation. Raidboss for example loads data files // in addition to the listed steps. I think it's even loading them twice since raidboss.js loads the data files // for gTimelineController and popup-text.js requests them again for its own purposes. bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; OnGameExists(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; OnGameActiveChanged(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { return(kUberSlowTimerMilli); } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_act_combat = Advanced_Combat_Tracker.ActGlobals.oFormActMain.InCombat; bool in_game_combat = ffxiv_.GetInGameCombat(); if (!notify_state_.in_act_combat.HasValue || in_act_combat != notify_state_.in_act_combat || !notify_state_.in_game_combat.HasValue || in_game_combat != notify_state_.in_game_combat) { notify_state_.in_act_combat = in_act_combat; notify_state_.in_game_combat = in_game_combat; OnInCombatChanged(new JSEvents.InCombatChangedEvent(in_act_combat, in_game_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = Advanced_Combat_Tracker.ActGlobals.oFormActMain.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; OnZoneChanged(new JSEvents.ZoneChangedEvent(zone_name)); ClearFateWatcherDictionaries(); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. FFXIVProcess.EntityData player = ffxiv_.GetSelfData(); // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) { OnPlayerDied(new JSEvents.PlayerDiedEvent()); } } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (!player.Equals(notify_state_.player)) { // Clear the FateWatcher dictionaries if we switched characters if (notify_state_.player != null && !player.name.Equals(notify_state_.player.name)) { ClearFateWatcherDictionaries(); } notify_state_.player = player; send = true; } var job = ffxiv_.GetJobSpecificData(player.job); if (job != null) { if (send || !JObject.DeepEquals(job, notify_state_.job_data)) { notify_state_.job_data = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = job; OnPlayerChanged(e); } } else if (send) { // No job-specific data. OnPlayerChanged(new JSEvents.PlayerChangedEvent(player)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List <string> logs; List <string> import_logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; import_logs = import_log_lines_; import_log_lines_ = last_import_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { OnLogsChanged(new JSEvents.LogEvent(logs)); logs.Clear(); } if (import_logs.Count > 0) { OnImportLogsChanged(new JSEvents.ImportLogEvent(import_logs)); import_logs.Clear(); } last_log_lines_ = logs; last_import_log_lines_ = import_logs; return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
// Events that we want to update as soon as possible. Return next time this should be called. private int SendFastRateEvents() { // Handle startup and shutdown. And do not fire any events until the page has loaded and had a chance to // register its event handlers. if (Overlay == null || Overlay.Renderer == null || Overlay.Renderer.Browser == null || Overlay.Renderer.Browser.IsLoading) { return(kSlowTimerMilli); } if (reset_notify_state_) { notify_state_ = new NotifyState(); } reset_notify_state_ = false; // Loading dance: // * wait for !CefBrowser::IsLoading. // * Execute JS in all overlays to wait for DOMContentReady and send back an event. // * When this OverlayMessage comes in, send back an onInitializeOverlay message. // * Overlays should load options from the provider user data dir in that message. // * As DOMContentReady happened, overlays should also construct themselves and attach to the DOM. // * Now, messages can be sent freely and everything is initialized. if (!notify_state_.added_dom_content_listener) { // Send this from C# so that overlays that are using non-cactbot html // (e.g. dps overlays) don't have to be modified. const string waitForDOMContentReady = @" (function() { var sendDOMContentLoaded = function() { if (!window.OverlayPluginApi) { window.setTimeout(sendDOMContentLoaded, 100); } else { window.OverlayPluginApi.overlayMessage(OverlayPluginApi.overlayName, JSON.stringify({'onDOMContentLoaded': true})); } }; if (document.readyState == 'loaded' || document.readyState == 'complete') { sendDOMContentLoaded(); } else { document.addEventListener('DOMContentLoaded', sendDOMContentLoaded); } })(); "; this.Overlay.Renderer.ExecuteScript(waitForDOMContentReady); notify_state_.added_dom_content_listener = true; } // This flag set as a result of onDOMContentLoaded overlay message. if (!notify_state_.dom_content_loaded) { return(kSlowTimerMilli); } if (!notify_state_.sent_data_dir && Config.Url.Length > 0) { notify_state_.sent_data_dir = true; var url = Config.Url; // If file is a remote pointer, load that file explicitly so that the manifest // is relative to the pointed to url and not the local file. if (url.StartsWith("file:///")) { var html = File.ReadAllText(new Uri(url).LocalPath); var match = System.Text.RegularExpressions.Regex.Match(html, @"<meta http-equiv=""refresh"" content=""0; url=(.*)?""\/?>"); if (match.Groups.Count > 1) { url = match.Groups[1].Value; } } var web = new System.Net.WebClient(); web.Encoding = System.Text.Encoding.UTF8; System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3 | System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12; var data_file_paths = new List <string>(); try { var data_dir_manifest = new Uri(new Uri(url), "data/manifest.txt"); var manifest_reader = new StringReader(web.DownloadString(data_dir_manifest)); for (var line = manifest_reader.ReadLine(); line != null; line = manifest_reader.ReadLine()) { line = line.Trim(); if (line.Length > 0) { data_file_paths.Add(line); } } } catch (System.Net.WebException e) { if (e.Status == System.Net.WebExceptionStatus.ProtocolError && e.Response is System.Net.HttpWebResponse && ((System.Net.HttpWebResponse)e.Response).StatusCode == System.Net.HttpStatusCode.NotFound) { // Ignore file not found. } else if (e.InnerException != null && (e.InnerException is FileNotFoundException || e.InnerException is DirectoryNotFoundException)) { // Ignore file not found. } else if (e.InnerException != null && e.InnerException.InnerException != null && (e.InnerException.InnerException is FileNotFoundException || e.InnerException.InnerException is DirectoryNotFoundException)) { // Ignore file not found. } else { LogError("Unable to read manifest file: " + e.Message); } } catch (Exception e) { LogError("Unable to read manifest file: " + e.Message); } if (data_file_paths.Count > 0) { var file_data = new Dictionary <string, string>(); foreach (string data_filename in data_file_paths) { try { var file_path = new Uri(new Uri(url), "data/" + data_filename); file_data[data_filename] = web.DownloadString(file_path); } catch (Exception e) { LogError("Unable to read data file: " + e.Message); } } OnDataFilesRead(new JSEvents.DataFilesRead(file_data)); } } bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; OnGameExists(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; OnGameActiveChanged(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { return(kUberSlowTimerMilli); } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_act_combat = FFXIV_ACT_Plugin.ACTWrapper.InCombat; bool in_game_combat = ffxiv_.GetInGameCombat(); if (!notify_state_.in_act_combat.HasValue || in_act_combat != notify_state_.in_act_combat || !notify_state_.in_game_combat.HasValue || in_game_combat != notify_state_.in_game_combat) { notify_state_.in_act_combat = in_act_combat; notify_state_.in_game_combat = in_game_combat; OnInCombatChanged(new JSEvents.InCombatChangedEvent(in_act_combat, in_game_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = FFXIV_ACT_Plugin.ACTWrapper.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; OnZoneChanged(new JSEvents.ZoneChangedEvent(zone_name)); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. Tuple <FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> player_data = ffxiv_.GetSelfData(); var player = player_data != null ? player_data.Item1 : null; var player_cast = player_data != null ? player_data.Item2 : null; // The |target| can be null when no target is selected. Tuple <FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> target_data = ffxiv_.GetTargetData(); var target = target_data != null ? target_data.Item1 : null; var target_cast = target_data != null ? target_data.Item2 : null; // The |focus| can be null when no focus target is selected. Tuple <FFXIVProcess.EntityData, FFXIVProcess.SpellCastingData> focus_data = ffxiv_.GetFocusData(); var focus = focus_data != null ? focus_data.Item1 : null; var focus_cast = focus_data != null ? focus_data.Item2 : null; // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) { OnPlayerDied(new JSEvents.PlayerDiedEvent()); } } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (player != notify_state_.player) { notify_state_.player = player; send = true; } if (player.job == FFXIVProcess.EntityJob.RDM) { var job = ffxiv_.GetRedMage(); if (job != null) { if (send || !job.Equals(notify_state_.rdm)) { notify_state_.rdm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.RedMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.WAR) { var job = ffxiv_.GetWarrior(); if (job != null) { if (send || !job.Equals(notify_state_.war)) { notify_state_.war = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.WarriorDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.DRK) { var job = ffxiv_.GetDarkKnight(); if (job != null) { if (send || !job.Equals(notify_state_.drk)) { notify_state_.drk = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.DarkKnightDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.PLD) { var job = ffxiv_.GetPaladin(); if (job != null) { if (send || !job.Equals(notify_state_.pld)) { notify_state_.pld = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.PaladinDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.BRD) { var job = ffxiv_.GetBard(); if (job != null) { if (send || !job.Equals(notify_state_.brd)) { notify_state_.brd = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.BardDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.NIN) { var job = ffxiv_.GetNinja(); if (job != null) { if (send || !job.Equals(notify_state_.nin)) { notify_state_.nin = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.NinjaDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.DRG) { var job = ffxiv_.GetDragoon(); if (job != null) { if (send || !job.Equals(notify_state_.drg)) { notify_state_.drg = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.DragoonDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.BLM || player.job == FFXIVProcess.EntityJob.THM) { var job = ffxiv_.GetBlackMage(); if (job != null) { if (send || !job.Equals(notify_state_.blm)) { notify_state_.blm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.BlackMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.WHM) { var job = ffxiv_.GetWhiteMage(); if (job != null) { if (send || !job.Equals(notify_state_.whm)) { notify_state_.whm = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.WhiteMageDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.SMN || player.job == FFXIVProcess.EntityJob.SCH || player.job == FFXIVProcess.EntityJob.ACN) { var job = ffxiv_.GetSummonerAndScholar(); if (job != null) { if (send || !job.Equals(notify_state_.smn_sch)) { notify_state_.smn_sch = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.SummonerAndScholarDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.MNK || player.job == FFXIVProcess.EntityJob.PGL) { var job = ffxiv_.GetMonk(); if (job != null) { if (send || !job.Equals(notify_state_.mnk)) { notify_state_.mnk = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.MonkDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.MCH) { var job = ffxiv_.GetMachinist(); if (job != null) { if (send || !job.Equals(notify_state_.mch)) { notify_state_.mch = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.MachinistDetail(job); OnPlayerChanged(e); } } } else if (player.job == FFXIVProcess.EntityJob.AST) { var job = ffxiv_.GetAstrologian(); if (job != null) { if (send || !job.Equals(notify_state_.ast)) { notify_state_.ast = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.AstrologianDetail(job); OnPlayerChanged(e); } } } else if (send) { // TODO: SAM everything // No job-specific data. OnPlayerChanged(new JSEvents.PlayerChangedEvent(player)); } } // onTargetChangedEvent: Fires when current target or their state changes. if (target != notify_state_.target) { notify_state_.target = target; if (target != null) { OnTargetChanged(new JSEvents.TargetChangedEvent(target)); } else { OnTargetChanged(new JSEvents.TargetChangedEvent(null)); } } // onTargetCastingEvent: Fires each tick while the target is casting, and once // with null when not casting. int target_cast_id = target_cast != null ? target_cast.casting_id : 0; if (target_cast_id != 0 || target_cast_id != notify_state_.target_cast_id) { notify_state_.target_cast_id = target_cast_id; // The game considers things to be casting once progress reaches the end for a while, as the server is // resolving lag or something. That breaks our start time tracking, so we just don't consider them to // be casting anymore once it reaches the end. if (target_cast_id != 0 && target_cast.casting_time_progress < target_cast.casting_time_length) { DateTime start = now.AddSeconds(-target_cast.casting_time_progress); // If the start is within the timer interval, assume it's the same cast. Since we sample the game // at a different rate than it ticks, there will be some jitter in the progress that we see, and this // helps avoid it. TimeSpan range = new TimeSpan(0, 0, 0, 0, kFastTimerMilli); if (start + range < notify_state_.target_cast_start || start - range > notify_state_.target_cast_start) { notify_state_.target_cast_start = start; } TimeSpan progress = now - notify_state_.target_cast_start; OnTargetCasting(new JSEvents.TargetCastingEvent(target_cast.casting_id, progress.TotalSeconds, target_cast.casting_time_length)); } else { notify_state_.target_cast_start = new DateTime(); OnTargetCasting(new JSEvents.TargetCastingEvent(0, 0, 0)); } } // onFocusChangedEvent: Fires when current focus target or their state changes. if (focus != notify_state_.focus) { notify_state_.focus = focus; if (target != null) { OnFocusChanged(new JSEvents.FocusChangedEvent(focus)); } else { OnFocusChanged(new JSEvents.FocusChangedEvent(null)); } } // onFocusCastingEvent: Fires each tick while the focus target is casting, and // once with null when not casting. int focus_cast_id = focus_cast != null ? focus_cast.casting_id : 0; if (focus_cast_id != 0 || focus_cast_id != notify_state_.focus_cast_id) { notify_state_.focus_cast_id = focus_cast_id; // The game considers things to be casting once progress reaches the end for a while, as the server is // resolving lag or something. That breaks our start time tracking, so we just don't consider them to // be casting anymore once it reaches the end. if (focus_cast_id != 0 && focus_cast.casting_time_progress < focus_cast.casting_time_length) { DateTime start = now.AddSeconds(-focus_cast.casting_time_progress); // If the start is within the timer interval, assume it's the same cast. Since we sample the game // at a different rate than it ticks, there will be some jitter in the progress that we see, and this // helps avoid it. TimeSpan range = new TimeSpan(0, 0, 0, 0, kFastTimerMilli); if (start + range < notify_state_.focus_cast_start || start - range > notify_state_.focus_cast_start) { notify_state_.focus_cast_start = start; } TimeSpan progress = now - notify_state_.focus_cast_start; OnFocusCasting(new JSEvents.FocusCastingEvent(focus_cast.casting_id, progress.TotalSeconds, focus_cast.casting_time_length)); } else { notify_state_.focus_cast_start = new DateTime(); OnFocusCasting(new JSEvents.FocusCastingEvent(0, 0, 0)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List <string> logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { OnLogsChanged(new JSEvents.LogEvent(logs)); logs.Clear(); } last_log_lines_ = logs; fight_tracker_.Tick(DateTime.Now); return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
void svn_wc_notify_func_t_impl(IntPtr baton, IntPtr path, NotifyAction action, NodeKind kind, IntPtr mime_type, NotifyState content_state, NotifyState prop_state, long revision) { string actiondesc = action.ToString(); switch (action) { case NotifyAction.UpdateAdd: actiondesc = "Added"; break; case NotifyAction.UpdateDelete: actiondesc = "Deleted"; break; case NotifyAction.UpdateUpdate: actiondesc = "Updating"; break; case NotifyAction.UpdateExternal: actiondesc = "External Updated"; break; case NotifyAction.UpdateCompleted: actiondesc = "Finished"; break; case NotifyAction.CommitAdded: actiondesc = "Added"; break; case NotifyAction.CommitDeleted: actiondesc = "Deleted"; break; case NotifyAction.CommitModified: actiondesc = "Modified"; break; case NotifyAction.CommitReplaced: actiondesc = "Replaced"; break; case NotifyAction.CommitPostfixTxDelta: actiondesc = "Sending Content"; break; /*Add, Copy, Delete, Restore, Revert, FailedRevert, Resolved, Skip, StatusCompleted, StatusExternal, BlameRevision*/ } if (updatecallback != null) updatecallback(Marshal.PtrToStringAnsi(path), actiondesc); }