From 0eca16030b3bcc6452359d8ee13a736399b16fe8 Mon Sep 17 00:00:00 2001 From: Rin Date: Mon, 12 Jan 2026 20:49:13 +0800 Subject: [PATCH] fix(minecraft): dirty teabagging workaround --- .../mineflayer-perception-collector.ts | 34 ++++++++++++++---- .../src/cognitive/perception/types/signals.ts | 1 + .../src/cognitive/reflex/behaviors/teabag.ts | 35 +++++++++++++++++++ .../minecraft/src/cognitive/reflex/context.ts | 4 +++ .../src/cognitive/reflex/reflex-manager.ts | 9 +++++ .../src/cognitive/rules/social/teabag.yaml | 23 ++++++++++++ 6 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 services/minecraft/src/cognitive/reflex/behaviors/teabag.ts create mode 100644 services/minecraft/src/cognitive/rules/social/teabag.yaml diff --git a/services/minecraft/src/cognitive/perception/mineflayer-perception-collector.ts b/services/minecraft/src/cognitive/perception/mineflayer-perception-collector.ts index 90ac7a914..72088a130 100644 --- a/services/minecraft/src/cognitive/perception/mineflayer-perception-collector.ts +++ b/services/minecraft/src/cognitive/perception/mineflayer-perception-collector.ts @@ -23,6 +23,7 @@ export class MineflayerPerceptionCollector { private lastStatsAt = 0 private stats: Record = {} + private sneakingState: Map = 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() diff --git a/services/minecraft/src/cognitive/perception/types/signals.ts b/services/minecraft/src/cognitive/perception/types/signals.ts index caf0147b8..d6d2e78d8 100644 --- a/services/minecraft/src/cognitive/perception/types/signals.ts +++ b/services/minecraft/src/cognitive/perception/types/signals.ts @@ -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 diff --git a/services/minecraft/src/cognitive/reflex/behaviors/teabag.ts b/services/minecraft/src/cognitive/reflex/behaviors/teabag.ts new file mode 100644 index 000000000..ef236b7ee --- /dev/null +++ b/services/minecraft/src/cognitive/reflex/behaviors/teabag.ts @@ -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)) + } + }, +} diff --git a/services/minecraft/src/cognitive/reflex/context.ts b/services/minecraft/src/cognitive/reflex/context.ts index a63d17801..32fa30e8e 100644 --- a/services/minecraft/src/cognitive/reflex/context.ts +++ b/services/minecraft/src/cognitive/reflex/context.ts @@ -21,6 +21,8 @@ export interface ReflexSocialState { lastMessage: string | null lastMessageAt: number | null lastGreetingAtBySpeaker: Record + 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, diff --git a/services/minecraft/src/cognitive/reflex/reflex-manager.ts b/services/minecraft/src/cognitive/reflex/reflex-manager.ts index 8dcfc68cd..f69bcb375 100644 --- a/services/minecraft/src/cognitive/reflex/reflex-manager.ts +++ b/services/minecraft/src/cognitive/reflex/reflex-manager.ts @@ -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. diff --git a/services/minecraft/src/cognitive/rules/social/teabag.yaml b/services/minecraft/src/cognitive/rules/social/teabag.yaml new file mode 100644 index 000000000..775dd9dea --- /dev/null +++ b/services/minecraft/src/cognitive/rules/social/teabag.yaml @@ -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 }}'