
VillageAI
Intelligent village defense system. Villages centered on bells automatically detect nearby villagers. Friendly players gain reputation by trading. Hostile players lose reputation by attacking villagers. When hostile players are nearby reputation drops belo
2.1.1
Compatibility
Changes
[2.1.1] - 2026-03-11 - Bug Fix Release
๐ Bug Fixes
-
Village data lost on restart โ
loadVillagesAsync()andvalidateVillageData()checked for a"villages"YAML section that was never written; data is saved at root level ("village-0","village-1", โฆ). Both methods now iterate root keys directly, so all villages survive server restarts. -
Data loss on shutdown (async save race) โ
onDisable()calledsaveVillages()which usedCompletableFuture.supplyAsync(). On server shutdown, the JVM kills thread pools before async tasks complete, silently discarding the save.onDisable()now calls a new synchronoussaveVillagesSync()method that blocks until the file is written. -
VillageValidatorNPE on class load โprivate static final Logger logger = VillageAIPlugin.getInstance().getLogger()was evaluated when the class was first loaded by the JVM โ potentially beforeonEnable()sets the instance โ causing aNullPointerException. Replaced with a lazylogger()method that reads the instance at call time. -
/vtrade acceptalways returned "Quest not found" โ The command displayed only the first 8 characters of the quest UUID (e.g.a1b2c3d4), then tried to reconstruct a full UUID by appending"-0000-0000-0000-000000000000". This produced a UUID that never matched any quest./vtrade questsnow shows the full UUID, and/vtrade acceptparses it directly without reconstruction. -
VillageManager.restoreVillage()did not populatevillagerToVillagemap โ Members were written directly into theVillageset, bypassingaddMember(). After a restart,getVillageOf(villager)always returnednullfor all loaded villagers.restoreVillage()now iterates restored members and inserts them into the map.
โ ๏ธ Minor Fixes
-
ECONOMY_UPDATE_INTERVAL = 24000was 24 000 ms (24 seconds), not 24 hours as the comment stated. Changed to86_400_000L. -
VillageEconomy.getVillageUUID()returned world UUID โ every village in the same world shared the same economy UUID, making trade offers indistinguishable across villages. Now derives a deterministic per-village UUID from the village's coordinate-based ID string. -
updateAllPrices()andgenerateTradeOffers()looped all~1000 Materialvalues on every economy update. Both methods now only iterate materials actively tracked inSupplyData, reducing unnecessary CPU work. -
DeliveryQuest.onReward()never gave emeralds โ only sent a chat message. Emeralds are now added to the player's inventory (or dropped at their feet if the inventory is full). -
Dead field
VillagerNameManager.villageManagersโ declared but never read or written. Removed.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:QkbM4VXn:4W99dTN0"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:QkbM4VXn:4W99dTN0"
}
