Skip to content

feat: foundational SmartBrainLib architecture for HumanoidNPC - #15

Merged
DizzyMii merged 2 commits into
mainfrom
copilot/implement-smartbrainlib-architecture
Mar 26, 2026
Merged

feat: foundational SmartBrainLib architecture for HumanoidNPC#15
DizzyMii merged 2 commits into
mainfrom
copilot/implement-smartbrainlib-architecture

Conversation

Copilot AI commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Implements the four-component SBL brain architecture for a state-driven autonomous NPC that uses custom memory, sensors, and contextual behaviors rather than reactive goal AI.

New Components

  • ModMemoryTypesDeferredRegister<MemoryModuleType<?>> with three slots: BASE_LOCATION (GlobalPos), MACRO_OBJECTIVE (String), NEEDED_MATERIALS (List<String>)
  • InventoryStateSensor (ExtendedSensor<HumanoidNPC>) — polls main-hand, offhand, and carried inventory every 10 ticks; flags empty slots and tools below 25% durability; writes to NEEDED_MATERIALS
  • ContextualToolSwapBehavior (ExtendedBehaviour<HumanoidNPC>) — requires MACRO_OBJECTIVE; maps objective keywords → tool class (wood→Axe, stone→Pickaxe, combat→Sword, farm→Hoe, dig→Shovel); swaps first matching tool from carried inventory into main hand; no-ops when already optimal
  • HumanoidNPC (PathfinderMob + SmartBrainOwner<HumanoidNPC>) — declares all three memory types in brainProvider(); CORE (placeholder), IDLE (placeholder), and WORK activities with ContextualToolSwapBehavior; sensors polled before brain tick in customServerAiStep()
// Brain wiring: sensors run before brain tick to keep memories fresh
@Override
protected void customServerAiStep() {
    super.customServerAiStep();
    if (level() instanceof ServerLevel sl) {
        for (ExtendedSensor<? super HumanoidNPC> sensor : sensorCache) {
            sensor.tick(sl, this);   // writes NEEDED_MATERIALS
        }
        getBrain().tick(sl, this);   // ContextualToolSwapBehavior reads MACRO_OBJECTIVE
    }
}

Integration

  • MillEntities — registers HUMANOID_NPC entity type
  • Millenaire2 — wires ModMemoryTypes.MEMORY_MODULE_TYPES register on the mod event bus; registers HumanoidNPC attributes in EntityAttributeCreationEvent

Tests

