Tags
Creators
Details
All versions
4.1.0
Release
SacredCows 4.1.04 months ago 34
Compatibility
Minecraft: Java Edition
26.1.x
Platform
Fabric
Supported environments
Server-side
Singleplayer
Changes
[4.1.0]
Changed
- Chunk loading for named cows migrated from
setChunkForcedto a customTicketType(sacred_cows) withaddTicketWithRadius/removeTicketWithRadius, which is the API-appropriate path for forced chunk loading in 1.21.x and doesn't persist into the world's forced-chunks list - Permission level handling now uses the
PermissionLevelenum throughout instead of rawints, with parsing centralized inCowConfig.getPermissionLevel(). The reader accepts either an int id or an enum name and falls back toGAMEMASTERSon invalid input executeSetPunishmentModenow returns a failure (status 0) with a user-facing error on invalid input instead of silently defaulting toDEATH/sacredcows confignow styles the current punishment mode in yellow to match the visual treatment of other config values, and normalizes case when comparing the in-memory value to the saved property so the "changed" indicator behaves correctly- Cow names in log messages and the death broadcast now use
getCustomName().getString()instead of letting the rawComponentstringify itself (which producedliteral{Bessie}instead ofBessie) - Logging cleaned up across the mod:
System.err.printlncalls inCowConfigreplaced withLOGGER.error, hardcoded logger name inCowProtectionFeaturereplaced with the class name, and concatenated log strings converted to SLF4J parameterized format
Fixed
- 3x3 chunk-loading radius around named cows actually covers a 3x3 area now; the previous implementation looped over
dx/dzoffsets but constructednew ChunkPos(center.x(), center.z())inside the loop, so it only ever added the center chunk nine times CowChunkLoaderFeature.onCowDeathno longer registers a freshAFTER_DEATHlistener every time a cow dies — the listener is now registered once inregisterEventHandlersand the cleanup logic is called directly. (This was a slow-growing listener leak: every named-cow death added another listener, and every existing listener fired on every subsequent death.)- Saved cow positions are now actually restored on server start.
CowChunkLoaderFeaturereads from theCowPositionsDatasingleton onSERVER_STARTEDand re-adds chunk tickets for each persisted position before scanning live entities. The save path (SERVER_STOPPING) was already wired up; the load path had been missing - Named cows no longer leak chunk tickets as they wander. When a cow moves to a new chunk, the ticket on the previous chunk is now removed (this was dropped silently during the
setChunkForced→ ticket-API migration earlier in 4.0.3) CowConfig.save()now writes permission levels as integer ids rather thanString.valueOf(enum), which was producing strings likeGAMEMASTERSthat the loader couldn't parse back. Configs with non-defaultbypass-op-leveloradmin-op-levelwere silently resetting to default on restartCowPositionsData.getInstance()is nowstatic, so it can actually be used as a singleton accessorCowConfigconstructor is now called with the realconfigFilepath instead ofnullinSacredCows.loadConfig, sosave(),load(), andcreateDefaultConfig()are no longer no-ops- Eliminated several redundant
AFTER_DEATHregistrations inSacredCows.onInitializethat duplicated work already done inside feature classes
Removed
CowConfig.setDefaultProperties()— unreachable;createDefaultConfig()writes defaults directly via the enum iteratorsSacredCows.getVersion()and the/version.propertiesread it depended on — unusedcleanupTickCounter,cowTickCounter, and the publicserverTickCounterfield onSacredCows, along with the inline tick-dispatch loop inonInitialize
Technical
- Tick-driven work centralized in a new
util.TickCounterclass that exposesregisterIntervalCallback(intervalTicks, Runnable);CowProtectionFeatureandCowChunkLoaderFeaturenow register their periodic work through it instead ofSacredCowsowning the tick loop CowProtectionFeatureandCowChunkLoaderFeaturetake aTickCountervia constructor injectionScoreboardFeaturenow owns its ownSERVER_STARTEDregistration viaregisterEventHandlers()instead of being called inline fromSacredCowsCowChunkLoaderFeatureowns its ownSERVER_STARTED,ENTITY_LOAD,AFTER_DEATH, andSERVER_STOPPINGregistrations in one placeinstanceofpattern variables adopted inCowProtectionFeature.getPlayerFromDamageSource(projectile instanceof Projectile proj,projectile instanceof TraceableEntity ownable)- Build now compiles with
-Xlint:deprecation - Bumped
mod_versionto4.1.0
Upgrade notes
- Chunk loading mechanism changed. 4.0.2 and earlier used the world's
forced-chunks list (the one populated by
/forceload) to keep named cows loaded. 4.1.0 uses Fabric's ticket system instead, which doesn't persist to disk. After upgrading, existing worlds may have leftover entries from 4.0.2 in their/forceload queryoutput. These are harmless but can be cleared with/forceload remove <x> <z>for each, or/forceload remove allif you have no other manually-set forced chunks.
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:iI29uZEP:p3euPNtj"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:iI29uZEP:p3euPNtj"
}

