ProjectEbonhold server handoff — automation redesign + affix vendor¶
Audience: ProjectEbonhold addon + server-core maintainers
EbonBuilds status: client stepping stones for WP1–WP5 are on main; remaining acceptance criteria need PE/core.
Design: automation-server-redesign.md
Issues: #49 (umbrella) · #52 (WP3) · #54 (WP5) · #112 (vendor TOC)
This document lists exact PE surfaces EbonBuilds already probes. Do not invent live opcodes here — IDs below are suggested free ranges audited against PE client projectebonhold.lua (addonVersion 37, MPQ work copy). Final allocation belongs to PE maintainers (must not collide with Affix 310–315, Echo discovery 330–333, Perk loadouts 340–347).
Transport (unchanged): AddonMsg prefix AAM0x9, whisper-to-self, eventId\tbody, 240-byte soft limit with existing chunking (@mid\tiii/iii\tchunk).
Client readiness (already on EbonBuilds main)¶
| Capability / API | EbonBuilds behavior today |
|---|---|
GetCapabilities().intentQueueClient |
IntentQueue one-in-flight guard |
GetCapabilities().serverIntentAck |
true only when Perks.GetIntentAck exists |
GetCapabilities().serverIntentSubmit |
true when Perks/PerkService.SubmitAutomationIntent exists |
ProjectAPI.GetIntentAck() |
Reads Perks.GetIntentAck() or Perks.lastIntentAck; else nil |
ProjectAPI.SubmitAutomationIntent(...) |
Forwards to PE when present; else false, "unavailable" |
IntentQueue.PollAck |
Prefers matching intentId + accepted/rejected over board/pending/TTL signals |
IntentQueue.TryBegin |
Calls SubmitAutomationIntent after enqueue when the adapter exists (ignored if unavailable) |
GetCapabilities().constraintsClient |
Local pack + constraintsHash on intents |
GetCapabilities().serverConstraints |
true when Perks.SubmitAutomationConstraints or Perks.GetConstraintsAck exists |
ProjectAPI.SubmitAutomationConstraints(wire, hash) |
Forwards to PE when present; else false, "unavailable" |
AutomationConstraints.MaybeSubmitToServer |
Uploads on board attach when serverConstraints; dedupes by hash |
GetCapabilities().serverPolicy |
true when Perks.GetAutomationPolicy exists or Perks.serverPolicyEnabled |
ProjectAPI.GetAutomationPolicy() |
Reads PE policy table when exported; else nil |
| Affix Shop/Vendor | Soft-fail: IsMerchantAffixAvailable() false until ItemPurchasePopup:ShowPurchase exists |
EbonBuilds must not steal ProjectEbonhold.onEventReceived (adapter rules from #42). Extend PE officially; EbonBuilds keeps reading through ProjectAPI.
#52 — WP3 intent queue with server acks¶
Required PE Lua surface¶
-- PerkService (or Perks helpers)
function PerkService:SubmitAutomationIntent(intentId, offerId, action, targetSlot, constraintsHash)
-- CS intent request; local refuse only (pending / exclusivity)
end
-- After SS ack handler:
-- Perks.lastIntentAck = {
-- intentId = <number>,
-- status = "accepted" | "rejected",
-- boardState = "OPEN"|"FROZEN_PENDING"|"CONFIRMED"|"SPENT"|nil,
-- reasonCode = <TOKEN>,
-- reason = <short string>,
-- action = <echoed verb>|nil,
-- targetSlot = <n>|nil,
-- }
function Perks:GetIntentAck()
return self.lastIntentAck
end
Suggested AddonMsg IDs (TBD — free in PE v37 audit)¶
| Direction | Suggested ID | Name (suggested) | Body sketch |
|---|---|---|---|
| CS | 350 | REQUEST_AUTOMATION_INTENT |
intentId\|offerId\|action\|targetSlot\|constraintsHash |
| SS | 550 | SEND_AUTOMATION_INTENT_ACK |
intentId\|accepted\|boardState\|reasonCode\|reason or key=value form from redesign §3 |
Apply path after accepted may still use existing:
| Action | Existing CS | Existing SS |
|---|---|---|
| Freeze | REQUEST_FREEZE_PERK 207 |
SEND_FREEZE_PERK_RESULT 104 |
| Banish | REQUEST_BANISH_PERK 203 |
SEND_BANISH_REPLACEMENT_PERK 103 |
| Reroll | REQUEST_REROLL 27 |
SEND_PLAYER_PERK_CHOICE 16 |
| Select | REQUEST_PLAYER_PERK_SELECTION 17 |
SEND_PLAYER_PERK_SELECTION_RESULT 1000 |
Server rules¶
- One in-flight intent per player; second →
rejected+reasonCode=intent_in_flight(orwait). - Reject intent that would
rerollwhile board isFROZEN_PENDING/CONFIRMED(#38 invariant). - Echo
intentIdon every ack soIntentQueuecan match.
#54 — WP5 constraints upload + serverPolicy¶
Required PE Lua surface¶
function Perks:SubmitAutomationConstraints(wireBlob, constraintsHash)
-- CS upload (chunk-safe); return accepted, reason
end
function Perks:GetConstraintsAck()
-- { constraintsHash, status="accepted"|"stale"|"rejected", reasonCode?, reason? }
return self.lastConstraintsAck
end
Wire format already packed by EbonBuilds (AutomationConstraints):
v=1;rerollMode=ev;freezePenaltyPct=10;protectFamilies=caster,tank;policy=g:296:bos,s:101:np;maxRerolls=8
Suggested AddonMsg IDs (TBD)¶
| Direction | Suggested ID | Name (suggested) | Notes |
|---|---|---|---|
| CS | 351 | REQUEST_AUTOMATION_CONSTRAINTS |
body = wire blob (chunked) |
| SS | 551 | SEND_AUTOMATION_CONSTRAINTS_ACK |
echo constraintsHash + status |
| CS | 352 | REQUEST_AUTOMATION_POLICY |
optional policy poll |
| SS | 552 | SEND_AUTOMATION_POLICY |
boardState + action + ranks + reason (redesign §3) |
When policy channel is live, export either:
Perks.GetAutomationPolicy()→ policy table, orPerks.serverPolicyEnabled = true(plus whatever SS handler fillslastAutomationPolicy)
EbonBuilds flips serverPolicy only via those probes — no silent hard-code. Hard rules always win; soft prefs may emit constraint_ignored_* reason codes in Logbook text.
#112 — load item_purchase.lua¶
Finding (PE ref v3.6.0.868): modules/itemPurchase/item_purchase.lua defines ItemPurchasePopup / ShowPurchase, but stock projectebonhold.toc does not list it.
Ask (exact TOC patch): after the extraction module lines, add:
modules\extraction\extraction_service.lua
modules\extraction\extraction.lua
+
+modules\itemPurchase\item_purchase.lua
Ready-to-apply patch file: pe-item-purchase-toc.patch.
Full line to insert (Windows TOC path separators, matching PE style):
modules\itemPurchase\item_purchase.lua
Load any hard deps the file needs (currently self-contained global ItemPurchasePopup; hooks MerchantFrame_ConfirmExtendedItemCost). Optional: document a dedicated affix-purchase entry if PE wants a narrower API than ShowPurchase.
Local installs (dev machines — not publishable PE source)¶
PE AddOn source is outside this repo (not ours to PR upstream). Local TOC patches applied for testing:
| Tree | Patch |
|---|---|
MPQ work copy …\Work\Interface\AddOns\ProjectEbonhold\projectebonhold.toc |
TOC line added |
Repo .work/pe-rar-api/ProjectEbonhold/projectebonhold.toc |
TOC line added |
Live %\AppData%\Local\ebonhold\Interface\AddOns\ProjectEbonhold\ |
Full addon copy installed with TOC line (reload UI / switcher to server PE mode to verify Shop buttons) |
Until PE maintainers ship the TOC line in the official distribution, EbonBuilds Affix Shop/Vendor buttons stay hidden on stock clients (ProjectEbonholdAffixBridge.IsMerchantAffixAvailable()); Anvil/Extraction already works.
Compat matrix¶
| Client | PE | Behavior |
|---|---|---|
| Current EbonBuilds | Current PE (no intent/constraints) | Client decide + client IntentQueue + soft vendor hide |
| Current EbonBuilds | PE with GetIntentAck only |
Queue prefers SS ack; decide still client |
| Current EbonBuilds | PE with constraints upload | serverConstraints=true; Autopilot still client decide until serverPolicy |
| Future EbonBuilds | PE with policy | Executor-only when serverPolicy=true |
Out of scope¶
- AffixServer learned-affix protocol (
310–314) — unchanged - DPS benchmarks (#46) as automation constraints
- Stealing PE
onEventReceivedhandlers from EbonBuilds