Separate looted currency and preserve growing realm state in atomic chunks
Core tests / core (push) Successful in 49s
Core tests / core (push) Successful in 49s
This commit is contained in:
@@ -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 <div className="diagnostics"><div className="diag-hero"><div><div className="eyebrow">EVIDENCE FROM YOUR REALM</div><h2>{page==='Needs attention'?'A clear view of what needs you.':page==='Gameplay Results'?'Plans become progress.':page==='Recovery Results'?'Did the recovery help?':'Inside the bot engine.'}</h2><p>{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.'}</p></div><span className={'diag-status '+(stale?'warning':'good')}><span/>{stale?'Collection delayed':'Collector active'}</span></div><p className="diag-updated"><Clock3 size={14}/> Last collection {stamp(data.collectedAt)} · View refreshes every 15 seconds</p>{error&&<div className="notice" role="alert">{error}</div>}
|
||||
{page==='Needs attention'&&<><div className="diag-metrics"><Metric label="Needs attention" value={data.attention.length} note="Current signals, not historical totals" icon={AlertTriangle} tone={data.attention.length?'warning':'good'}/><Metric label="Critical" value={critical} note="Resource pressure or population over cap" icon={ShieldCheck} tone={critical?'critical':''}/><Metric label="Online residents" value={o.bots.length} note="Target: 50 · 25 per faction" icon={Users}/><Metric label="Observed quest rewards" value={metrics.rewarded??0} note="New rewarded quest records · 24 hours" icon={ScrollText}/></div><div className="diag-section-title"><h2>Follow-up queue</h2><span className="fineprint">Critical signals first</span></div>{data.attention.length?<div className="diag-alerts">{data.attention.map(a=><button className={'diag-alert '+a.severity} key={a.key} onClick={()=>onNavigate(a.target,a.name)}><AlertTriangle size={20}/><div><span className="diag-label">{a.severity}</span><h3>{a.title}</h3><p>{a.detail}</p><small>Open {a.target}</small></div><ArrowRight size={18}/></button>)}</div>:<div className="diag-clear"><CheckCircle2 size={36}/><h3>No current attention signals</h3><p>The monitored checks are clear. This is not proof that every gameplay path is bug-free.</p></div>}<div className="diag-links">{['Gameplay Results','Recovery Results','Bot Performance'].map(p=><button key={p} onClick={()=>onNavigate(p)}>{p}<ArrowRight size={16}/></button>)}</div></>}
|
||||
{page==='Gameplay Results'&&<><div className="diag-metrics"><Metric label="Quest rewards" value={metrics.rewarded??0} note="New rewarded quest records" icon={ScrollText}/><Metric label="Items looted" value={metrics.lootItems??0} note={`${metrics.lootEvents??0} native loot collections`} icon={Package}/><Metric label="Shared-target kills" value={metrics.partyKills??0} note="Killer had a nearby bot fighting the same target" icon={Users}/><Metric label="Quests abandoned" value={metrics.abandoned??0} note="An abandonment is not automatically a bug" icon={Activity}/></div><div className="diag-note">Evidence since {stamp(o.startedAt)}. Native counters last collected {stamp(o.at)}. Quest rewards count newly recorded quest IDs; repeated rewards for an already recorded quest may not increase this count. Quest objectives completed: <strong>{metrics.quests??0}</strong>. Party actions rejected: <strong>{metrics.partyFailed??0}</strong>. Gaps or a server reload can miss unflushed events.</div><div className="diag-toolbar"><label className="diag-search"><Search size={17}/><input aria-label="Search gameplay results" placeholder="Find a resident or zone…" value={query} onChange={e=>setQuery(e.target.value)}/></label><select aria-label="Filter gameplay results" value={filter} onChange={e=>setFilter(e.target.value)}><option value="all">All online residents</option><option value="grouped">Currently grouped</option></select></div><div className="diag-bot-grid">{visibleBots.map(b=><section className="diag-bot" key={b.name}><div className="diag-bot-head"><span className="diag-avatar">{b.name[0]}</span><div><h3>{b.name}</h3><small>{b.zone}</small></div><span className={'diag-chip '+(b.grouped?'good':'')}>{b.grouped?'Grouped':'Solo / unknown'}</span></div><div className="diag-bot-values"><div><strong>{b.totals.rewarded}</strong><small>Quest rewards</small></div><div><strong>{b.totals.lootItems}</strong><small>Items looted</small></div><div><strong>{b.totals.partyKills}</strong><small>Shared kills</small></div></div><details><summary>Evidence details</summary><p>Objectives completed: {b.totals.quests} · Abandoned: {b.totals.abandoned} · Loot collections: {b.totals.lootEvents}</p><p>Observed bot companions: {b.groupBots.filter(n=>n!==b.name).join(', ')||'None reported'}</p><button className="diag-text-link" onClick={()=>onNavigate('Bot Activity',b.name)}>View activity <ArrowRight size={14}/></button></details></section>)}</div>{!visibleBots.length&&<div className="empty-note">No residents match your filter.</div>}<details className="diag-timeline"><summary>Recent evidence ({o.recent.length})</summary>{o.recent.map(e=><div key={e.id}><small>{stamp(e.at)}</small><strong>{e.name}</strong><p>{e.detail} · +{e.amount}</p></div>)}</details></>}
|
||||
{page==='Gameplay Results'&&<><div className="diag-metrics"><Metric label="Quest rewards" value={metrics.rewarded??0} note="New rewarded quest records" icon={ScrollText}/><Metric label="Items looted" value={metrics.lootItems??0} note={`${metrics.lootEvents??0} native loot collections`} icon={Package}/><Metric label="Currency looted" value={formatCopper(metrics.lootedCopper??0)} note="Looted coins only · excludes quest rewards and sales" icon={Package}/><Metric label="Shared-target kills" value={metrics.partyKills??0} note="Killer had a nearby bot fighting the same target" icon={Users}/><Metric label="Quests abandoned" value={metrics.abandoned??0} note="An abandonment is not automatically a bug" icon={Activity}/></div><div className="diag-note">Evidence since {stamp(o.startedAt)}. Native counters last collected {stamp(o.at)}. Quest rewards count newly recorded quest IDs; repeated rewards for an already recorded quest may not increase this count. Quest objectives completed: <strong>{metrics.quests??0}</strong>. Party actions rejected: <strong>{metrics.partyFailed??0}</strong>. Currency tracking starts with this update; historical coin loot is unavailable. Gaps or a server reload can miss unflushed events.</div><div className="diag-toolbar"><label className="diag-search"><Search size={17}/><input aria-label="Search gameplay results" placeholder="Find a resident or zone…" value={query} onChange={e=>setQuery(e.target.value)}/></label><select aria-label="Filter gameplay results" value={filter} onChange={e=>setFilter(e.target.value)}><option value="all">All online residents</option><option value="grouped">Currently grouped</option></select></div><div className="diag-bot-grid">{visibleBots.map(b=><section className="diag-bot" key={b.name}><div className="diag-bot-head"><span className="diag-avatar">{b.name[0]}</span><div><h3>{b.name}</h3><small>{b.zone}</small></div><span className={'diag-chip '+(b.grouped?'good':'')}>{b.grouped?'Grouped':'Solo / unknown'}</span></div><div className="diag-bot-values"><div><strong>{b.totals.rewarded}</strong><small>Quest rewards</small></div><div><strong>{b.totals.lootItems}</strong><small>Items looted</small></div><div><strong>{formatCopper(b.totals.lootedCopper??0)}</strong><small>Currency looted</small></div><div><strong>{b.totals.partyKills}</strong><small>Shared kills</small></div></div><details><summary>Evidence details</summary><p>Objectives completed: {b.totals.quests} · Abandoned: {b.totals.abandoned} · Loot collections: {b.totals.lootEvents}</p><p>Observed bot companions: {b.groupBots.filter(n=>n!==b.name).join(', ')||'None reported'}</p><button className="diag-text-link" onClick={()=>onNavigate('Bot Activity',b.name)}>View activity <ArrowRight size={14}/></button></details></section>)}</div>{!visibleBots.length&&<div className="empty-note">No residents match your filter.</div>}<details className="diag-timeline"><summary>Recent evidence ({o.recent.length})</summary>{o.recent.map(e=><div key={e.id}><small>{stamp(e.at)}</small><strong>{e.name}</strong><p>{e.detail} · +{e.kind==='lootedCopper'?formatCopper(e.amount):e.amount}</p></div>)}</details></>}
|
||||
{page==='Recovery Results'&&<><div className="diag-metrics">{['resumed','no_progress','pending','unverified'].map(status=><Metric key={status} label={labels[status]} value={recoveries.filter(r=>r.status===status).length} note="Among the latest 100 retained attempts" tone={status==='resumed'?'good':status==='no_progress'?'warning':''} icon={status==='resumed'?CheckCircle2:Clock3}/>)}</div><p className="diag-note">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.</p><div className="diag-toolbar"><label className="diag-search"><Search size={17}/><input aria-label="Search recovery results" placeholder="Find a resident…" value={query} onChange={e=>setQuery(e.target.value)}/></label><select aria-label="Filter recovery results" value={filter} onChange={e=>setFilter(e.target.value)}><option value="all">All outcomes</option>{Object.entries(labels).map(([k,v])=><option key={k} value={k}>{v}</option>)}</select></div><div className="diag-alerts">{attempts.map((r,i)=><article className="diag-recovery" key={r.at+':'+r.name+':'+i}><div><h3>{r.name}</h3><small>{stamp(r.at)}</small></div><span className={'diag-chip '+(r.status==='resumed'?'good':r.status==='no_progress'?'warning':'')}>{labels[r.status]}</span><p>{r.result}</p><details><summary>Attempt details</summary><p>{r.detail}</p><small>Checked: {stamp(r.checkedAt)}</small></details></article>)}</div>{!attempts.length&&<div className="diag-clear"><ShieldCheck size={36}/><h3>No matching recovery attempts</h3><p>Results appear after the native guard attempts a recovery. No intervention is forced to populate this view.</p></div>}</>}
|
||||
{page==='Bot Performance'&&<><div className="diag-metrics"><Metric label="Average full tick" value={perf?perf.averageTickMs.toFixed(2)+' ms':'—'} note="Cumulative mean since profiler start/reset" icon={Timer}/><Metric label="Measured full ticks" value={perf?Math.round(perf.ticks).toLocaleString():'—'} note="Bot update cycles, not world-server ticks"/><Metric label="Profiler" value={perf?.enabled?'Enabled':'Unavailable'} note={stamp(perf?.at??null)} tone={perf?.enabled?'good':'warning'}/><Metric label="Stored samples" value={data.performance.history.length} note="Up to 144 snapshots retained" icon={Clock3}/></div>{(!perf||Date.now()/1000-perf.at>720)&&<div className="notice">Profiler data is missing or stale. Measurements below are not current.</div>}<section className="panel"><div className="panel-head"><h2>Average full-tick history</h2><small>Milliseconds · cumulative average</small></div><div className="panel-body"><div className="diag-bars" role="img" aria-label="History of cumulative average bot tick duration">{data.performance.history.slice(-48).map(p=><div key={p.at} title={`${stamp(p.at)}: ${p.averageTickMs.toFixed(3)} ms`} style={{height:Math.max(3,100*p.averageTickMs/Math.max(.001,...data.performance.history.slice(-48).map(x=>x.averageTickMs)))+'%'}}/>)}</div><p className="fineprint">{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.</p></div></section><section className="panel" style={{marginTop:20}}><div className="panel-head"><h2>Most expensive measured operations</h2><small>Sorted by accumulated time</small></div><div className="diag-table-scroll"><table className="diag-table"><thead><tr><th>Operation</th><th>Calls</th><th>Mean</th><th>Maximum</th><th>Per full tick</th></tr></thead><tbody>{perf?.rows.map((r,i)=><tr key={i}><td><strong>{r.name}</strong><small>{r.category}</small></td><td>{Math.round(r.calls).toLocaleString()}</td><td>{r.averageMs.toFixed(3)} ms</td><td>{r.maxMs.toFixed(3)} ms</td><td>{r.perTickMs.toFixed(3)} ms</td></tr>)}</tbody></table></div>{!perf?.rows.length&&<p className="empty-note">Waiting for the first native profiler export.</p>}</section></>}
|
||||
</div>;
|
||||
|
||||
Reference in New Issue
Block a user