Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6b4c2fa
gate patrol enemy action authority
kaiserproger May 8, 2026
71f133b
gate patrol info mode authority
kaiserproger May 8, 2026
cb3594d
gate patrol speed authority
kaiserproger May 8, 2026
0e7f222
Merge feature/packetauth-012: gate patrol enemy action authority
kaiserproger May 8, 2026
35154ea
Merge feature/packetauth-013: gate patrol info mode authority
kaiserproger May 8, 2026
0dd1f90
Merge feature/packetauth-014: gate patrol speed authority
kaiserproger May 8, 2026
9ffb532
backlog: close patrol leader authority tasks
kaiserproger May 8, 2026
97ce517
gate patrol wait time authority
kaiserproger May 8, 2026
a15704f
gate companion group removal authority
kaiserproger May 8, 2026
dbe56e4
gate messenger send authority
kaiserproger May 8, 2026
3f81792
Merge feature/packetauth-015: gate patrol wait time authority
kaiserproger May 8, 2026
bd40b26
Merge feature/packetauth-016: gate companion group removal authority
kaiserproger May 8, 2026
28cf1d9
Merge feature/packetauth-017: gate messenger send authority
kaiserproger May 8, 2026
a5c11b4
backlog: close messenger authority tasks
kaiserproger May 8, 2026
0ad7016
gate recruit promotion authority
kaiserproger May 8, 2026
37e1374
gate hire group authority
kaiserproger May 8, 2026
024845b
unbind workers when work areas are removed
kaiserproger May 8, 2026
bcfd9e7
Merge feature/packetauth-018: gate recruit promotion authority
kaiserproger May 8, 2026
6977abc
Merge feature/packetauth-019: gate hire group authority
kaiserproger May 8, 2026
1bab98a
Merge feature/workerunbind-001: unbind workers on work-area removal
kaiserproger May 8, 2026
ac5497b
backlog: close hire and worker unbind tasks
kaiserproger May 8, 2026
a144ddf
define assassin leader control authority
kaiserproger May 8, 2026
607d18a
extract settlement treasury derivation
kaiserproger May 8, 2026
a7bab98
Merge feature/packetauth-008a: define assassin leader control authority
kaiserproger May 8, 2026
640a572
Merge feature/settrefactor-003a: extract settlement treasury derivation
kaiserproger May 8, 2026
2f65ab6
backlog: close treasury and split events package
kaiserproger May 8, 2026
d0a2ee2
allow ops to update assassin count
kaiserproger May 8, 2026
0cf0e74
move claim services out of events package
kaiserproger May 8, 2026
06eab18
move military services out of events package
kaiserproger May 8, 2026
2730755
Merge feature/packetauth-008b: gate assassin count authority
kaiserproger May 8, 2026
a469494
Merge feature/eventspkg-005a: move claim services from events
kaiserproger May 8, 2026
4b561b5
Merge feature/eventspkg-005b: move military services from events
kaiserproger May 8, 2026
d3b683e
fix siege tuning import after event package moves
kaiserproger May 8, 2026
60a016d
backlog: close assassin and events package slices
kaiserproger May 8, 2026
c499f17
move civilian services out of events package
kaiserproger May 8, 2026
2283f29
Merge feature/eventspkg-005c: move civilian services from events
kaiserproger May 8, 2026
cd07e1b
backlog: close civilian events package slice
kaiserproger May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 234 additions & 50 deletions docs/BANNERMOD_BACKLOG.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,21 @@
import java.util.UUID;

