{r.name}
{stamp(r.at)}{r.result}
Attempt details
{r.detail}
Checked: {stamp(r.checkedAt)}diff --git a/ale/azerothai_outcomes.lua b/ale/azerothai_outcomes.lua index 2cca248..02bb78f 100644 --- a/ale/azerothai_outcomes.lua +++ b/ale/azerothai_outcomes.lua @@ -4,6 +4,12 @@ AuthDBExecute([[CREATE TABLE IF NOT EXISTS azerothai_outcomes ( abandoned INT UNSIGNED NOT NULL DEFAULT 0,loot_events INT UNSIGNED NOT NULL DEFAULT 0, loot_items INT UNSIGNED NOT NULL DEFAULT 0,party_kills INT UNSIGNED NOT NULL DEFAULT 0, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)]]) +AuthDBExecute([[CREATE TABLE IF NOT EXISTS azerothai_currency ( + guid INT UNSIGNED PRIMARY KEY,looted_copper BIGINT UNSIGNED NOT NULL DEFAULT 0)]]) +local money={} +RegisterPlayerEvent(37,function(_,p,amount) + if p:IsPlayerbot() and amount and amount>0 then local id=p:GetGUIDLow();money[id]=(money[id] or 0)+amount end +end) local pending={} local function add(p,key,amount) if not p:IsPlayerbot() then return end @@ -28,7 +34,10 @@ local function flush() for guid,r in pairs(pending) do AuthDBExecute('INSERT INTO azerothai_outcomes(guid,quests,abandoned,loot_events,loot_items,party_kills) VALUES ('..guid..','..r.quests..','..r.abandoned..','..r.loot_events..','..r.loot_items..','..r.party_kills..') ON DUPLICATE KEY UPDATE quests=quests+VALUES(quests),abandoned=abandoned+VALUES(abandoned),loot_events=loot_events+VALUES(loot_events),loot_items=loot_items+VALUES(loot_items),party_kills=party_kills+VALUES(party_kills),updated_at=NOW()') end - pending={} + for guid,amount in pairs(money) do + AuthDBExecute('INSERT INTO azerothai_currency(guid,looted_copper) VALUES ('..guid..','..amount..') ON DUPLICATE KEY UPDATE looted_copper=looted_copper+VALUES(looted_copper)') + end + money={};pending={} end CreateLuaEvent(flush,60000,0) -- Native profiler JSON is generated every five minutes; enabling it is an operator setting. diff --git a/app/api/realm-insights/route.ts b/app/api/realm-insights/route.ts index af0f064..5da087d 100644 --- a/app/api/realm-insights/route.ts +++ b/app/api/realm-insights/route.ts @@ -6,7 +6,7 @@ import type {RuntimeConfig} from '@/core/types'; import {observeInsights,activityReason,reportWindow,summarize,DAY,type Cursor,type Evidence} from '@/core/realm-insights'; const recovery=z.object({id:z.number().int().positive(),guid:z.number().int().positive(),name:z.string().max(64),at:z.number().finite(),detail:z.string().max(255),status:z.enum(['pending','resumed','no_progress','unverified']).optional(),checkedAt:z.number().nonnegative().optional(),result:z.string().max(255).optional()}); const count=z.number().int().nonnegative(); -const outcome=z.object({guid:count,name:z.string().max(64),quests:count,abandoned:count,lootEvents:count,lootItems:count,partyKills:count,rewarded:count}); +const outcome=z.object({guid:count,name:z.string().max(64),quests:count,abandoned:count,lootEvents:count,lootItems:count,partyKills:count,rewarded:count,lootedCopper:count.default(0)}); const measurement=z.number().finite().nonnegative(); const performance=z.object({at:measurement,enabled:z.boolean(),ticks:measurement,averageTickMs:measurement,rows:z.array(z.object({category:z.string().max(40),name:z.string().max(160),calls:measurement,totalMs:measurement,averageMs:measurement,maxMs:measurement,perTickMs:measurement})).max(20)}); async function ensure(){await env.DB!.batch([ diff --git a/app/globals.css b/app/globals.css index f037c64..7f7ad56 100644 --- a/app/globals.css +++ b/app/globals.css @@ -8,4 +8,6 @@ *,::before,::after{border-color:var(--border)} .readonly-fieldset{border:0;padding:0;margin:0;min-width:0}fieldset[disabled] button,fieldset[disabled] input,fieldset[disabled] textarea{cursor:default} -.diagnostics{--diag-green:#7de3b4;--diag-amber:#f1c47a;--diag-red:#ff9aa9}.diag-hero{display:flex;justify-content:space-between;align-items:center;gap:22px;border:1px solid #35475a;border-radius:14px;padding:28px;background:radial-gradient(ellipse at right top,#244c514d,transparent 70%),linear-gradient(120deg,#1b2a3d,#101b28)}.diag-hero h2{font:28px Georgia,serif;line-height:1.25}.diag-hero p{color:#b0bfd1;font-size:14px;line-height:1.6;margin-top:10px;max-width:640px}.diag-status{display:flex;align-items:center;gap:8px;white-space:nowrap;font-size:12px;border:1px solid currentColor;border-radius:30px;padding:8px 12px}.diag-status>span{height:6px;width:6px;border-radius:100%;background:currentColor}.diagnostics .good{color:var(--diag-green)}.diagnostics .warning{color:var(--diag-amber)}.diagnostics .critical{color:var(--diag-red)}.diag-updated{display:flex;align-items:center;gap:7px;margin:14px 0 24px;font-size:12px;color:#91a5be;line-height:1.6}.diag-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:24px}.diag-metric{padding:20px;background:#111e2d;border:1px solid #2b3d50;border-radius:12px;display:flex;flex-direction:column;gap:12px;min-width:0}.diag-metric-label{display:flex;align-items:center;gap:8px;font-size:13px;color:#a9bcd4}.diag-metric>strong{font-size:30px;font-weight:500;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.diag-metric>small{font-size:11px;line-height:1.6;color:#91a5be}.diag-section-title{display:flex;align-items:center;justify-content:space-between;margin:24px 0 14px;gap:10px}.diag-section-title .fineprint{margin:0}.diag-alerts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.diag-alert{display:flex;align-items:flex-start;gap:14px;text-align:left;padding:21px;background:#121f2d;border:1px solid #3d4652;border-radius:10px;transition:background .15s,border-color .15s}.diag-alert:hover{background:#1a2b3d;border-color:#7991ac}.diag-alert>div{flex:1;min-width:0}.diag-alert h3{color:#e1e9f4;margin:4px 0 8px}.diag-alert p,.diag-recovery p{color:#aebed1;font-size:13px;line-height:1.65}.diag-alert small{display:block;color:#9dc0f4;margin-top:12px}.diag-label{text-transform:uppercase;letter-spacing:1.5px;font-size:10px}.diag-links{display:flex;gap:12px;margin-top:24px;flex-wrap:wrap}.diag-links button{display:flex;gap:20px;align-items:center;padding:12px 16px;background:#17273a;border:1px solid #344a65;border-radius:8px;font-size:13px}.diag-clear{text-align:center;padding:40px 20px;background:#11251f55;border:1px solid #284c40;border-radius:12px}.diag-clear svg{margin:0 auto 15px;color:#7de3b4}.diag-clear p{color:#a9bacd;margin:10px auto 0;font-size:13px;line-height:1.7;max-width:600px}.diag-note{padding:16px 20px;border:1px solid #2d4057;border-radius:9px;background:#142237;color:#a6bad2;font-size:12px;line-height:1.8;margin-bottom:22px}.diag-toolbar{display:flex;gap:12px;margin:22px 0;flex-wrap:wrap}.diag-search{display:flex;align-items:center;gap:10px;flex:1;min-width:200px;background:#0e1826;border:1px solid #34475e;border-radius:8px;padding:11px 14px;color:#8ba4c4}.diag-search input{width:100%;outline:none;background:transparent;color:#dce5f1;font-size:14px}.diag-toolbar select{background:#142237;border:1px solid #34475e;border-radius:8px;padding:10px 14px;font-size:13px;max-width:100%}.diag-bot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:16px}.diag-bot{padding:20px;border:1px solid #293c52;border-radius:12px;background:linear-gradient(135deg,#142334,#101b29)}.diag-bot-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diag-bot-head>div{flex:1}.diag-bot-head small{font-size:11px;display:block;margin-top:5px}.diag-avatar{display:grid;place-items:center;width:40px;height:44px;background:#28394c;border:1px solid #526079;border-radius:8px;font:24px Georgia,serif;color:#e2c48f}.diag-chip{padding:5px 8px;border:1px solid #3a4b5d;border-radius:30px;font-size:10px;color:#9cafc6;background:#0d1825;white-space:nowrap}.diag-bot-values{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:22px 0}.diag-bot-values strong{display:block;font-size:26px;font-weight:500;font-variant-numeric:tabular-nums}.diag-bot-values small{font-size:10px;display:block;margin-top:6px}.diagnostics details{border-top:1px solid #293c52;padding-top:12px;font-size:12px;color:#aebed1}.diagnostics summary{cursor:pointer;color:#a6c9f9}.diagnostics details p{font-size:12px;line-height:1.7;margin-top:10px}.diag-text-link{display:flex;align-items:center;gap:8px;color:#9fc6ff;margin-top:12px}.diag-timeline{margin-top:24px}.diag-timeline>div{padding:16px 0;border-bottom:1px solid #26384d}.diag-timeline strong{display:block;margin-top:7px}.diag-recovery{padding:20px;border:1px solid #314359;border-radius:10px;background:#121f2d;display:grid;grid-template-columns:1fr auto;gap:14px}.diag-recovery>p,.diag-recovery>details{grid-column:1/-1}.diag-recovery small{display:block;margin-top:7px;font-size:11px}.diag-bars{height:150px;display:flex;align-items:flex-end;gap:4px;border-bottom:1px solid #39556b;background:repeating-linear-gradient(to top,transparent 0,transparent 36px,#233449 37px,#233449 38px)}.diag-bars>div{flex:1;max-width:30px;min-width:1px;background:linear-gradient(to top,#377599,#83d7c4);border-radius:3px 3px 0 0}.diag-table-scroll{overflow:auto;max-height:650px}.diag-table{border-collapse:collapse;width:100%;font-size:12px;font-variant-numeric:tabular-nums}.diag-table th{text-align:left;padding:15px 18px;color:#92a9c4;background:#122033;white-space:nowrap}.diag-table td{padding:15px 18px;border-top:1px solid #26384d;white-space:nowrap}.diag-table td:first-child{white-space:normal;min-width:230px;max-width:400px;overflow-wrap:anywhere}.diag-table small{display:block;font-size:10px;margin-top:5px}.diagnostics button:focus-visible,.diagnostics select:focus-visible,.diagnostics summary:focus-visible,.diag-search:focus-within{outline:2px solid #92bdf9;outline-offset:3px}@media(max-width:1100px){.diag-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.diag-alerts{grid-template-columns:1fr}}@media(max-width:767px){.diag-hero{padding:22px;align-items:flex-start;flex-direction:column}.diag-hero h2{font-size:25px}.diag-metric{padding:15px}.diag-metric>strong{font-size:25px}.diag-metric-label{font-size:12px}.diag-alert{padding:17px}.diag-section-title{align-items:flex-start;flex-direction:column}.diag-table td,.diag-table th{padding:12px}.diag-recovery{grid-template-columns:1fr}.diag-recovery .diag-chip{justify-self:start}.diag-updated{align-items:flex-start}} +.diagnostics{--diag-green:#7de3b4;--diag-amber:#f1c47a;--diag-red:#ff9aa9}.diag-hero{display:flex;justify-content:space-between;align-items:center;gap:22px;border:1px solid #35475a;border-radius:14px;padding:28px;background:radial-gradient(ellipse at right top,#244c514d,transparent 70%),linear-gradient(120deg,#1b2a3d,#101b28)}.diag-hero h2{font:28px Georgia,serif;line-height:1.25}.diag-hero p{color:#b0bfd1;font-size:14px;line-height:1.6;margin-top:10px;max-width:640px}.diag-status{display:flex;align-items:center;gap:8px;white-space:nowrap;font-size:12px;border:1px solid currentColor;border-radius:30px;padding:8px 12px}.diag-status>span{height:6px;width:6px;border-radius:100%;background:currentColor}.diagnostics .good{color:var(--diag-green)}.diagnostics .warning{color:var(--diag-amber)}.diagnostics .critical{color:var(--diag-red)}.diag-updated{display:flex;align-items:center;gap:7px;margin:14px 0 24px;font-size:12px;color:#91a5be;line-height:1.6}.diag-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:24px}.diag-metric{padding:20px;background:#111e2d;border:1px solid #2b3d50;border-radius:12px;display:flex;flex-direction:column;gap:12px;min-width:0}.diag-metric-label{display:flex;align-items:center;gap:8px;font-size:13px;color:#a9bcd4}.diag-metric>strong{font-size:30px;font-weight:500;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.diag-metric>small{font-size:11px;line-height:1.6;color:#91a5be}.diag-section-title{display:flex;align-items:center;justify-content:space-between;margin:24px 0 14px;gap:10px}.diag-section-title .fineprint{margin:0}.diag-alerts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.diag-alert{display:flex;align-items:flex-start;gap:14px;text-align:left;padding:21px;background:#121f2d;border:1px solid #3d4652;border-radius:10px;transition:background .15s,border-color .15s}.diag-alert:hover{background:#1a2b3d;border-color:#7991ac}.diag-alert>div{flex:1;min-width:0}.diag-alert h3{color:#e1e9f4;margin:4px 0 8px}.diag-alert p,.diag-recovery p{color:#aebed1;font-size:13px;line-height:1.65}.diag-alert small{display:block;color:#9dc0f4;margin-top:12px}.diag-label{text-transform:uppercase;letter-spacing:1.5px;font-size:10px}.diag-links{display:flex;gap:12px;margin-top:24px;flex-wrap:wrap}.diag-links button{display:flex;gap:20px;align-items:center;padding:12px 16px;background:#17273a;border:1px solid #344a65;border-radius:8px;font-size:13px}.diag-clear{text-align:center;padding:40px 20px;background:#11251f55;border:1px solid #284c40;border-radius:12px}.diag-clear svg{margin:0 auto 15px;color:#7de3b4}.diag-clear p{color:#a9bacd;margin:10px auto 0;font-size:13px;line-height:1.7;max-width:600px}.diag-note{padding:16px 20px;border:1px solid #2d4057;border-radius:9px;background:#142237;color:#a6bad2;font-size:12px;line-height:1.8;margin-bottom:22px}.diag-toolbar{display:flex;gap:12px;margin:22px 0;flex-wrap:wrap}.diag-search{display:flex;align-items:center;gap:10px;flex:1;min-width:200px;background:#0e1826;border:1px solid #34475e;border-radius:8px;padding:11px 14px;color:#8ba4c4}.diag-search input{width:100%;outline:none;background:transparent;color:#dce5f1;font-size:14px}.diag-toolbar select{background:#142237;border:1px solid #34475e;border-radius:8px;padding:10px 14px;font-size:13px;max-width:100%}.diag-bot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:16px}.diag-bot{padding:20px;border:1px solid #293c52;border-radius:12px;background:linear-gradient(135deg,#142334,#101b29)}.diag-bot-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diag-bot-head>div{flex:1}.diag-bot-head small{font-size:11px;display:block;margin-top:5px}.diag-avatar{display:grid;place-items:center;width:40px;height:44px;background:#28394c;border:1px solid #526079;border-radius:8px;font:24px Georgia,serif;color:#e2c48f}.diag-chip{padding:5px 8px;border:1px solid #3a4b5d;border-radius:30px;font-size:10px;color:#9cafc6;background:#0d1825;white-space:nowrap}.diag-bot-values{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:22px 0}.diag-bot-values strong{display:block;font-size:26px;font-weight:500;font-variant-numeric:tabular-nums}.diag-bot-values small{font-size:10px;display:block;margin-top:6px}.diagnostics details{border-top:1px solid #293c52;padding-top:12px;font-size:12px;color:#aebed1}.diagnostics summary{cursor:pointer;color:#a6c9f9}.diagnostics details p{font-size:12px;line-height:1.7;margin-top:10px}.diag-text-link{display:flex;align-items:center;gap:8px;color:#9fc6ff;margin-top:12px}.diag-timeline{margin-top:24px}.diag-timeline>div{padding:16px 0;border-bottom:1px solid #26384d}.diag-timeline strong{display:block;margin-top:7px}.diag-recovery{padding:20px;border:1px solid #314359;border-radius:10px;background:#121f2d;display:grid;grid-template-columns:1fr auto;gap:14px}.diag-recovery>p,.diag-recovery>details{grid-column:1/-1}.diag-recovery small{display:block;margin-top:7px;font-size:11px}.diag-bars{height:150px;display:flex;align-items:flex-end;gap:4px;border-bottom:1px solid #39556b;background:repeating-linear-gradient(to top,transparent 0,transparent 36px,#233449 37px,#233449 38px)}.diag-bars>div{flex:1;max-width:30px;min-width:1px;background:linear-gradient(to top,#377599,#83d7c4);border-radius:3px 3px 0 0}.diag-table-scroll{overflow:auto;max-height:650px}.diag-table{border-collapse:collapse;width:100%;font-size:12px;font-variant-numeric:tabular-nums}.diag-table th{text-align:left;padding:15px 18px;color:#92a9c4;background:#122033;white-space:nowrap}.diag-table td{padding:15px 18px;border-top:1px solid #26384d;white-space:nowrap}.diag-table td:first-child{white-space:normal;min-width:230px;max-width:400px;overflow-wrap:anywhere}.diag-table small{display:block;font-size:10px;margin-top:5px}.diagnostics button:focus-visible,.diagnostics select:focus-visible,.diagnostics summary:focus-visible,.diag-search:focus-within{outline:2px solid #92bdf9;outline-offset:3px}@media(max-width:1100px){.diag-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.diag-alerts{grid-template-columns:1fr}}@media(max-width:767px){.diag-hero{padding:22px;align-items:flex-start;flex-direction:column}.diag-hero h2{font-size:25px}.diag-metric{padding:15px}.diag-metric>strong{font-size:25px}.diag-metric-label{font-size:12px}.diag-alert{padding:17px}.diag-section-title{align-items:flex-start;flex-direction:column}.diag-table td,.diag-table th{padding:12px}.diag-recovery{grid-template-columns:1fr}.diag-recovery .diag-chip{justify-self:start}.diag-updated{align-items:flex-start}} + +.diag-bot-values{grid-template-columns:repeat(2,minmax(0,1fr))}.diag-bot-values strong{overflow-wrap:anywhere;font-variant-numeric:tabular-nums} diff --git a/app/realm-diagnostics.tsx b/app/realm-diagnostics.tsx index d4d7a3c..da29808 100644 --- a/app/realm-diagnostics.tsx +++ b/app/realm-diagnostics.tsx @@ -1,6 +1,7 @@ 'use client'; import {useEffect,useState} from 'react'; import {Activity,CheckCircle2,AlertTriangle,ArrowRight,Clock3,Search,ShieldCheck,Package,ScrollText,Users,Timer} from 'lucide-react'; +import {formatCopper} from '@/core/realm-outcomes'; import type {Attention,Perf} from '@/core/realm-outcomes'; import type {Evidence} from '@/core/realm-insights'; type Recovery={name:string;at:number;status:string;result:string;detail:string;checkedAt:number|null}; @@ -21,7 +22,7 @@ export function RealmDiagnostics({page,onNavigate,initialQuery=''}:{page:string; const attempts=recoveries.filter(r=>r.name.toLowerCase().includes(query.toLowerCase())&&(filter==='all'||r.status===filter)); return
{page==='Needs attention'?'Prioritized signals, with a direct path to the details.':page==='Gameplay Results'?'Native quest, loot, and group observations from the last 24 hours.':page==='Recovery Results'?'Native follow-up separates an attempted fix from observed progress.':'Measured execution time, sampled every five minutes.'}
The monitored checks are clear. This is not proof that every gameplay path is bug-free.
Objectives completed: {b.totals.quests} · Abandoned: {b.totals.abandoned} · Loot collections: {b.totals.lootEvents}
Observed bot companions: {b.groupBots.filter(n=>n!==b.name).join(', ')||'None reported'}
{e.detail} · +{e.amount}
Objectives completed: {b.totals.quests} · Abandoned: {b.totals.abandoned} · Loot collections: {b.totals.lootEvents}
Observed bot companions: {b.groupBots.filter(n=>n!==b.name).join(', ')||'None reported'}
{e.detail} · +{e.kind==='lootedCopper'?formatCopper(e.amount):e.amount}
Progress resumed means native movement over five yards, a map change, or a level change was observed within the follow-up window. It does not prove lasting recovery or that the intervention caused it. Three minutes without that evidence is flagged for review. Restarts or unavailable residents may prevent verification.
{r.result}
{r.detail}
Checked: {stamp(r.checkedAt)}Results appear after the native guard attempts a recovery. No intervention is forced to populate this view.
{stamp(data.performance.history[0]?.at??null)} — {stamp(perf?.at??null)}. Timers can overlap; they must not be added together or interpreted as CPU utilization.
| Operation | Calls | Mean | Maximum | Per full tick |
|---|---|---|---|---|
| {r.name}{r.category} | {Math.round(r.calls).toLocaleString()} | {r.averageMs.toFixed(3)} ms | {r.maxMs.toFixed(3)} ms | {r.perTickMs.toFixed(3)} ms |
Waiting for the first native profiler export.
}