1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
//! Bindings for [ArcDPS](https://www.deltaconnected.com/arcdps/) plugins.
//!
//! # Usage
//! Plugins export information for ArcDPS via the [`export!`] macro.
//! To see which fields are supported by it, have a look at [`SupportedFields`].
//!
//! ```no_run
//! # mod test {
//! use std::error::Error;
//! use arcdps::{Agent, Event, StateChange};
//!
//! arcdps::export! {
//! name: "Example Plugin",
//! sig: 0x12345678, // change this to a random number
//! init,
//! combat: custom_combat_name,
//! }
//!
//! fn init() -> Result<(), String> {
//! // may return an error to indicate load failure
//! Ok(())
//! }
//!
//! fn custom_combat_name(
//! event: Option<&Event>,
//! src: Option<&Agent>,
//! dst: Option<&Agent>,
//! skill_name: Option<&str>,
//! id: u64,
//! revision: u64,
//! ) {
//! if let Some(event) = event {
//! if let StateChange::EnterCombat = event.get_statechange() {
//! // source agent has entered combat
//! }
//! }
//! }
//! # }
//! ```
//!
//! # Unofficial Extras
//! [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases) support is hidden behind the `extras` feature flag.
//!
//! ```no_run
//! # mod test {
//! use arcdps::extras::{UserInfoIter, UserRole};
//!
//! arcdps::export! {
//! name: "Example Plugin",
//! sig: 123,
//! extras_squad_update,
//! }
//!
//! fn extras_squad_update(users: UserInfoIter) {
//! for user in users {
//! if let UserRole::SquadLeader | UserRole::Lieutenant = user.role {
//! // user can place markers
//! }
//! }
//! }
//! # }
//! ```
#![allow(clippy::missing_safety_doc)]
pub mod callbacks;
pub mod evtc;
pub mod exports;
#[cfg(feature = "extras")]
pub mod extras;
#[cfg(feature = "log")]
pub mod log;
mod globals;
mod panic;
mod util;
pub use arcdps_codegen::export;
pub use arcdps_imgui as imgui;
pub use evtc::{
Activation, Affinity, Agent, AgentOwned, Attribute, BuffCategory, BuffCycle, BuffRemove,
CustomSkill, Event, Language, Profession, Specialization, StateChange, Strike,
};
pub use globals::{d3d11_device, d3d_version, dxgi_swap_chain};
pub use util::strip_account_prefix;
use callbacks::*;
#[cfg(feature = "extras")]
use extras::callbacks::*;
/// Reference on what fields are currently supported by the [`export!`] macro.
///
/// This struct is not used anywhere.
pub struct SupportedFields {
/// Name of the plugin.
pub name: &'static str,
/// Unique signature of the plugin.
///
/// Pick a random number that is not used by other modules.
pub sig: u32,
/// Callback for plugin load.
pub init: Option<InitFunc>,
/// Callback for plugin unload.
pub release: Option<ReleaseFunc>,
/// Callback for plugin unload.
// TODO: higher level abstraction?
pub update_url: Option<UpdateUrlFunc>,
/// Raw WndProc callback.
pub raw_wnd_nofilter: Option<RawWndProcCallback>,
/// Raw ImGui callback.
pub raw_imgui: Option<RawImguiCallback>,
/// Raw options callback.
pub raw_options_end: Option<RawOptionsCallback>,
/// Raw combat callback.
pub raw_combat: Option<RawCombatCallback>,
/// Raw filtered WndProc callback.
pub raw_wnd_filter: Option<RawWndProcCallback>,
/// Raw options windows callback.
pub raw_options_windows: Option<RawOptionsWindowsCallback>,
/// Raw local combat callback.
pub raw_combat_local: Option<RawCombatCallback>,
/// Callback for key presses.
///
/// Returning `true` will allow ArcDPS and GW2 to receive the key press.
/// First parameter indicates the [virtual key code](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
/// Second parameter is `true` if the key was pressed and `false` when released.
/// Third parameter is `true` if the key was down before this event occurred, for example by holding it down.
pub wnd_nofilter: Option<WndProcCallback>,
/// Callback for area combat events.
///
/// May be called asynchronously, use `id` to keep track of order.
/// First event id will be `2`.
///
/// At least one participant will be a party/squad member or minion of, or a buff applied by squad in the case of buff remove.
/// Not all statechanges are present in the realtime API, see [`StateChange`] for details.
///
/// No `event` and `src.elite == 0` indicates a tracking change.
/// Player was added when `src.prof != 0`, otherwise removed.
/// When added `dst.name` contains the account name,
/// `dst.id` the instance id,
/// `dst.prof` the [`Profession`],
/// `dst.elite` the elite [`Specialization`],
/// `dst.is_self` whether the added player is self (local player),
/// `src.team` the team and `dst.team` the subgroup.
///
/// No `event` and `src.elite != 0` indicates a target change.
/// `src.id` will contain the new target.
///
/// *Note that Arc's realtime combat API comes with an intentional delay and filtering.*
pub combat: Option<CombatCallback>,
/// Callback for standalone UI creation.
///
/// Provides an [`imgui::Ui`] for drawing.
/// The second parameter is `true` whenever the player is **not** in character select, loading screens or forced cameras.
pub imgui: Option<ImguiCallback>,
/// Callback for plugin settings UI creation.
///
/// Provides an [`imgui::Ui`] for drawing.
pub options_end: Option<OptionsCallback>,
/// Callback for local combat events.
///
/// Same as [`combat`](Self::combat) but for events from chat log.
pub combat_local: Option<CombatCallback>,
/// Callback for filtered key presses.
///
/// Same as [`wnd_nofilter`](Self::wnd_nofilter) but filtered to only notify when modifiers are pressed.
pub wnd_filter: Option<WndProcCallback>,
/// Callback for options windows.
///
/// Called for each window checkbox in ArcDPS settings.
/// Last call will always be with [`None`].
/// Does not draw the checkbox if returning `true`.
pub options_windows: Option<OptionsWindowsCallback>,
/// Raw extras init callback.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub raw_extras_init: Option<RawExtrasSubscriberInit>,
/// Raw extras squad update callback.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub raw_extras_squad_update: Option<RawExtrasSquadUpdateCallback>,
/// Raw extras language changed callback.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub raw_extras_language_changed: Option<RawExtrasLanguageChangedCallback>,
/// Raw extras keybind changed callback.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub raw_extras_keybind_changed: Option<RawExtrasKeybindChangedCallback>,
/// Raw extras chat message callback.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub raw_extras_chat_message: Option<RawExtrasChatMessageCallback>,
/// Initialization callback for [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases).
///
/// Can be called before or after ArcDPS [`init`](Self::init).
/// Receives information about the Unofficial Extras addon and the current player account name as parameters.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub extras_init: Option<ExtrasInitFunc>,
/// Squad update callback for [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases).
///
/// Called whenever anything in the squad changes.
/// Only the users that changed are sent.
/// If a user was removed from the squad, their `role` will be set to [`UserRole::None`](crate::extras::UserRole::None).
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub extras_squad_update: Option<ExtrasSquadUpdateCallback>,
/// Language changed callback for [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases).
///
/// Called whenever the language is changed, either by changing it in the UI or by pressing the translation key (Right Ctrl by default).
///
/// Will be called directly after initialization, with the current language, to get the startup language.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub extras_language_changed: Option<ExtrasLanguageChangedCallback>,
/// Keybind changed callback for [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases).
///
/// Called whenever a keybind is changed, either by changing it in the ingame UI or with the presets feature of Unofficial Extras.
/// It is called for every keybind separately.
///
/// After initialization this is called for every current keybind that exists.
/// If you want to get a single keybind, at any time you want, call the exported function.
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub extras_keybind_changed: Option<ExtrasKeybindChangedCallback>,
/// Chat message callback for [Unofficial Extras](https://github.com/Krappa322/arcdps_unofficial_extras_releases).
///
/// Called whenever a chat message is sent in your party/squad
///
/// *Requires the `"extras"` feature.*
#[cfg(feature = "extras")]
pub extras_chat_message: Option<ExtrasChatMessageCallback>,
}
/// Exports for usage in macros.
#[doc(hidden)]
pub mod __macro {
pub use crate::{
globals::{FreeFn, MallocFn},
util::{str_from_cstr, str_to_wide, strip_account_prefix},
};
pub use std::os::raw::{c_char, c_void};
pub use windows::Win32::{
Foundation::{HMODULE, HWND, LPARAM, WPARAM},
UI::WindowsAndMessaging::{WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP},
};
use crate::{
exports::has_e3_log_file,
globals::{init_dxgi, init_imgui, ARC_GLOBALS, IG_UI},
imgui,
panic::init_panic_hook,
};
#[cfg(feature = "log")]
use crate::{exports::has_e8_log_window, log::ArcDpsLogger};
/// Internally used function to initialize with information received from Arc.
#[inline]
#[allow(clippy::too_many_arguments)]
pub unsafe fn init(
arc_version: *const c_char,
arc_handle: HMODULE,
imgui_ctx: *mut imgui::sys::ImGuiContext,
malloc: Option<MallocFn>,
free: Option<FreeFn>,
id3d: *const c_void,
d3d_version: u32,
name: &'static str,
) {
// arc exports have to be retrieved before panic hook & logging
ARC_GLOBALS.init(arc_handle, str_from_cstr(arc_version));
// only set panic hook if log file export was found
if has_e3_log_file() {
init_panic_hook(name);
// only set logger if log file & window exports were found
#[cfg(feature = "log")]
if has_e8_log_window() {
let result = log::set_boxed_logger(Box::new(ArcDpsLogger::new(name)));
if result.is_ok() {
log::set_max_level(log::LevelFilter::Trace);
}
}
}
// initialize imgui & dxgi
init_imgui(imgui_ctx, malloc, free);
init_dxgi(id3d, d3d_version, name);
}
/// Internally used function to retrieve the [`imgui::Ui`].
#[inline]
pub unsafe fn ui() -> &'static imgui::Ui<'static> {
IG_UI.as_ref().expect("imgui ui not initialized")
}
}