e6be8f0fd7786a9e1781db2e71cb5b9146f04980 on August 17, 2026. Its project file reported base version 3.2.1.17 and target net10.0-windows7.0. Refresh repository source, package metadata, Dalamud API, and game structures before implementation.01 / Overview
ECommons is a toolbox, not one feature.
It wraps recurring Dalamud and FFXIVClientStructs work: service access, frame-driven tasks, addon callbacks and semantic UI wrappers, throttling, configuration, events, hooks, logging, reflection, IPC, game data, and general utilities.
| Need | Useful surface |
|---|---|
| Shared Dalamud service access | Svc |
| Ordered nonblocking work | NeoTaskManager.TaskManager |
| Converted addon callback values | Automation.Callback |
| Semantic addon control | AddonMaster |
| Structured addon reads | AtkReader |
| Time or frame pacing | EzThrottler, FrameThrottler |
| Declarative cross-plugin contracts | EzIPC |
| Last-resort private adapter | DalamudReflector, ReflectionHelper |
Adopt a helper when it provides a clearer maintained contract, not merely a shorter line. Compare lifecycle, errors, patch sensitivity, dependency cost, and fit with existing architecture.
02 / Architecture
Initialize once, dispose once, request only what you use.
using Dalamud.Plugin;
using ECommons;
public sealed class Plugin : IDalamudPlugin
{
public Plugin(IDalamudPluginInterface pluginInterface)
{
ECommonsMain.Init(pluginInterface, this);
}
public void Dispose()
{
ECommonsMain.Dispose();
}
}At the observed snapshot, ECommons accepts an IDalamudPlugin or IAsyncDalamudPlugin instance. Disposal cleans up ECommons-managed task managers, hooks, IPC, shared data, events, config/window helpers, throttlers, DTR entries, and related components.
Advanced modules
| Module | Purpose | Caution |
|---|---|---|
All | All advanced modules | Highest surface; avoid by default. |
DalamudReflector | Internal Dalamud/plugin reflection | Private contracts and update monitoring. |
ObjectFunctions | Object-related game functions | Patch-sensitive hooks. |
ObjectLife | Object lifecycle tracking | Additional update/hook state. |
SplatoonAPI | Optional Splatoon integration | Cross-plugin dependency behavior. |
VfxTracking | VFX lifecycle tracking | Patch-sensitive hooks. |
ECommonsMain.Init(pluginInterface, this, Module.DalamudReflector);Base initialization exposes many ordinary helpers. Passing Module.All as a default adds cost and private surfaces without evidence that a feature needs them.
03 / Source map
Know which layer owns the operation.
Task managers, callback conversion, chat, commands, key input, and cutscene helpers.
Addon discovery, AddonMaster actions, and AtkReader snapshots.
Login, territory, framework, addon, signature, and game-hook lifecycles.
Declarative calls/events plus cross-plugin shared storage.
Player state, Lumina sheets, enums, lazy data, and game functions.
Windows, ImGui, DTR, logging, throttling, math, strings, and buffers.
04 / Services
Svc centralizes injected Dalamud services.
using ECommons.DalamudServices;
if (Svc.ClientState.IsLoggedIn)
{
var territory = Svc.ClientState.TerritoryType;
Svc.Log.Debug("Current territory: {Territory}", territory);
}This removes plumbing and creates a familiar adapter pattern. It can also hide dependencies and complicate tests when static access spreads. Keep domain logic behind narrow interfaces and use Svc at the Dalamud adapter boundary. Player availability never proves that territory, addon, conditions, or an optional dependency are ready.
05 / NeoTaskManager
Run stateful work on framework updates without blocking.
TaskManager is a frame-driven queue, not a background thread. The observed return contract is:
| Result | Meaning |
|---|---|
false | Not complete; poll again on a later update. |
true | Complete; advance to the next task. |
null | Abort the queue. |
using ECommons.Automation.NeoTaskManager;
private readonly TaskManager tasks = new(new TaskManagerConfiguration
{
TimeLimitMS = 30_000,
AbortOnTimeout = true,
AbortOnError = true,
TimeoutSilently = false,
ShowDebug = false,
ShowError = true,
});
tasks.Enqueue(() => IssueCommand(), "Issue command");
tasks.Enqueue(
() => IsTransitionComplete(),
"Wait for transition",
new TaskManagerConfiguration(timeLimitMS: 45_000));An action task issues a short operation once. A predicate observes state later. Never put synchronous I/O, Thread.Sleep, or a busy loop in a framework callback.
Operations, ordering, and events
tasks.IsBusy;
tasks.NumQueuedTasks;
tasks.Progress;
tasks.CurrentTask;
tasks.RemainingTimeMS;
tasks.Abort();
tasks.AbortCurrent();The manager supports delays, front insertion, stacked sequences, and step-mode debugging. Document queue ordering so inserted work cannot violate invariants. Timeout, completion, exception, and companion-action callbacks can observe or alter task behavior; never extend time forever or swallow a failure into a wedged queue.
- Stable task names appear in UI, logs, and tests.
- Enter actions run once; predicates are safe to poll.
- Timeouts are finite and operation-specific.
- Retries are allowed only for idempotent work.
- Dispose and cancellation clear active state.
- Relog, teleport, and zoning use readiness observations, not delays alone.
06 / Automation helpers
Use semantic helpers, then verify game state.
Callback conversion
using ECommons.Automation;
// Callback indexes and values are addon/version specific.
Callback.Fire(addon, updateState: false, 0);
Callback.Fire(addon, updateState: true, 3, 1);
Callback.Fire(addon, updateState: false, "text");The observed implementation converts common values such as int, uint, float, bool, string, and existing AtkValue entries. It solves memory conversion, not semantic discovery. Validate pointer, visibility, readiness, and callback meaning.
Chat and commands
Chat.SendMessage("A validated chat message");
Chat.ExecuteCommand("/example");
var safe = Chat.SanitiseText(untrustedText);The validated path enforces byte and sanitization rules. SendMessageUnsafe should receive only already-validated internal bytes. Allow-list values when commands include external input, then observe completion.
Windows key input
WindowsKeypress.SendKeypress(Keys.Escape);
WindowsKeypress.SendKeyDown(Keys.W);
WindowsKeypress.SendKeyUp(Keys.W);Always release held keys on cancellation, error, focus loss, and disposal. Prefer game or addon APIs when available.
07 / UI helpers
AddonMaster communicates intent better than callback numbers.
The observed source contains well over one hundred semantic wrappers across dialogs, inventory, retainers, duties, shops, crafting, gathering, travel, social UI, portraits, and specialized content.
| Area | Examples |
|---|---|
| Dialogs | SelectYesno, SelectString, SelectIconString, SelectOk, InputNumeric, Talk |
| Inventory and items | Inventory, InventoryLarge, ItemFinder, ItemSearchResult |
| Retainers | RetainerList, RetainerSell, RetainerTaskAsk, transfer views |
| Duty and party | ContentsFinderConfirm, settings/status, LookingForGroup |
| Craft and gather | Gathering, RecipeNote, Synthesis |
| Shops and travel | Shop, currency exchanges, Teleport, world travel |
if (GenericHelpers.TryGetAddonByName<AddonSelectYesno>(
"SelectYesno", out var addon))
{
new AddonMaster.SelectYesno(&addon->AtkUnitBase).Yes();
}Confirm current generated types and constructors. A semantic Yes() still needs evidence that the expected prompt is open before a destructive confirmation.
AtkReader
AtkReader implementations expose structured snapshots for known UI surfaces such as select lists, retainers, synthesis, letters, banners, and specialized addons. Separate “read current state” from “perform action.” Never retain unmanaged pointers after the addon or callback scope can be reconstructed.
08 / State and pacing
Helpers contribute to readiness; they do not define it.
GameHelpers.Player reduces local-player boilerplate. GenericHelpers.IsOccupied() combines many conditions. Neither is a universal workflow oracle. Build a narrow readiness predicate for the next action and require consecutive passes after disruptive transitions.
Time and frame throttling
using ECommons.Throttlers;
if (EzThrottler.Throttle("example-refresh", 1_000))
RefreshSnapshot();
if (FrameThrottler.Throttle("example-frame-check", 30))
CheckState();
EzThrottler.Reset("example-refresh");Stable, namespaced keys prevent collisions. A throttle controls frequency; it does not provide cancellation, mutual exclusion, readiness, or idempotency.
09 / Configuration and windows
Choose helpers by lifecycle clarity.
EzConfig
using ECommons.Configuration;
EzConfig.Init();
var config = EzConfig.LoadConfiguration<PluginConfiguration>(
"configuration.json");
EzConfig.Save();Decide ownership, migrations, malformed-data recovery, save timing, multiple-file needs, and sensitive-data policy. Never silently discard user config after a parse or migration failure.
EzConfigGui
EzConfigGui can register a conventional main/config window, draw and open events, toggling, and config saving. It fits small conventional plugins. Multiple independently owned windows, complex docking, or testable presentation boundaries may justify an explicit Dalamud WindowSystem.
10 / Events and hooks
Central cleanup helps; current signatures remain mandatory.
ProperOnLogin and EzEvent
using ECommons.Events;
ProperOnLogin.Register(() =>
{
// Recheck exact readiness and rebuild idempotently.
});The event waits beyond the earliest login signal, but callbacks must remain idempotent. EzEvent helpers centralize framework, territory, logout, addon, and other lifecycle subscriptions. Queue longer work instead of running it in the handler.
EzHook
Hook helpers reduce signature and disposal boilerplate. They do not make a stale signature safe. Require a current signature or generated interop target, original-delegate path, exception containment, thread awareness, deterministic enable/disable, and live runtime verification.
11 / IPC and reflection
EzIPC simplifies declarations; the public contract still belongs to you.
Provider
using ECommons.EzIpcManager;
public sealed class PublicApi
{
public PublicApi() => EzIPC.Init(this);
[EzIPC]
public void Refresh() => QueueRefresh();
[EzIPC("GetState")]
public string ReadState() => "ready";
[EzIPCEvent]
public Action<string>? StateChanged;
}Subscriber
public sealed class PublicApiClient
{
public PublicApiClient() => EzIPC.Init(this, "ExamplePlugin");
[EzIPC] public readonly Action Refresh = null!;
[EzIPC("GetState")] public readonly Func<string> ReadState = null!;
[EzIPCEvent]
private void StateChanged(string state) { }
}Document full tags, signatures, direction, thread context, absence behavior, and version semantics. Use applyPrefix: false only when a provider intentionally uses a different full-tag convention.
DalamudReflector and ReflectionHelper
var value = instance.GetFoP("MemberName");
instance.SetFoP("MemberName", newValue);
var result = instance.Call("MethodName", arguments);Reflection remains a private contract. Prefer IPC. If reflection is necessary, pin the observed target, validate types, read back writes, invalidate on reload, and fail the optional feature cleanly. The companion Reflection and IPC guide provides the complete decision matrix, provider/subscriber code, direct discovery, Dropbox case study, caching, and troubleshooting.
12 / Additional areas
Use utility breadth without creating hidden policy.
Typed Lumina helpers and enums for jobs, worlds, territories, actions, items, and classifications.
Vectors, angles, bitmasks, directions, numeric helpers, and conversions. State units and coordinate spaces.
Dalamud logs plus selected user-visible messages. Never log credentials or unrelated sensitive state.
Server-info-bar entries, tooltips, and clicks. Dispose entries and keep callbacks short.
Bounded recent samples and diagnostic histories. Choose capacity from a retention requirement.
Cutscene and other visible-behavior helpers need explicit opt-in and state restoration.
13 / Adoption
Introduce one adapter and one behavior at a time.
| Question | Favor ECommons | Favor local adapter |
|---|---|---|
| Maintained semantic helper exists? | Yes, exact operation | No, only a loose low-level primitive |
| Dependency already present? | Yes | No, need is one small stable Dalamud API |
| Patch-sensitive logic centralized? | Yes; update cadence fits | Plugin needs tighter evidence/control |
| Lifecycle fit? | Matches plugin ownership | Independent scopes or test seams differ |
| Private reflection either way? | Convenient discovery | Stricter typed result and diagnostics needed |
Low-risk first
- Pin the reviewed dependency.Record source/package version and target framework.
- Initialize and dispose.Prove reload and teardown before more adoption.
- Confine services.Keep
Svcin outer adapters where practical. - Add pacing.Use throttlers for new bounded polling.
- Use a semantic UI helper.Adopt Callback or one AddonMaster wrapper for new work.
- Test the adapter.Keep domain logic independent of static/framework details.
Medium risk
Migrate callback/chat conversion after parity tests, introduce AddonMaster one addon at a time, try NeoTaskManager on a new workflow before replacing a mature runner, create a newly versioned EzIPC API, and test event/hook disposal before expanding.
Deep integration
Mature task runners, multi-window architecture, configuration migrations, all IPC wrappers, and broad advanced modules need measured benefit and staged migration. Do not mix dependency adoption, architecture replacement, and behavior changes in one review.
14 / Validation
Separate source confidence from runtime acceptance.
- ECommons commit/package and target framework are recorded.
- Advanced modules are the minimum necessary.
ECommonsMain.InitandDisposerun exactly once.- Framework callbacks never block.
- Queued tasks have finite timeout and cancellation.
- Addon interactions validate the expected addon and prompt/state.
- Raw callback values are source-backed for the supported version.
- Throttle keys are stable and namespaced.
- Keys, hooks, events, IPC, and DTR entries clean up on error/reload.
- Optional dependencies become feature-unavailable, not plugin-fatal.
- Reflection has version/type checks, read-back, diagnostics, and invalidation.
- Configuration migration and malformed-data recovery are tested.
- Static tests, build, package, and in-game evidence are reported separately.
15 / Sources