Skip to content

Commit 9b53e43

Browse files
shinohara-rinnekomeowww
authored andcommitted
feat(minecraft): logging for perception pipeline
1 parent 2d4eff7 commit 9b53e43

File tree

4 files changed

+163
-31
lines changed

4 files changed

+163
-31
lines changed

services/minecraft/src/cognitive/perception/attention-detector.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export type AttentionEventPayload = PlayerAttentionEventPayload | MobAttentionEv
2626
export class AttentionDetector {
2727
private readonly buckets = new Map<string, LeakyBucket>()
2828

29+
private lastStatsAt = 0
30+
private emittedSinceStats: Record<string, number> = {}
31+
2932
private readonly movementState = new Map<
3033
string,
3134
{
@@ -68,6 +71,16 @@ export class AttentionDetector {
6871
this.movementState.delete(id)
6972
}
7073
}
74+
75+
if (now - this.lastStatsAt >= 2000) {
76+
this.deps.logger.withFields({
77+
deltaMs,
78+
...this.emittedSinceStats,
79+
}).log('AttentionDetector: stats')
80+
81+
this.lastStatsAt = now
82+
this.emittedSinceStats = {}
83+
}
7184
}
7285

7386
public ingest(event: RawPerceptionEvent): void {
@@ -216,6 +229,30 @@ export class AttentionDetector {
216229
}
217230

218231
private emitAttention(payload: AttentionEventPayload): void {
232+
const key = payload.kind === 'player'
233+
? `emit.player.${payload.playerAction}`
234+
: 'emit.mob'
235+
this.emittedSinceStats[key] = (this.emittedSinceStats[key] ?? 0) + 1
236+
237+
if (payload.kind === 'player') {
238+
this.deps.logger.withFields({
239+
kind: payload.kind,
240+
action: payload.playerAction,
241+
playerName: payload.playerName,
242+
distance: payload.distance,
243+
hasLineOfSight: payload.hasLineOfSight,
244+
}).log('AttentionDetector: emit')
245+
}
246+
else {
247+
this.deps.logger.withFields({
248+
kind: payload.kind,
249+
mobName: payload.mobName,
250+
action: payload.mobAction,
251+
distance: payload.distance,
252+
hasLineOfSight: payload.hasLineOfSight,
253+
}).log('AttentionDetector: emit')
254+
}
255+
219256
this.deps.eventManager.emit<AttentionEventPayload>({
220257
type: 'perception',
221258
payload,

services/minecraft/src/cognitive/perception/mineflayer-perception-collector.ts

Lines changed: 91 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export class MineflayerPerceptionCollector {
2323
private readonly lastSneak = new Map<string, boolean>()
2424
private lastSelfHealth: number | null = null
2525

26+
private lastStatsAt = 0
27+
private stats: Record<string, number> = {}
28+
private losSamples = 0
29+
private losTotalMs = 0
30+
2631
constructor(
2732
private readonly deps: {
2833
logger: Logg
@@ -35,6 +40,13 @@ export class MineflayerPerceptionCollector {
3540
this.bot = bot
3641
this.lastSelfHealth = bot.bot.health
3742

43+
this.lastStatsAt = Date.now()
44+
this.stats = {}
45+
this.losSamples = 0
46+
this.losTotalMs = 0
47+
48+
this.deps.logger.withFields({ maxDistance: this.deps.maxDistance }).log('MineflayerPerceptionCollector: init')
49+
3850
this.onBot('entityMoved', (entity: any) => {
3951
const now = Date.now()
4052
const dist = this.distanceTo(entity)
@@ -61,6 +73,8 @@ export class MineflayerPerceptionCollector {
6173
}
6274

6375
this.deps.emitRaw(event)
76+
this.bumpStat('sighted.entity_moved')
77+
this.maybeLogStats()
6478
})
6579

6680
this.onBot('entitySwingArm', (entity: any) => {
@@ -83,6 +97,8 @@ export class MineflayerPerceptionCollector {
8397
}
8498

8599
this.deps.emitRaw(event)
100+
this.bumpStat('sighted.arm_swing')
101+
this.maybeLogStats()
86102
})
87103

88104
this.onBot('entityUpdate', (entity: any) => {
@@ -119,6 +135,8 @@ export class MineflayerPerceptionCollector {
119135
}
120136

121137
this.deps.emitRaw(event)
138+
this.bumpStat('sighted.sneak_toggle')
139+
this.maybeLogStats()
122140
})
123141

124142
this.onBot('soundEffectHeard', (soundId: string, pos: Vec3) => {
@@ -167,6 +185,8 @@ export class MineflayerPerceptionCollector {
167185
}
168186

169187
this.deps.emitRaw(event)
188+
this.bumpStat('felt.damage_taken')
189+
this.maybeLogStats()
170190
})
171191

172192
// Felt: item collected (best-effort; depends on mineflayer version/events)
@@ -190,6 +210,8 @@ export class MineflayerPerceptionCollector {
190210
}
191211

192212
this.deps.emitRaw(event)
213+
this.bumpStat('felt.item_collected')
214+
this.maybeLogStats()
193215
})
194216

195217
this.onBot('entityCollect', (collector: any, collected: any) => {
@@ -212,17 +234,21 @@ export class MineflayerPerceptionCollector {
212234
}
213235

214236
this.deps.emitRaw(event)
237+
this.bumpStat('felt.item_collected')
238+
this.maybeLogStats()
215239
})
216240
}
217241

218242
public destroy(): void {
219243
if (!this.bot)
220244
return
221245

246+
this.deps.logger.withFields({ listeners: this.listeners.length }).log('MineflayerPerceptionCollector: destroy')
247+
222248
for (const { event, handler } of this.listeners) {
223249
try {
224-
(this.bot.bot as any).off?.(event, handler);
225-
(this.bot.bot as any).removeListener?.(event, handler)
250+
(this.bot.bot as any).off?.(event, handler)
251+
(this.bot.bot as any).removeListener?.(event, handler)
226252
}
227253
catch (err) {
228254
this.deps.logger.withError(err as Error).error('MineflayerPerceptionCollector: failed to remove listener')
@@ -236,6 +262,29 @@ export class MineflayerPerceptionCollector {
236262
this.bot = null
237263
}
238264

265+
private bumpStat(key: string): void {
266+
this.stats[key] = (this.stats[key] ?? 0) + 1
267+
}
268+
269+
private maybeLogStats(): void {
270+
const now = Date.now()
271+
if (now - this.lastStatsAt < 2000)
272+
return
273+
274+
const losAvgMs = this.losSamples > 0 ? this.losTotalMs / this.losSamples : 0
275+
276+
this.deps.logger.withFields({
277+
...this.stats,
278+
losSamples: this.losSamples,
279+
losAvgMs,
280+
}).log('MineflayerPerceptionCollector: stats')
281+
282+
this.lastStatsAt = now
283+
this.stats = {}
284+
this.losSamples = 0
285+
this.losTotalMs = 0
286+
}
287+
239288
private onBot(event: string, handler: (...args: any[]) => void): void {
240289
if (!this.bot)
241290
return
@@ -273,41 +322,52 @@ export class MineflayerPerceptionCollector {
273322
if (!this.bot)
274323
return false
275324

276-
try {
277-
const canSee = (this.bot.bot as any).canSeeEntity
278-
if (typeof canSee === 'function')
279-
return !!canSee.call(this.bot.bot, entity)
280-
}
281-
catch { }
325+
const startedAt = Date.now()
282326

283-
// Fallback ray-march; intentionally simple (we'll optimize later)
284327
try {
285-
const from = this.bot.bot.entity.position.offset(0, this.bot.bot.entity.height * 0.9, 0)
286-
const to = entity.position.offset(0, entity.height * 0.9, 0)
287-
const dir = to.minus(from)
288-
const total = dir.norm()
289-
if (total <= 0)
290-
return true
291-
292-
const stepSize = 0.25
293-
const steps = Math.ceil(total / stepSize)
294-
const step = dir.normalize().scaled(stepSize)
328+
try {
329+
const canSee = (this.bot.bot as any).canSeeEntity
330+
if (typeof canSee === 'function')
331+
return !!canSee.call(this.bot.bot, entity)
332+
}
333+
catch { }
295334

296-
let cur = from.clone()
297-
for (let i = 0; i < steps; i++) {
298-
cur = cur.plus(step)
299-
const block = this.bot.bot.blockAt(cur)
300-
if (!block)
301-
continue
335+
// Fallback ray-march; intentionally simple (we'll optimize later)
336+
try {
337+
const from = this.bot.bot.entity.position.offset(0, this.bot.bot.entity.height * 0.9, 0)
338+
const to = entity.position.offset(0, entity.height * 0.9, 0)
339+
const dir = to.minus(from)
340+
const total = dir.norm()
341+
if (total <= 0)
342+
return true
343+
344+
const stepSize = 0.25
345+
const steps = Math.ceil(total / stepSize)
346+
const step = dir.normalize().scaled(stepSize)
347+
348+
let cur = from.clone()
349+
for (let i = 0; i < steps; i++) {
350+
cur = cur.plus(step)
351+
const block = this.bot.bot.blockAt(cur)
352+
if (!block)
353+
continue
354+
355+
if (block.boundingBox === 'block' && !block.transparent)
356+
return false
357+
}
302358

303-
if (block.boundingBox === 'block' && !block.transparent)
304-
return false
359+
return true
360+
}
361+
catch {
362+
return false
305363
}
306-
307-
return true
308364
}
309-
catch {
310-
return false
365+
finally {
366+
const costMs = Date.now() - startedAt
367+
if (costMs > 0) {
368+
this.losSamples++
369+
this.losTotalMs += costMs
370+
}
311371
}
312372
}
313373
}

services/minecraft/src/cognitive/perception/pipeline.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export class PerceptionPipeline {
1414
private collector: MineflayerPerceptionCollector | null = null
1515
private initialized = false
1616

17+
private lastStatsAt = 0
18+
private collectedSinceStats = 0
19+
private processedSinceStats = 0
20+
1721
constructor(
1822
private readonly deps: {
1923
eventManager: EventManager
@@ -29,6 +33,12 @@ export class PerceptionPipeline {
2933
public init(bot: MineflayerWithAgents): void {
3034
this.initialized = true
3135

36+
this.lastStatsAt = Date.now()
37+
this.collectedSinceStats = 0
38+
this.processedSinceStats = 0
39+
40+
this.deps.logger.withFields({ maxDistance: 32 }).log('PerceptionPipeline: init')
41+
3242
this.collector = new MineflayerPerceptionCollector({
3343
logger: this.deps.logger,
3444
emitRaw: (event) => {
@@ -40,6 +50,7 @@ export class PerceptionPipeline {
4050
}
4151

4252
public destroy(): void {
53+
this.deps.logger.log('PerceptionPipeline: destroy')
4354
this.collector?.destroy()
4455
this.collector = null
4556
this.buffer.clear()
@@ -50,15 +61,19 @@ export class PerceptionPipeline {
5061
if (!this.initialized)
5162
return
5263
this.buffer.push(event)
64+
this.collectedSinceStats++
5365
}
5466

5567
public tick(deltaMs: number): void {
5668
if (!this.initialized)
5769
return
5870

71+
const startedAt = Date.now()
72+
5973
this.detector.tick(deltaMs)
6074

6175
const events = this.buffer.drain()
76+
this.processedSinceStats += events.length
6277
for (const event of events) {
6378
try {
6479
this.detector.ingest(event)
@@ -67,5 +82,21 @@ export class PerceptionPipeline {
6782
this.deps.logger.withError(err as Error).error('PerceptionPipeline: detector error')
6883
}
6984
}
85+
86+
const now = Date.now()
87+
if (now - this.lastStatsAt >= 2000) {
88+
this.deps.logger.withFields({
89+
deltaMs,
90+
tickCostMs: now - startedAt,
91+
queueDepth: this.buffer.size(),
92+
drained: events.length,
93+
collected: this.collectedSinceStats,
94+
processed: this.processedSinceStats,
95+
}).log('PerceptionPipeline: stats')
96+
97+
this.lastStatsAt = now
98+
this.collectedSinceStats = 0
99+
this.processedSinceStats = 0
100+
}
70101
}
71102
}

services/minecraft/src/cognitive/perception/raw-event-buffer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ export class RawEventBuffer {
77
this.queue.push(event)
88
}
99

10+
public size(): number {
11+
return this.queue.length
12+
}
13+
1014
public drain(): RawPerceptionEvent[] {
1115
if (this.queue.length === 0)
1216
return []

0 commit comments

Comments
 (0)