public EntityChangedEvent(FFXIVProcess.EntityData e) { if (e != null) { id = e.id; level = e.level; name = e.name; job = e.job.ToString(); currentHP = e.hp; maxHP = e.max_hp; currentMP = e.mp; maxMP = e.max_mp; pos = new Point3F(e.pos_x, e.pos_y, e.pos_z); distance = e.distance; } }
public PlayerChangedEvent(FFXIVProcess.EntityData e) { id = e.id; level = e.level; name = e.name; job = e.job.ToString(); currentHP = e.hp; maxHP = e.max_hp; currentMP = e.mp; maxMP = e.max_mp; maxTP = 1000; currentGP = e.gp; maxGP = e.max_gp; currentCP = e.cp; maxCP = e.max_cp; pos = new Point3F(e.pos_x, e.pos_y, e.pos_z); jobDetail = null; bait = e.bait; debugJob = e.debug_job; }
// 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); }
public FocusChangedEvent(FFXIVProcess.EntityData e) : base(e) { }
public TargetChangedEvent(FFXIVProcess.EntityData e) : base(e) { }
// 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 = 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)); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. FFXIVProcess.EntityData player = ffxiv_.GetSelfData(); // The |target| can be null when no target is selected. FFXIVProcess.EntityData target = ffxiv_.GetTargetData(); // The |focus| can be null when no focus target is selected. FFXIVProcess.EntityData focus = ffxiv_.GetFocusData(); // 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 (player.job == FFXIVProcess.EntityJob.SAM) { var job = ffxiv_.GetSamurai(); if (job != null) { if (send || !job.Equals(notify_state_.sam)) { notify_state_.sam = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = new JSEvents.PlayerChangedEvent.SamuraiDetail(job); OnPlayerChanged(e); } } } else if (send) { // 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)); } } // 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)); } } // 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; fight_tracker_.Tick(DateTime.Now); return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
public StatusEffectsUpdateEvent(FFXIVProcess.EntityData player, FFXIVProcess.EntityData target, FFXIVProcess.EntityData focus) { playerStatusEffects = player?.statusEffects; targetStatusEffects = target?.statusEffects; focusStatusEffects = focus?.statusEffects; }