/**
* TESTWORKER-001 acceptance gametest.
* WORKERUNBIND-001 acceptance gametest.
*
* <p>Verifies that when a worker's bound work-area entity is removed from the level,
* the worker auto-unbinds within ~20 ticks: {@code boundWorkAreaUUID} returns to
* {@code null} and {@code getCurrentWorkArea()} also returns {@code null}, so no
* AI goal can still observe the dead area through the worker's binding.
*
* <p><b>Status:</b> the {@code @GameTest} annotation on
* {@link #workerAutoUnbindsWithinTwentyTicksAfterCropAreaRemoval(GameTestHelper)} is
* intentionally commented out. Production code does NOT currently fan out work-area
* entity removal to bound workers (no symmetric counterpart to
* {@link com.talhanation.bannermod.persistence.military.ClaimRemovalFanout} exists for a
* single work-area entity going away while its parent claim survives). The test below
* is the executable acceptance specification for the production fix tracked by
* {@code WORKERUNBIND-001}. Re-enable the {@code @GameTest} line once that fix lands.
*/
@GameTestHolder(BannerModMain.MOD_ID)
public class BannerModWorkerUnbindOnAreaRemovalGameTests {

private static final UUID OWNER_UUID = UUID.fromString("00000000-0000-0000-0000-000000005001");
private static final String OWNER_TEAM_ID = "workerunbind_owner";

/**
* TODO(WORKERUNBIND-001): re-enable {@code @GameTest} once the work-area-removal
* fanout is implemented. The body below is the contract.
*/
@PrefixGameTestTemplate(false)
// @GameTest(template = "harness_empty", timeoutTicks = 60)
@GameTest(template = "harness_empty", timeoutTicks = 60)
public static void workerAutoUnbindsWithinTwentyTicksAfterCropAreaRemoval(GameTestHelper helper) {
ServerLevel level = helper.getLevel();
Player owner = BannerModDedicatedServerGameTestSupport.createFakeServerPlayer(level, OWNER_UUID, "workerunbind-owner");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.talhanation.bannermod.events;
package com.talhanation.bannermod.ai.civilian.animals;

import com.talhanation.bannermod.ai.civilian.animals.WorkerTemptGoal;
import net.minecraft.world.entity.Entity;
Expand All @@ -9,12 +9,12 @@
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;

final class WorkerAnimalGoalInjector {
public final class WorkerAnimalGoalInjector {

private WorkerAnimalGoalInjector() {
}

static void injectTemptGoal(Entity entity) {
public static void injectTemptGoal(Entity entity) {
if (entity instanceof Chicken chicken) {
chicken.goalSelector.addGoal(3, new WorkerTemptGoal(chicken, 1.0,
Ingredient.of(Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.PUMPKIN_SEEDS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.talhanation.bannermod.ai.military.villager;

import com.talhanation.bannermod.entity.military.VillagerNobleEntity;
import com.talhanation.bannermod.events.runtime.VillagerConversionService;
import com.talhanation.bannermod.entity.military.runtime.VillagerConversionService;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.npc.Villager;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.talhanation.bannermod.events;
package com.talhanation.bannermod.army.command.runtime;

import com.talhanation.bannermod.bootstrap.BannerModMain;
import com.talhanation.bannermod.config.RecruitsServerConfig;
Expand Down Expand Up @@ -26,12 +26,12 @@
import java.util.Optional;
import java.util.UUID;

final class RecruitCommandActionService {
public final class RecruitCommandActionService {

private RecruitCommandActionService() {
}

static boolean handleRecruiting(Player player, RecruitsGroup group, AbstractRecruitEntity recruit, boolean message) {
public static boolean handleRecruiting(Player player, RecruitsGroup group, AbstractRecruitEntity recruit, boolean message) {
String name = recruit.getName().getString() + ": ";
int sollPrice = recruit.getCost();
boolean playerCanPay = BannerModCurrencyHelper.canAfford(player, sollPrice);
Expand All @@ -50,7 +50,7 @@ static boolean handleRecruiting(Player player, RecruitsGroup group, AbstractRecr
return false;
}

static void onMountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID mountUuid, UUID group) {
public static void onMountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID mountUuid, UUID group) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
if (mountUuid != null) {
recruit.shouldMount(true, mountUuid);
Expand All @@ -61,7 +61,7 @@ static void onMountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID m
}
}

static void onDismountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
public static void onDismountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.shouldMount(false, null);
if (recruit.isPassenger()) {
Expand All @@ -71,13 +71,13 @@ static void onDismountButton(UUID playerUuid, AbstractRecruitEntity recruit, UUI
}
}

static void onProtectButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID protectUuid, UUID group) {
public static void onProtectButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID protectUuid, UUID group) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.shouldProtect(true, protectUuid);
}
}

static void onClearTargetButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
public static void onClearTargetButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.setTarget(null);
recruit.setLastHurtByPlayer(null);
Expand All @@ -86,14 +86,14 @@ static void onClearTargetButton(UUID playerUuid, AbstractRecruitEntity recruit,
}
}

static void onClearUpkeepButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
public static void onClearUpkeepButton(UUID playerUuid, AbstractRecruitEntity recruit, UUID group) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.clearUpkeepEntity();
recruit.clearUpkeepPos();
}
}

static void onUpkeepCommand(UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean isEntity, UUID entityUuid, BlockPos blockPos) {
public static void onUpkeepCommand(UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean isEntity, UUID entityUuid, BlockPos blockPos) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
if (isEntity) {
recruit.setUpkeepUUID(Optional.of(entityUuid));
Expand All @@ -108,13 +108,13 @@ static void onUpkeepCommand(UUID playerUuid, AbstractRecruitEntity recruit, UUID
}
}

static void onShieldsCommand(Player player, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean shields) {
public static void onShieldsCommand(Player player, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean shields) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.setShouldBlock(shields);
}
}

static void onRangedFireCommand(ServerPlayer serverPlayer, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean should) {
public static void onRangedFireCommand(ServerPlayer serverPlayer, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean should) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
recruit.setShouldRanged(should);

Expand All @@ -131,7 +131,7 @@ static void onRangedFireCommand(ServerPlayer serverPlayer, UUID playerUuid, Abst
}
}

static void onRestCommand(ServerPlayer serverPlayer, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean should) {
public static void onRestCommand(ServerPlayer serverPlayer, UUID playerUuid, AbstractRecruitEntity recruit, UUID group, boolean should) {
if (recruit.isEffectedByCommand(playerUuid, group)) {
onClearTargetButton(playerUuid, recruit, group);
recruit.setShouldRest(should);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.talhanation.bannermod.entity.citizen.CitizenEntity;
import com.talhanation.bannermod.entity.citizen.CitizenIndex;
import com.talhanation.bannermod.events.ClaimEvents;
import com.talhanation.bannermod.events.WorkerSettlementClaimPolicy;
import com.talhanation.bannermod.settlement.civilian.runtime.WorkerSettlementClaimPolicy;
import com.talhanation.bannermod.persistence.military.RecruitsClaim;
import com.talhanation.bannermod.registry.citizen.ModCitizenEntityTypes;
import com.talhanation.bannermod.settlement.civilian.CitizenBirthRules;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.talhanation.bannermod.events.runtime;
package com.talhanation.bannermod.citizen.runtime;

import com.talhanation.bannermod.ai.pathfinding.async.TrueAsyncPathfindingRuntime;
import com.talhanation.bannermod.config.RecruitsServerConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,23 +355,23 @@ else if(recruit instanceof IVillagerWorker worker && worker.hasOnlyScreen()){
// Convert-Type menu — only for plain base recruits (not companions, not noble villagers,
// not villager-worker compat). Sends MessageConvertRecruitType; server discards old
// entity and spawns the chosen type carrying owner/group/xp/level/inventory across.
if (com.talhanation.bannermod.events.runtime.RecruitTypeConverter.isConvertibleBaseType(this.recruit)) {
if (com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.isConvertibleBaseType(this.recruit)) {
ActionMenuButton convertMenu = new ActionMenuButton(zeroLeftPos, zeroTopPos + (20 + topPosGab) * 7,
80, 20, TEXT_MENU_CONVERT, java.util.List.of(
new ContextMenuEntry(TEXT_CONVERT_SWORDSMAN.getString(),
() -> sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind.SWORDSMAN),
() -> sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind.SWORDSMAN),
!(recruit.getClass() == com.talhanation.bannermod.entity.military.RecruitEntity.class)),
new ContextMenuEntry(TEXT_CONVERT_BOWMAN.getString(),
() -> sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind.BOWMAN),
() -> sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind.BOWMAN),
!(recruit.getClass() == com.talhanation.bannermod.entity.military.BowmanEntity.class)),
new ContextMenuEntry(TEXT_CONVERT_PIKEMAN.getString(),
() -> sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind.PIKEMAN),
() -> sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind.PIKEMAN),
!(recruit.getClass() == com.talhanation.bannermod.entity.military.RecruitShieldmanEntity.class)),
new ContextMenuEntry(TEXT_CONVERT_CROSSBOWMAN.getString(),
() -> sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind.CROSSBOWMAN),
() -> sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind.CROSSBOWMAN),
!(recruit.getClass() == com.talhanation.bannermod.entity.military.CrossBowmanEntity.class)),
new ContextMenuEntry(TEXT_CONVERT_CAVALRY.getString(),
() -> sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind.CAVALRY),
() -> sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind.CAVALRY),
!(recruit.getClass() == com.talhanation.bannermod.entity.military.HorsemanEntity.class))
));
convertMenu.setTooltip(Tooltip.create(TOOLTIP_CONVERT));
Expand All @@ -380,7 +380,7 @@ else if(recruit instanceof IVillagerWorker worker && worker.hasOnlyScreen()){
}
}

private void sendConvertRecruit(com.talhanation.bannermod.events.runtime.RecruitTypeConverter.Kind kind) {
private void sendConvertRecruit(com.talhanation.bannermod.entity.military.runtime.RecruitTypeConverter.Kind kind) {
BannerModMain.SIMPLE_CHANNEL.sendToServer(new MessageConvertRecruitType(this.recruit.getUUID(), kind));
this.onClose();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* a {@code routEndTick} (the game-tick at which the rout window closes). An entry is created
* lazily on the first evaluation and left in place — recruits removed from the world have
* their entries reaped by {@link #invalidate(UUID)}, called from the existing entity-leave
* hook in {@link com.talhanation.bannermod.events.runtime.RecruitCombatRuntime}.</p>
* hook in {@link com.talhanation.bannermod.combat.runtime.RecruitCombatRuntime}.</p>
*
* <p>Transitions into {@link MoraleState#ROUTED} fire two side effects:
* <ol>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.talhanation.bannermod.events.runtime;
package com.talhanation.bannermod.combat.runtime;

import com.talhanation.bannermod.entity.military.AbstractRecruitEntity;
import com.talhanation.bannermod.entity.military.MessengerEntity;
import com.talhanation.bannermod.entity.military.runtime.RecruitEntityAccess;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.player.Player;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.talhanation.bannermod.events.runtime;
package com.talhanation.bannermod.combat.runtime;

import com.talhanation.bannermod.bootstrap.BannerModMain;
import com.talhanation.bannermod.compat.IWeapon;
import com.talhanation.bannermod.config.RecruitsServerConfig;
import com.talhanation.bannermod.entity.military.AbstractRecruitEntity;
import com.talhanation.bannermod.entity.military.MessengerEntity;
import com.talhanation.bannermod.entity.military.runtime.RecruitEntityAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.talhanation.bannermod.events.runtime;
package com.talhanation.bannermod.combat.runtime;

import com.talhanation.bannermod.entity.military.RecruitPoliticalContext;
import com.talhanation.bannermod.war.WarRuntimeContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.talhanation.bannermod.events.runtime;
package com.talhanation.bannermod.combat.runtime;

import com.talhanation.bannermod.entity.military.AbstractRecruitEntity;
import com.talhanation.bannermod.entity.military.RecruitPoliticalContext;
Expand All @@ -18,7 +18,7 @@
import java.util.Collection;
import java.util.UUID;

final class RecruitTargetAuthority {
public final class RecruitTargetAuthority {
private RecruitTargetAuthority() {
}

Expand All @@ -41,10 +41,10 @@ static boolean canTargetUnderClaimAuthority(LivingEntity attacker, LivingEntity
return claimAuthorityAllowsTarget(attackerPoliticalEntityId, claimOwnerPoliticalEntityId, occupiesClaim, atWarWithClaimOwner);
}

static boolean claimAuthorityAllowsTarget(@Nullable UUID attackerPoliticalEntityId,
@Nullable UUID claimOwnerPoliticalEntityId,
boolean occupiesClaim,
boolean atWarWithClaimOwner) {
public static boolean claimAuthorityAllowsTarget(@Nullable UUID attackerPoliticalEntityId,
@Nullable UUID claimOwnerPoliticalEntityId,
boolean occupiesClaim,
boolean atWarWithClaimOwner) {
if (claimOwnerPoliticalEntityId == null) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.talhanation.bannermod.entity.civilian.workarea;

import com.talhanation.bannermod.bootstrap.BannerModMain;
import com.talhanation.bannermod.entity.civilian.AbstractWorkerEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.entity.EntityLeaveLevelEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
Expand All @@ -24,7 +27,18 @@ public static void onJoin(EntityJoinLevelEvent event) {

@SubscribeEvent
public static void onLeave(EntityLeaveLevelEvent event) {
WorkAreaIndex.instance().onEntityLeave(event.getEntity());
Entity entity = event.getEntity();
WorkAreaIndex.instance().onEntityLeave(entity);
if (!(entity instanceof AbstractWorkAreaEntity area)) return;
if (!(event.getLevel() instanceof ServerLevel serverLevel)) return;
Entity.RemovalReason reason = area.getRemovalReason();
if (reason == null || !reason.shouldDestroy()) return;
for (Entity loadedEntity : serverLevel.getAllEntities()) {
if (loadedEntity instanceof AbstractWorkerEntity worker
&& area.getUUID().equals(worker.getBoundWorkAreaUUID())) {
worker.setCurrentWorkArea(null);
}
}
}

@SubscribeEvent
Expand Down
Loading
Loading