fix(minecraft): dirty teabagging workaround

This commit is contained in:
Rin
2026-02-18 11:12:02 +08:00
committed by Neko Ayaka
parent 8d68af30b0
commit 0eca16030b
6 changed files with 100 additions and 6 deletions
@@ -23,6 +23,7 @@ export class MineflayerPerceptionCollector {
private lastStatsAt = 0
private stats: Record<string, number> = {}
private sneakingState: Map<string, boolean> = new Map()
constructor(
private readonly deps: {
@@ -47,6 +48,10 @@ export class MineflayerPerceptionCollector {
if (dist === null || dist > this.deps.maxDistance)
return
// Ignore self
if (entity.username === this.bot?.bot.username)
return
const entityId = this.entityId(entity)
const event: SightedEntityMovedEvent = {
@@ -73,6 +78,10 @@ export class MineflayerPerceptionCollector {
if (dist === null || dist > this.deps.maxDistance)
return
// Ignore self
if (entity.username === this.bot?.bot.username)
return
const event: SightedArmSwingEvent = {
modality: 'sighted',
kind: 'arm_swing',
@@ -95,16 +104,27 @@ export class MineflayerPerceptionCollector {
if (!entity || entity.type !== 'player')
return
// Ignore self
if (entity.username === this.bot?.bot.username)
return
const entityId = this.entityId(entity)
const flags = entity?.metadata?.[0]
// Bit 1 (0x02) is sneaking
const isSneaking = typeof flags === 'number' ? !!(flags & 0x02) : false
// Check if state actually changed
const lastState = this.sneakingState.get(entityId)
if (lastState === isSneaking) {
return
}
this.sneakingState.set(entityId, isSneaking)
const now = Date.now()
const dist = this.distanceTo(entity)
if (dist === null || dist > this.deps.maxDistance)
return
const entityId = this.entityId(entity)
const flags = entity?.metadata?.[0]
const sneaking = typeof flags === 'number' ? !!(flags & 0x02) : false
const event: SightedSneakToggleEvent = {
modality: 'sighted',
kind: 'sneak_toggle',
@@ -113,12 +133,14 @@ export class MineflayerPerceptionCollector {
displayName: entity?.username,
distance: dist,
hasLineOfSight: true,
sneaking,
sneaking: isSneaking,
timestamp: now,
source: 'minecraft',
pos: entity?.position,
}
this.deps.logger.withFields({ entity: entity.username, sneaking: isSneaking }).log('MineflayerPerceptionCollector: sneak_toggle')
this.deps.emitRaw(event)
this.bumpStat('sighted.sneak_toggle')
this.maybeLogStats()
@@ -3,6 +3,7 @@ export type PerceptionSignalType
| 'entity_attention' // e.g. someone waving, teabagging
| 'environmental_anomaly' // e.g. sudden loud sound
| 'saliency_high' // generic high saliency event
| 'social_gesture' // e.g. teabagging, waving
export interface PerceptionSignal {
type: PerceptionSignalType
@@ -0,0 +1,35 @@
import type { ReflexBehavior } from '../types/behavior'
export const teabagBehavior: ReflexBehavior = {
id: 'teabag',
modes: ['social', 'idle'],
cooldownMs: 5000,
when: (ctx) => {
// Check if we recently received a teabag signal
// Check if we recently received a teabag signal
if (ctx.social.lastGesture === 'teabag') {
const now = Date.now()
const signalAge = now - (ctx.social.lastGestureAt || 0)
// Only respond if signal is fresh (< 2s)
return signalAge < 2000
}
return false
},
score: () => {
// Higher priority than LookAt (50)
return 60
},
run: async ({ bot }) => {
// Perform rapid squats
for (let i = 0; i < 4; i++) {
bot.bot.setControlState('sneak', true)
await new Promise(resolve => setTimeout(resolve, 150))
bot.bot.setControlState('sneak', false)
await new Promise(resolve => setTimeout(resolve, 150))
}
},
}
@@ -21,6 +21,8 @@ export interface ReflexSocialState {
lastMessage: string | null
lastMessageAt: number | null
lastGreetingAtBySpeaker: Record<string, number>
lastGesture: string | null
lastGestureAt: number | null
}
export interface ReflexThreatState {
@@ -69,6 +71,8 @@ export class ReflexContext {
lastMessage: null,
lastMessageAt: null,
lastGreetingAtBySpeaker: {},
lastGesture: null,
lastGestureAt: null,
},
threat: {
threatScore: 0,
@@ -8,6 +8,7 @@ import type { ReflexContextState } from './context'
import { DebugService } from '../../debug'
import { greetingBehavior } from './behaviors/greeting'
import { lookAtBehavior } from './behaviors/look-at'
import { teabagBehavior } from './behaviors/teabag'
import { ReflexRuntime } from './runtime'
export class ReflexManager {
@@ -27,6 +28,7 @@ export class ReflexManager {
this.runtime.registerBehavior(greetingBehavior)
this.runtime.registerBehavior(lookAtBehavior)
this.runtime.registerBehavior(teabagBehavior)
}
public init(bot: MineflayerWithAgents): void {
@@ -71,6 +73,13 @@ export class ReflexManager {
lastSignalAt: now,
})
if (signal.type === 'social_gesture') {
this.runtime.getContext().updateSocial({
lastGesture: (signal.metadata as any)?.gesture ?? 'unknown',
lastGestureAt: now,
})
}
// If it's a chat message (simulated via signal for now, or direct?)
// For now we rely on signal metadata or separate chat event.
// Assuming 'signal:social:chat' or similar might exist later.
@@ -0,0 +1,23 @@
name: teabag-attention
version: 1
trigger:
modality: sighted
kind: sneak_toggle
where:
entityType: player
accumulator:
threshold: 4
window: 2s
mode: sliding
signal:
type: social_gesture
description: 'Player {{ displayName }} is teabagging (rapid sneaking)'
confidence: 1.0
metadata:
gesture: teabag
distance: '{{ distance }}'
displayName: '{{ displayName }}'
hasLineOfSight: '{{ hasLineOfSight }}'