Scope. SND expands the native macro system and includes a Lua engine. This guide is about graduating an SND workflow into a standalone Dalamud plugin—not embedding undocumented private helpers or promising that a particular automation remains safe after an update.

01 / Choose the form

Use the smallest architecture that fits.

SND is excellent for quickly expressing a personal, sequential workflow. A standalone plugin becomes worthwhile when the work needs reusable UI, persistent configuration, release packaging, multiple consumers, cancellation, recovery, or long-lived state.

NeedPrefer
Personal or experimental sequenceSND Lua script
Configurable script with an intentional dependencySND plus documented modules or IPC
Reusable UI, state, and release packageStandalone Dalamud plugin
Long-running recovery and cancellationPlugin with a task runner

02 / Target architecture

Split behavior into four layers.

04
Presentation

Configuration, status, controls, and failure messages. It observes the workflow; it does not contain it.

03
Workflow

Task-specific decisions and composition. It calls shared operations instead of duplicating them.

02
Task runner

Ordered nonblocking steps with enter actions, completion predicates, timeouts, retries, cancellation, and diagnostics.

01
Shared commands

Chat, input, addon interaction, IPC, navigation, and other operations tested once and reused.

This boundary is the heart of the port. A shared operation belongs in one service, a workflow contains only its own decisions, and the task runner owns sequence mechanics.

03 / Translation

Turn waits into observable state.

A sequential script can suspend itself. A plugin must remain responsive while the framework continues to update and draw.

SND-style pseudocode

yield("/command destination")
yield("/wait 1")

while IsBusy() do
    yield("/wait 0.1")
end

Plugin-style pseudocode

runner.Enqueue(new TaskStep(
    name: "Issue command",
    onEnter: () => commands.Execute("destination"),
    isComplete: () => true,
    timeout: TimeSpan.FromSeconds(2)));

runner.Enqueue(new TaskStep(
    name: "Observe transition start",
    isComplete: () => gameState.IsBusy,
    timeout: TimeSpan.FromSeconds(10)));

runner.Enqueue(new TaskStep(
    name: "Wait for transition end",
    isComplete: () => !gameState.IsBusy,
    timeout: TimeSpan.FromSeconds(45),
    maxRetries: 1));

The runner polls completion from the framework/update loop. The example shows the contract shape; it is not a drop-in promise of exact API names.

04 / Readiness

A delay is not a readiness check.

After relog, teleport, zoning, or addon reconstruction, require several consecutive ready observations before the next action. Three passes is a practical default.

CommandBusy startsBusy endsReady × 3Settle

Reset the pass counter whenever readiness becomes false. Keep the predicate specific: player available, expected territory loaded, not between areas, required addon present, and no occupied state that invalidates the next action.

05 / Step contract

Every step must finish or fail clearly.

Stable name

Use the same name in UI, logs, reports, and tests.

Enter once

Run the action once per attempt, not on every poll.

Safe predicate

Transient unavailable state returns false instead of crashing.

Finite timeout

No step waits forever or silently wedges the sequence.

Guarded retry

Retry only when repeating the operation is safe.

Cancellation

Stop, dispose, and reload paths leave no active work behind.

06 / Porting workflow

Move in small, testable slices.

  1. Inventory the script.Record every command, condition, wait, loop, external call, mutable variable, and exit path.
  2. Extract shared operations.Reuse or create a service; do not paste command helpers into the workflow.
  3. Draw state transitions.Include busy-start and busy-end observations, not just the happy path.
  4. Create named steps.Replace each blocking wait or loop with pollable state.
  5. Add failure mechanics.Implement finite timeouts and cancellation before retries.
  6. Gate readiness.Use consecutive checks after relog, teleport, zone, and addon reconstruction.
  7. Expose diagnostics.Show current step, elapsed time, retries, and last failure.
  8. Compose gradually.Test commands, short sequences, and finally the complete workflow.

07 / Validation

Prove the behavior at each layer.

  • No Thread.Sleep, busy loop, or synchronous long wait blocks the framework callback.
  • Stop and dispose cancel active work and unregister events exactly once.
  • Every step completes, times out, is cancelled, or emits a specific failure.
  • Retries cannot repeat destructive work without an explicit idempotency guard.
  • Relog and zone transitions require observed readiness, not elapsed time alone.
  • Unavailable player, territory, addon, IPC provider, or dependency state is handled.
  • Debug and Release builds use the current toolchain contract.
  • The packaged archive contains the intended DLL, manifest, and approved assets only.
  • Live in-game validation is recorded separately from compile and package success.

08 / Upstream references

Refresh mutable details at the source.

Machine-use note. This guide intentionally excludes private helper libraries, local paths, exact automation targets, and internal application code. Treat pseudocode as architecture, preserve evidence boundaries, and prefer current upstream docs over cached examples.