HumanoidNpcTests — 12 @GameTest methods covering: brain configuration on spawn, all three memory types accessible, default WORK activity, sensor writes (empty hands, damaged tool detection), tool-swap correctness per objective keyword, no-op when already optimal, no-op when no matching tool, and base-location memory helpers.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://launchermeta.mojang.com/mc/game/version_manifest_v2.json
    • Triggering command: /usr/lib/jvm/temurin-21-jdk-amd64/bin/java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java -Dstderr.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -jar /home/REDACTED/.gradle/caches/modules-2/files-2.1/net.neoforged/neoform-runtime/2.0.18/af5a11e64f209ac5909434ac2617b6af19a02a8d/neoform-runtime-2.0.18-all.jar --home-dir /home/REDACTED/.gradle/caches/neoformruntime --work-dir /home/REDACTED/work/Millenaire_port/Millenaire_port/build/tmp/neoformruntime run --java-executable /usr/lib/jvm/temurin-21-jdk-amd64/bin/java --parchment-data /home/REDACTED/.gradle/caches/modules-2/files-2.1/org.parchmentmc.data/parchment-1.21.3/2024.12.07/b53d6f313ce9f126e3e9ec9419e383aa3a365f46/parchment-1.21.3-2024.12.07.zip --parchment-conflict-prefix p_ (http block)
  • launchermeta.mojang.com
    • Triggering command: /usr/lib/jvm/temurin-21-jdk-amd64/bin/java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java -Dstderr.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -jar /home/REDACTED/.gradle/caches/modules-2/files-2.1/net.neoforged/neoform-runtime/2.0.18/af5a11e64f209ac5909434ac2617b6af19a02a8d/neoform-runtime-2.0.18-all.jar --home-dir /home/REDACTED/.gradle/caches/neoformruntime --work-dir /home/REDACTED/work/Millenaire_port/Millenaire_port/build/tmp/neoformruntime run --java-executable /usr/lib/jvm/temurin-21-jdk-amd64/bin/java --parchment-data /home/REDACTED/.gradle/caches/modules-2/files-2.1/org.parchmentmc.data/parchment-1.21.3/2024.12.07/b53d6f313ce9f126e3e9ec9419e383aa3a365f46/parchment-1.21.3-2024.12.07.zip --parchment-conflict-prefix p_ (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

- ModMemoryTypes: BASE_LOCATION (GlobalPos), MACRO_OBJECTIVE (String),
  NEEDED_MATERIALS (List<String>) via DeferredRegister
- InventoryStateSensor: checks main-hand/offhand durability + inventory,
  writes NEEDED_MATERIALS every 10 ticks
- ContextualToolSwapBehavior: maps objective keywords to tool classes
  (wood→Axe, stone→Pickaxe, combat→Sword, farm→Hoe, dig→Shovel),
  swaps best-match from carried inventory into main hand
- HumanoidNPC: PathfinderMob + SmartBrainOwner; CORE/IDLE/WORK brain
  activities; sensors polled before brain tick in customServerAiStep
- MillEntities: register HUMANOID_NPC entity type
- Millenaire2: wire MEMORY_MODULE_TYPES register + HumanoidNPC attributes
- HumanoidNpcTests: 12 @gameTest methods covering all components

Co-authored-by: DizzyMii <156777136+DizzyMii@users.noreply.github.com>
Agent-Logs-Url: https://github.com/DizzyMii/Millenaire_port/sessions/a8ac6081-94b5-40c7-a63b-631d58cb5a8b
Copilot AI changed the title [WIP] Implement foundational SmartBrainLib architecture for HumanoidNPC feat: foundational SmartBrainLib architecture for HumanoidNPC Mar 26, 2026
Copilot AI requested a review from DizzyMii March 26, 2026 02:32
@DizzyMii
DizzyMii marked this pull request as ready for review March 26, 2026 02:35
Copilot AI review requested due to automatic review settings March 26, 2026 02:35
@DizzyMii
DizzyMii merged commit 24972df into main Mar 26, 2026
1 check passed
@DizzyMii
DizzyMii deleted the copilot/implement-smartbrainlib-architecture branch March 26, 2026 02:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the initial SmartBrainLib-style (stubbed) brain architecture for a new HumanoidNPC entity, including custom memory modules, a sensor that writes inventory-derived needs into memory, and a contextual tool-swap behaviour wired into the entity’s WORK activity.

Changes:

  • Introduces HumanoidNPC with manual sensor ticking + Brain ticking, plus a small carried-inventory abstraction and base-location memory helpers.
  • Adds ModMemoryTypes, InventoryStateSensor, and ContextualToolSwapBehavior to support memory-driven tool equipping.
  • Registers the new entity + memory-module DeferredRegister and adds a comprehensive GameTest suite for the new brain components.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/main/java/org/dizzymii/millenaire2/gametest/HumanoidNpcTests.java Adds GameTests validating the new HumanoidNPC brain wiring, sensor output, tool-swap behavior, and base-location memory helper.
src/main/java/org/dizzymii/millenaire2/entity/brain/sensor/InventoryStateSensor.java New sensor that inspects hands + carried inventory and writes NEEDED_MATERIALS memory.
src/main/java/org/dizzymii/millenaire2/entity/brain/behaviour/ContextualToolSwapBehavior.java New behaviour that selects a tool based on MACRO_OBJECTIVE and equips it in main hand.
src/main/java/org/dizzymii/millenaire2/entity/brain/ModMemoryTypes.java Registers custom Brain MemoryModuleTypes used by HumanoidNPC.
src/main/java/org/dizzymii/millenaire2/entity/MillEntities.java Registers the HUMANOID_NPC entity type.
src/main/java/org/dizzymii/millenaire2/entity/HumanoidNPC.java New entity implementing the SBL-like owner interface, manual sensor polling, and activity wiring.
src/main/java/org/dizzymii/millenaire2/Millenaire2.java Wires memory-module registration on the mod event bus and registers HumanoidNPC attributes.

Comment on lines +111 to +115
float durabilityFraction =
1.0f - ((float) stack.getDamageValue() / (float) stack.getMaxDamage());
if (durabilityFraction < DAMAGE_THRESHOLD) {
needed.add(slotKey + ":damaged:" + stack.getItem().getDescriptionId());
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc and tests describe the damaged-tool threshold as “≤ 25% durability remaining”, but the implementation uses a strict < DAMAGE_THRESHOLD comparison. At exactly 25% remaining, the tool will not be flagged as damaged. Either change the comparison to <= or update the documentation/tests to match the intended strictness.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

List<ItemStack> inventory = entity.getCarriedInventory();
ItemStack bestTool = findBestToolForObjective(inventory, objective);
if (bestTool != null && !bestTool.isEmpty()) {
entity.setItemInHand(InteractionHand.MAIN_HAND, bestTool.copy());

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start() copies the chosen tool into the main hand (bestTool.copy()) but never removes it from carriedInventory or returns the previous main-hand stack back into inventory. This effectively duplicates items and isn't a true swap. Consider performing an actual move/swap (remove the selected stack from carried inventory, place it in main hand, and optionally store the previous main-hand stack back into carried inventory). Updating/adding a GameTest to assert the carried inventory changes would prevent regressions.

Suggested change
entity.setItemInHand(InteractionHand.MAIN_HAND, bestTool.copy());
int index = inventory.indexOf(bestTool);
if (index >= 0) {
ItemStack previousMainHand = entity.getItemInHand(InteractionHand.MAIN_HAND);
// Move the selected tool into the main hand
entity.setItemInHand(InteractionHand.MAIN_HAND, bestTool);
// Put the previous main-hand stack back into the carried inventory slot,
// or clear the slot if the main hand was empty.
if (previousMainHand.isEmpty()) {
inventory.set(index, ItemStack.EMPTY);
} else {
inventory.set(index, previousMainHand);
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants