For Developers/API Reference·匠

API Reference

plugin.siren.API.CultivationAPI is the facade an addon calls directly, rather than the events it listens to. It has 261 public static methods under 250 distinct names (11 names are overloaded), plus one constant, MENU_NAV_EVENT_KEY. This page lists every one, grouped by what it is for; the 30 register* methods (33 counting overloads) and their 23 unregister* counterparts are summarized in the registration index and explained with examples on API Registries. CultivationConfigs adds 50 more accessors and saveAll(); they are in Config files.

Everything on the class is static; the constructor is private and it is never instantiated. Most methods take the same two arguments every Hytale ECS system already has in hand, which the tables abbreviate to accessor, ref:

Threading

The source is explicit about this in one place: the event listeners are invoked synchronously on the world thread of the player the event happened to, and you must hop threads yourself - CompletableFuture.runAsync(task, otherWorld) - before touching anything that lives on another world.

The reads below carry no separate documented threading contract of their own; they are plain component reads through the accessor you pass in, so the accessor's own thread rules apply. Call them from the world thread that owns the Ref you are reading.

Registration and listener calls are the exception in the opposite direction: they are safe from any plugin's setup() in any load order, because the backing structures are static maps and CopyOnWriteArrayLists that nothing reads until a player interacts with the mod.

Component types

Ask for a ComponentType when you want to read or write a Cultivation component through the ECS yourself rather than through the convenience reads below.

MethodReturnsDescription
getCultivationComponentType()ComponentType<EntityStore, CultivationComponent>Realm, stage and banked Qi.
getCultivationStateComponentType()ComponentType<EntityStore, CultivationStateComponent>Transient state - meditation, ritual progress.
getCultivationSettingsComponentType()ComponentType<EntityStore, CultivationSettingsComponent>Per-player settings, such as the HUD toggle.
getRaceComponentType()ComponentType<EntityStore, RaceComponent>The player's chosen race.
getSkillTreeComponentType()ComponentType<EntityStore, SkillTreeComponent>Unlocked nodes and unspent points - see Skill Tree.
getTechniqueComponentType()ComponentType<EntityStore, TechniqueComponent>Learned arts and cooldowns - see Techniques.
getSpiritVeinComponentType()ComponentType<ChunkStore, SpiritVeinComponent>A chunk's Spirit Vein pool. This one is a ChunkStore component, not an entity component - see Qi Gathering.

Cultivation state

Plain reads of a player's ladder. They return null, 0 or Human rather than throwing when the player has no component yet. While a ProgressionProvider is installed, getRealm, getGlobalLevel and getQi answer from the provider, and getStage is always null.

CultivationRealm runs BODY_REFINEMENT, QI_CONDENSATION, FOUNDATION_ESTABLISHMENT, GOLDEN_CORE_FORMATION, NASCENT_SOUL, SOUL_FORMATION, VOID_REFINEMENT. CultivationStage runs EARLY, MIDDLE, LATE, PEAK. Both are documented player-side on the Cultivation Realms page.

MethodReturnsDescription
getCultivationComponent(accessor, ref)CultivationComponentThe raw component, or null when the entity does not have one.
getCultivationStateComponent(accessor, ref)CultivationStateComponentThe raw state component, or null.
getRaceComponent(accessor, ref)RaceComponentThe raw race component, or null.
getRealm(accessor, ref)CultivationRealmThe player's effective realm - the one every realm gate tests - or null if they have no CultivationComponent, for example a non-player entity. Under a ProgressionProvider this is the provider's equivalent realm.
getStage(accessor, ref)CultivationStageThe player's stored sub-stage, or null if they have no CultivationComponent. Always null while a ProgressionProvider is installed, since a replacement ladder has only its own getSubRankLabel. Prefer getGlobalLevel for anything numeric.
getGlobalLevel(accessor, ref)intRealm and stage flattened into one ever-increasing number, or the installed provider's level. 0 when they have no CultivationComponent.
getQi(accessor, ref)floatBanked progress toward the next rank-up - banked Qi, or the installed provider's progress. 0 when they have no CultivationComponent.
getRace(accessor, ref)PlayerRaceThe player's current race, defaulting to Human when they have no RaceComponent yet. Never null.
isMeditating(accessor, ref)booleanWhether they are sitting in meditation right now. false when they have no state component.
getHighestRealmReached(accessor, ref)intThe player's permanent high-water realm mark, as a CultivationRealm ordinal. This is the account-scoped record Progressive Reveal reads: it rises only through earned progress, never through an admin realm, level or reset. Read-only; it falls back to the live realm if the account has not been seeded yet.
isMaxLevel(accessor, ref)booleanWhether the cultivator is at the top of whichever progression is live.
getQiRequiredForNext(accessor, ref)floatThe Qi the cultivator needs to bank before their next rank-up becomes possible.
isReadyForBreakthrough(accessor, ref)booleanWhether they have banked enough for a realm breakthrough and only need the ritual.
isReadyForAdvancement(accessor, ref)booleanWhether they have banked enough for a sub-stage advancement and only need the ritual.

Driving progression

v0.6.1 The write half of the reads above. Every one of these goes through the same path the mod's own commands and rituals use, so an add-on granting Qi fires the same events, honors an installed ProgressionProvider, and refreshes the HUD and rankings exactly as meditating would - none of which is true of reaching into the component.

Banked Qi has two writes: addQi for anything a player earned (through every multiplier and the cancellable PreQiGainEvent), and setQi for the admin path that skips both. To re-scale a gain that is already happening instead, hook CultivationEvents.onPreQiGain - see the events page.

setQi, setRealm and setStage write the built-in ladder. Under an installed ProgressionProvider they change nothing the cultivator can see, because the provider owns the numbers; drive the provider's own API instead (addQi is the one write here that reaches a provider).

One entity, one thread

All of these run on the caller's thread and touch only the given entity, so they are safe from a system, a command or an interaction on that entity's own world thread. Inside a ticking system the accessor must be that system's CommandBuffer, never a bare Store. To act on a player who may be in another world, hop to their world thread first - Universe.get().getPlayer(uuid) then PlayerRef then CompletableFuture.runAsync(..., theirWorld) - as the mod's own admin tooling does.

MethodReturnsDescription
addQi(accessor, ref, float amount, PlayerRef playerRef)Grants Qi exactly as absorbing a core or meditating would: through every multiplier (race, skill tree, pills, sect hall, Yin-Yang balance), through the cancellable PreQiGainEvent, and into whatever progression is installed. playerRef may be null for a non-player cultivator. A no-op for an entity with no CultivationComponent.
setQi(accessor, ref, float qi)Sets banked Qi outright, skipping every multiplier and event - the /cultivation admin setqi path, not the gameplay one. Prefer addQi for anything a player earned. A NaN or infinite value is ignored and a negative one is clamped to 0. Built-in ladder only. It does not rank anyone up; that still takes the meditation ritual.
setRealm(accessor, ref, CultivationRealm realm)Moves a cultivator to a realm outright and re-applies their stat bonuses. It fires no breakthrough event and runs no ritual, because nothing was broken through. Built-in ladder only.
setStage(accessor, ref, CultivationStage stage)As setRealm, for the sub-stage. Built-in ladder only.
completeBreakthrough(accessor, ref, PlayerRef playerRef)booleanCompletes a realm breakthrough right now - consuming the banked Qi, granting the skill points, firing PreBreakthroughEvent and BreakthroughEvent, and playing the celebration - as though the ritual had just finished. What an addon offering its own path to a breakthrough (a quest, an item, a boss kill) should call. On the built-in ladder the cultivator must be at Peak stage, but banked Qi is not required. Returns true only if the rank actually advanced.
completeAdvancement(accessor, ref, PlayerRef playerRef)booleanAs completeBreakthrough, for a single sub-stage advancement. On the built-in ladder the cultivator must not be at Peak stage. Returns true only if the rank actually advanced.
demote(accessor, ref, PlayerRef playerRef, boolean wasBreakthrough)Applies the failed-ritual penalty: one sub-stage down (never below the current realm's first stage) and the banked Qi wiped, with the demotion events fired. wasBreakthrough only picks which message the cultivator is sent.
startMeditating(accessor, ref)Seats a cultivator in meditation exactly as /cultivation meditate does. It is refused (and the player told why) while a Nascent Soul is fleeing or a Dream Trial, Inner Demon duel or Cleanse Rite is running; otherwise the cancellable PreMeditationStartEvent fires, and on success the cultivator sits, the pose plays and MeditationStartEvent fires. Moving away still cancels it. A cultivator already meditating is left alone.
tryStartMeditating(accessor, ref)booleanAs startMeditating, but reports the outcome. true when the cultivator is now meditating (including already being so); false when they have no cultivation state, their soul is fleeing, a Dream Trial, Inner Demon duel or Cleanse Rite is active, or a PreMeditationStartEvent listener cancelled it. In every false case nothing was changed.
stopMeditating(accessor, ref)Stands a cultivator up exactly as /cultivation meditate does, including the stand-up penalty. Stopping someone mid-ritual costs what walking away costs: a breakthrough or advancement demotes a stage and scatters the banked Qi, a Closed-Door Retreat forfeits its payout, an Ascension or Reincarnation fails, a weapon refinement is forfeited. Check isMeditating and the ritual reads first if you only meant to stand up an idle meditator.
tryStopMeditating(accessor, ref)booleanAs stopMeditating, but reports the outcome. true when the cultivator is no longer meditating (including never having been); false when they have no cultivation state or a PreMeditationStopEvent listener kept them seated, in which case nothing changed and no penalty applied.
activateQiBarrier(accessor, ref, float durationSeconds, float shieldAmount)Grants a timed Qi Barrier - an absorption pool that deducts from incoming damage before health does, the same primitive the built-in qi_barrier technique uses. Fires the usual pre and post buff-apply events.

Skill tree

Node ids are the ones in plugin.siren.ECS.SkillTree.SkillTreeRegistry - for example "VITALITY_1". The player-facing map of the tree is on the Skill Tree page, and points are granted per the Points-Per-Breakthrough and Points-Per-Advancement keys on the Config page.

MethodReturnsDescription
getAvailableSkillPoints(accessor, ref)intThe player's unspent skill tree points, or 0 when they have no SkillTreeComponent yet.
isNodeUnlocked(accessor, ref, String nodeId)booleanWhether the player has unlocked that node id (see SkillTreeRegistry for the built-in ids, for example "VITALITY_1"). false when they have no SkillTreeComponent or the id matches no unlocked node.
grantSkillPoints(accessor, ref, int points)Grants unspent skill tree points. A no-op for an entity with no SkillTreeComponent.
unlockSkillNode(accessor, ref, String nodeId)booleanUnlocks a node, spending the cultivator's points and re-applying the stat modifiers the node grants - the same path the skill tree menu takes. Returns true if the node was unlocked.
grantSkillNode(accessor, ref, String nodeId)booleanGrants a node without charging points, for an addon handing one out as a reward rather than a purchase. Returns true if the node was granted.

The world's Qi

v0.6.1 readSpiritVein reports what a chunk's Spirit Vein holds without creating or writing anything - and answers truthfully for a chunk nobody has ever visited, because the seeding roll is a pure function of the world seed and the chunk position. That is what makes a map overlay or a divining item possible without seeding a vein into every chunk a player walks past.

Coordinates are chunk coordinates, not blocks. Hytale chunks are 32 blocks wide, so use ChunkUtil.chunkCoordinate(blockX) rather than dividing by 16.

MethodReturnsDescription
readSpiritVein(World world, int chunkX, int chunkZ)SpiritVeinManager.VeinReadingReads a chunk's Spirit Vein without touching it: no component is created and nothing is written. A chunk nobody has visited still gives a truthful answer. The right call for a map overlay or a divining item.
drainSpiritVein(World world, int chunkX, int chunkZ, float amount)floatDraws Qi out of a chunk's vein, creating and seeding it if nothing has touched it yet. Must run on that world's own thread. Returns how much was actually drawn - less than requested when the vein is running dry, and 0 when the chunk is not loaded.
getMeditationRegenMultiplier(String world, int chunkX, int chunkZ, UUID player)floatThe combined meditation multiplier the formations and dwelling covering that chunk are worth this player - above 1 inside a Qi-gathering array or their own abode, below 1 where a warding array chokes an outsider.

Asking the subsystems

v0.6.1 The events for sects, daos, beasts, dwellings and duels existed with no way to ask a question - listening to SectEvents without being able to say "what sect is this player in" meant shadowing the whole registry. These close that gap. The sect and abode reads are UUID-keyed, so they answer for offline players.

MethodReturnsDescription
isSectSystemEnabled()booleanWhether sects are switched on at all (Sects-Enabled). Ask before building anything on top of sects, and stand down and say so if it is false.
getSect(UUID player)SectThe sect this player belongs to, or null. Answers null for everyone while Sects-Enabled is off; the roster stays in memory, so switching the system back on restores every sect.
getSectByName(String name)SectThe sect of this name (case-insensitive), or null. null for every name while Sects-Enabled is off.
getSectQiBonusPercent(UUID player)floatThe extra Qi percentage this player's sect hall is worth them, or 0.
getSectHolding(String world, int chunkX, int chunkZ)SectThe sect whose hall or building stands in this chunk, or null.
getSectBuildingAllowance(Sect sect)intHow many buildings this sect may hold at its current level.
getSectBuildingType(String typeId)SectBuildingTypeThe configured kind for an id, or null if a server removed it.
addSectProgressXp(UUID member, boolean breakthrough)intBanks shared sect progression for a member, exactly as their own advancement does. Returns how many levels the sect gained.
getOrCreateDao(accessor, ref)DaoComponentThe cultivator's Dao component, created if they have never touched the Dao system.
getDaoElement(accessor, ref)DaoElementThis cultivator's chosen element, or null when they walk no dao.
getPath(accessor, ref)CultivationPathThis cultivator's moral path (Righteous, Devil or neither) under the live Yin-Yang balance, or null when they have no Dao component.
getYinPercent(accessor, ref)floatThis cultivator's Yin percentage, 0 (wholly Yang) to 100 (wholly Yin).
getKarma(accessor, ref)floatThis cultivator's karma - the blood on their ledger that deepens a tribulation.
getTerrainElement(String biomeName)DaoElementThe element a biome's name resolves to, or null if it maps to none.
getGroundDao(String world, int chunkX, int chunkZ)DaoElementThe element the ground in this chunk presses cultivators toward, or null - which covers unclaimed ground, a sect with no Dao, and a sect building whose Dao switch is deliberately off.
getAbode(UUID player)DwellingThis player's claimed Cave Abode, or null when they have none.
getDwellingAt(String world, int chunkX, int chunkZ)DwellingWhichever dwelling encloses this chunk (personal or a sect hall's), or null.
getDuel(UUID player)DuelManager.DuelThe duel this player is currently in, or null.
getFormationTier(Formation formation)intThis formation's tier, clamped to [0, FormationManager.MAX_TIER].
getStrongestTrappingTierAgainst(String world, int chunkX, int chunkZ, UUID player)intThe strongest trapping-array tier this player is currently caught by in this chunk, or a negative value when not trapped.
isArrayAltarAvailable()booleanWhether the AugmentBlocks builtin resolved, i.e. array altar growth/decay/reconciliation is active this boot.

Lifespan

Lifespan (寿元) is the per-profile online-play-hour budget keyed to the highest realm ever reached. It is off by default on a server, so start with isLifespanEnabled(). Events: LifespanEvents. Player-facing docs: Lifespan.

MethodReturnsDescription
isLifespanEnabled()booleanWhether Lifespan is switched on server-wide. Off by default.
getLifespanBudgetHours(accessor, ref)floatThis cultivator's current online-play-hour budget, keyed to the highest realm they have ever reached. 0 if not yet tracked.
getLifespanUsedHours(accessor, ref)floatHow many hours of the budget above this cultivator has spent, online, since Lifespan started tracking them.
getLifespanRemainingHours(accessor, ref)floatHours remaining before Withering, floored at 0.
getLifespanRemainingFraction(accessor, ref)floatRemaining hours as a 0..1 fraction of the current budget - 1 for a fresh/untracked cultivator.
getLifespanWarnTier(accessor, ref)intThe highest warning tier already shown (0..3) for the current countdown.
isWithering(accessor, ref)booleanWhether this cultivator is currently in the post-expiry Withering grace state.
getLifespanGraceMinutesRemaining(accessor, ref)floatOnline minutes of grace left before Lifespan-Expiry-Action runs, or 0 if not currently Withering.
extendLifespan(accessor, ref, float hours, API.LifespanEvents.ExtendSource source)floatGrants bonus hours on top of the cultivator's current realm-mark budget, for the PILL and ADMIN sources only (a breakthrough's own budget rise is automatic). Fires PreLifespanExtendEvent then LifespanExtendEvent, and clears Withering if the grant brings the remaining hours back above 0. Returns the hours actually granted - 0 if Lifespan is disabled, there is no component, or a listener cancelled or zeroed the grant.

Body tempering

The second ladder, climbed by taking the hit. Events: BodyTemperingEvents. Player-facing docs: Body Tempering.

MethodReturnsDescription
getBodyTemperingLevel(accessor, ref)intThe cultivator's body-tempering level, or BodyTemperingComponent.MIN_LEVEL if they have none yet.
getMaxBodyTemperingLevel()intThe ceiling a body can temper to, from the config.
getTemperingStage(accessor, ref)intThe cultivator's Tempering Stage (锻体境), 0 to 9 - a pure function of their body-tempering level, 0 while not yet tempered (a level under 11 at the default Max-Level). Nothing resets it, so it reads correctly the instant a save loads.
getTemperingHealthBonus(accessor, ref)floatThe flat max-health bonus this cultivator's current Tempering Stage grants. 0 while untempered.
getBodyTemperingProgress(accessor, ref)floatHow far through the current level this body is, 0..1. Always 1 at the ceiling.
getArmorProtectionScore(accessor, ref)floatHow armored this entity is right now: 0 for bare skin, 1 for the most protective set the server's config describes. Read live off the worn items, scoring a modded piece on the same terms as a vanilla one.
getBodyTemperingDamageReduction(accessor, ref)floatThe fraction of incoming damage the tempered body currently turns aside, 0 to 1, scaled by both how far up the ladder they are and what they are wearing. 0 when body tempering is off. With the Endless Leveling protection handoff on, EL applies it and this still reports the same figure.
addBodyTemperingXp(accessor, ref, PlayerRef playerRef, float amount)intGrants body-tempering XP directly, leveling the body as far as it goes and firing the same events a real blow would. For an addon with its own idea of what tempers a body. The armor scaling is not applied - this is the raw amount to bank. Returns how many levels were gained.

Weapon spirits

Reads for a Life-Bound weapon's spirit (器灵). Each takes the held ItemStack, except the voice-line history, which is per player. Events: WeaponSpiritEvents. Player-facing docs: Weapon Spirits.

MethodReturnsDescription
isWeaponSpiritAwakened(ItemStack stack)booleanTrue once this held weapon's spirit has woken up.
getWeaponSpiritLevel(ItemStack stack)intThis weapon spirit's current level, or 0 if it has not awakened yet.
getWeaponSpiritBondedTechnique(ItemStack stack)StringThe technique id a matured weapon spirit lends its owner, or null if not matured (or none configured).
getWeaponSpiritTemperament(ItemStack stack)WeaponSpiritTemperamentThis spirit's resolved WeaponSpiritTemperament, or null if not yet awakened.
getWeaponSpiritDevotion(ItemStack stack)floatThe spirit's current Devotion (0 to WeaponSpirit-Devotion-Max), decay already applied - the number to show a player.
getWeaponSpiritDevotionMax()floatThe configured ceiling getWeaponSpiritDevotion is measured against (WeaponSpirit-Devotion-Max).
getWeaponSpiritName(ItemStack stack)StringThe player-chosen name for this spirit, or blank if never named (or not awakened).
getWeaponSpiritNextPerkLevel(ItemStack stack)intWhich level 2 to 4 perk the spirit's next level would unlock (2 is WHETTED, 3 WATCHFUL, 4 SWIFT), or 0 at WeaponSpirit-Max-Level or before it awakens.
getWeaponSpiritRecentVoiceLines(UUID player)List<Message>The last (up to five) composed voice lines this player has been sent this session, oldest first. Never null.

Heavenly and Personal Dao

The layer on top of the Elemental Dao: the Heavenly Dao (天道) meter, the open registry of Personal Daos, and the component that holds both. Events: DaoComprehensionEvents. Player-facing docs: The Dao.

MethodReturnsDescription
isPersonalDaoSystemEnabled()booleanWhether the Personal Dao system is enabled in config.
getHeavenlyDao(accessor, ref)floatThis cultivator's raw Heavenly Dao value, 0..Heavenly-Dao-Max.
getHeavenlyDaoFraction(accessor, ref)floatThis cultivator's Heavenly Dao as a 0..1 fraction of Heavenly-Dao-Max.
getHeavenlyDaoRank(accessor, ref)HeavenlyDaoRankThis cultivator's Heavenly Dao rank, resolved from their current fraction.
getOrCreateDaoComprehension(accessor, ref)DaoComprehensionComponentThis cultivator's Dao Comprehension component (Heavenly Dao + every Personal Dao's progress), creating it if they have never touched the layer.
registerPersonalDao(PersonalDao dao)Registers a Personal Dao. Registering the same key twice replaces the first.
unregisterPersonalDao(String key)Withdraws a previously registered Personal Dao - for a plugin unloading cleanly.
getPersonalDaos()List<PersonalDao>Every registered Personal Dao, sorted by sort order then registration order. Unmodifiable.
getPersonalDao(String key)PersonalDaoThe Personal Dao registered under this key, or null if nothing claims it.
getPersonalDaoComprehension(accessor, ref, String key)floatThe comprehension this cultivator has banked toward this Personal Dao, or 0.
isPersonalDaoManifested(accessor, ref, String key)booleanTrue if this cultivator currently has this Personal Dao manifested.
addPersonalDaoComprehension(accessor, ref, String key, float amount, PlayerRef playerRef)Grants (or, with a negative amount, works off) comprehension toward a registered Personal Dao by key. A no-op for an unregistered key.

Merit, seasons and rankings

Events: MeritEvents and SeasonEvents. Player-facing docs: Merit and Seasonal Cycle.

MethodReturnsDescription
getMerit(accessor, ref)floatThe cultivator's Merit (功德) - the virtue ledger sitting beside, and independent of, Karma.
addMerit(accessor, ref, PlayerRef playerRef, float amount)Raw, addon-facing Merit credit (or, with a negative amount, correction): no deed, no cooldown, no soft-Path throttle, so the caller decides the amount is fair. Runs the same rank-up and technique-grant consequences a gated gain would.
isSeasonEnabled()booleanWhether the shared season cadence is switched on (Season-Enabled). A feature that hangs off a season boundary should stand down and say so, naming the key, rather than inventing a second clock.
getCurrentSeasonId()intThe season currently running, or 0 when seasons have never started (Season-Enabled is off, or the first tick has not happened). Ask this from your own setup() rather than waiting for SeasonOpenEvent - the boot-time open fires before any addon has registered a listener.
isSeasonChampion(UUID uuid)booleanWhether this account holds any of the four season championships - Path War, cultivation, Dao mastery or sect - as of the last season that closed. false the whole time Season-Enabled is off.
getLeaderboardSnapshot()List<Utils.CultivationLeaderboard.Entry>An unsorted, immutable snapshot of every recorded ranking row - one per account this server has ever seen cultivate, online or not. Built for filtering. Sandbox profiles never enter it.

Meridian injuries and oaths

Events: MeridianEvents and OathEvents. Player-facing docs: Meridian Injuries and Heavenly Oaths.

MethodReturnsDescription
hasMeridianInjury(accessor, ref, MeridianInjury injury)booleanTrue if this cultivator currently carries this Meridian Injury.
getMeridianInjuryMagnitude(accessor, ref, MeridianInjury injury)floatThe magnitude (0..1) rolled for this Meridian Injury, or 0 if not carried.
getMeridianInjuryRemainingSeconds(accessor, ref, MeridianInjury injury)floatThe online-active seconds remaining on this Meridian Injury, or 0 if not carried.
getOaths(UUID player)List<Utils.Oath.Oath>Every oath currently binding this cultivator.
isDaoHeartFlawed(accessor, ref)booleanWhether this cultivator currently carries a Dao-Heart Flaw (a broken oath's punishment).

Techniques

performTechnique runs every gate - system enabled, per-technique enabled, realm unlock, dao match, Qi cost, cooldown - and on success deducts the Qi, stamps the cooldown and runs the effect. It works on built-in techniques and on ones you registered yourself, and it fires TechniqueEvents.PreTechniquePerformEvent and TechniquePerformEvent in both cases. grantTechnique is the learn side: the same chokepoint every built-in source goes through, so the pre-learn veto and the learn event fire for an addon's own grant too. Building a technique is covered under Registries.

MethodReturnsDescription
registerTechnique(String id, String displayName, String nameKey, String descriptionKey, TechniqueRule defaultRule, TechniqueEffect effect)TechniqueRegisters a brand-new technique, usable through every technique trigger. Re-registering an id returns the existing technique.
newTechniqueRule(String id, boolean enabled, boolean daoSpecific, String requiredElement, String elements, String damageType, String unlockRealm, float qiCost, float cooldownSeconds, Object... params)TechniqueRuleConvenience builder for the TechniqueRule you pass to registerTechnique. params is an even-length list of alternating names and numbers your effect reads back with context.getParam(key, fallback).
performTechnique(accessor, ref, PlayerRef playerRef, Technique technique)booleanPerforms a technique right now, running every gate and, on success, deducting Qi, stamping the cooldown and running the effect. The player is messaged either way. Returns true if it was performed, false if a gate blocked it.
grantTechnique(accessor, ref, PlayerRef playerRef, Technique technique, String messageKey)booleanWrites a technique into a cultivator's learned set for good, through the same chokepoint every built-in source uses - so the pre-learn veto and the learn event fire for an addon's own grant too. messageKey is a lang key announcing it (taking a technique param), or null to say nothing. Returns true if it taught something new. Inside a system, accessor must be the system's CommandBuffer.
registerTechniqueGate(String id, String failureKey, TechniqueGate.Condition condition)TechniqueGateRegisters a TechniqueGate: a condition another mod supplies at runtime ('is this player currently transformed into some other mod's creature form'). A technique names the gate ids it requires on its TechniqueRule. Re-registering an id replaces it. A gate that throws is treated as passed.
registerTechniqueGate(String id, String failureKey, String statusKey, TechniqueGate.Condition condition)TechniqueGateAs above, with your own short status-label key for the /cultivation technique list and the keybinds UI instead of the generic fallback.
unregisterTechniqueGate(String id)Withdraws a gate - for a plugin unloading cleanly.

Mastery and keybind presets

The mastery ladder and the keybind loadouts. A preset is exported as a detached, portable TechniquePresetSnapshot and imported through a filter that keeps only the arts the receiving cultivator may actually fire - a snapshot is a piece of paper, not a license.

MethodReturnsDescription
registerMasteryStage(MasteryStageRule stage)booleanRegisters an extra mastery rung, appended after the configured ladder. Returns true if the rung was added, false if the ladder is already full.
getMasteryStages()MasteryStageRule[]The mastery ladder as the server has it configured (up to five rungs).
getTechniqueMasteryStage(accessor, ref, String techniqueId)intThe rung (0-based) this cultivator has taken an art to.
getTechniqueMasteryMultiplier(accessor, ref, String techniqueId)floatThe multiplier this cultivator's mastery of an art applies to its magnitude.
addTechniqueMasteryXp(accessor, ref, PlayerRef playerRef, Technique technique)Banks mastery practice for an art, promoting it if that was the last requirement outstanding - the same path a real performance takes.
registerTechniquePresetCap(String key, int cap)Lets an addon raise how many keybind layouts a player may keep (default 3, ceiling 8). The highest registered cap wins. Withdraw it in shutdown() with unregisterTechniquePresetCap.
unregisterTechniquePresetCap(String key)Withdraws a cap raise - for a plugin unloading cleanly.
getMaxTechniquePresets()intHow many keybind layouts a player may keep right now: the highest any addon asked for, never below the default and never above the ceiling.
getTechniquePresetNames(accessor, ref)List<String>What this cultivator's keybind layouts are called, in the order the component holds them - so the index of a name here is the index exportTechniquePreset takes. Names are not unique.
exportTechniquePreset(accessor, ref, int presetIndex)TechniquePresetSnapshotCopies one of the cultivator's keybind layouts out as a detached, portable TechniquePresetSnapshot, by index. null if they have no technique component or no layout at that index.
exportTechniquePreset(accessor, ref, String presetName)TechniquePresetSnapshotThe same copy, addressed by the layout's name and matched ignoring case. Names are not unique, so it returns the first match.
importTechniquePreset(accessor, ref, TechniquePresetSnapshot snapshot, String newName)TechniquePresetSnapshot.ImportResultAdds a keybind layout from a snapshot, keeping only the arts the cultivator may actually fire: registered on this server, enabled, and already known to them. Returns an ImportResult with the new layout's index and final name, how many slots carry an art, and how many arts were refused for each reason.

Spirit beasts

The bound companion and the arts it grows into. Events: BeastEvents. Registering an art is covered under Registries. Player-facing docs: Spirit Beasts.

MethodReturnsDescription
getBeast(accessor, ref)SpiritBeastComponentThe player's bound spirit beast, or null when they have none.
registerBeastArt(String id, String displayName, String nameKey, String descriptionKey, BeastArtRule defaultRule, BeastArtEffect effect)BeastArtRegisters a beast art, or returns the already-registered one if the id is taken. An art belongs to a species: list its id in that species' Arts in BeastConfig before any creature can learn it. Registering alone gives it to nobody.
getBeastArts()Collection<BeastArt>Every registered beast art, built-ins first then addons in registration order.
getBeastArt(String id)BeastArtThe art with this id (matched leniently on id or display name), or null.
getBeastArtRule(BeastArt art)BeastArtRuleThe server's effective rule for an art - its config entry, else its registered default.
getKnownBeastArts(accessor, ref)List<BeastArt>The arts this cultivator's bound beast has actually grown into.
performBeastArt(accessor, ref, BeastArt art, PlayerRef playerRef)booleanPerforms one of the bound beast's arts on its owner's behalf, running every gate the mod's own callers run (feature enabled, beast summoned, art known and off cooldown, no addon veto). Returns true if the art actually fired.
getNextBeastForm(BeastSpecies species)BeastSpeciesThe species this beast would become next, or null if it is a terminal form.
getBeastEvolutionBlockReason(accessor, ref)StringWhy this cultivator's beast cannot evolve right now, as a lang key, or null when it can.
canBeastBeRidden(accessor, ref)booleanTrue if this cultivator's beast could carry a rider if summoned now.

Races, items and sect buildings

The small content registries. Each is covered with an example under Registries.

MethodReturnsDescription
registerRace(String id, String displayName, String translationKey, CultivationRealm unlockRealm, Supplier<RaceConfig> stats)PlayerRaceRegisters a brand-new race players can choose once their cultivation reaches unlockRealm. Re-registering an id is a no-op that returns the existing race.
registerQiAbsorptionItemModifier(String itemId, float multiplier)Registers, or overwrites, the Spirit Vein absorption multiplier granted while that item is held in a meditating player's active hotbar slot.
registerSectBuildingType(SectBuildingType type)Registers a kind of sect building. Server owners still choose whether any sect may raise it by listing it in SectConfig; this only makes the kind known.
registerLifeBoundTrait(LifeBoundTrait trait)Registers a nature a bound treasure can reveal. It enters the weighted roll immediately; treasures already bound keep what they rolled.
getLifeBoundTrait(String traitId)LifeBoundTraitThe trait with this id, or null.
getLifeBoundTraitAmount(ItemStack stack, String traitId)floatThe magnitude of a named trait on a specific item, or 0 when that treasure is of a different nature - so it is safe to ask about any trait on any item.
getLifeBoundGrantedTechnique(ItemStack stack)StringThe technique id this held treasure lends its owner right now, or null.

Profiles

v0.7.0 The read surface for a player's saved cultivators. Note these take a Store<EntityStore> rather than a ComponentAccessor.

Profiles are observed, not driven. There is deliberately no switchProfile on the facade - a switch is the player's own act, through the menu or /cultivation profile. What an addon does is listen (ProfileEvents, see Events), keep its own per-profile state in step, and declare that it does so via ProgressionProvider.supportsProfiles() (see Building Add-ons).

MethodReturnsDescription
getActiveProfileName(Store<EntityStore> store, Ref<EntityStore> ref)StringThe name of the profile the player is cultivating, or "" if they have never had one created. Readable at any time - which is what an addon needs when a player joins, since no switch has fired yet.
getProfileCount(Store<EntityStore> store, Ref<EntityStore> ref)intHow many real (non-sandbox) profiles this player keeps.
isTestProfileActive(Store<EntityStore> store, Ref<EntityStore> ref)booleanWhether the player is on the permission-gated sandbox profile, whose realm was set by hand rather than earned. Worth checking anywhere your own mod records or ranks a player's standing - Cultivation keeps a sandbox cultivator off its own leaderboard.
registerProfileCap(String key, int cap)Lets an addon raise how many profiles a player may keep (default 3, ceiling 6). The highest registered cap wins, and nothing is destroyed when a cap falls. Withdraw it in shutdown() with unregisterProfileCap.
unregisterProfileCap(String key)Withdraws a cap raise - for a plugin unloading cleanly.
getMaxProfiles()intHow many profiles a player may keep right now: the highest any addon asked for, never below the default and never above the ceiling.

Titles, banners, palettes and auras

v0.7.0 Everything cosmetic. A palette, aura or title id is stored on the player, never the object, so unregistering one is always safe: an id nobody claims falls back to the default look. Builders and examples are under Registries.

MethodReturnsDescription
registerTitle(CultivationTitle title)Adds a cosmetic title players may equip, to the picker and to every place a title is displayed (Rankings, sect roster, chat, the overhead nameplate). Call from your plugin's setup(); load order does not matter.
unregisterTitle(String titleKey)Removes a previously registered title - for a plugin unloading cleanly.
getTitles()List<CultivationTitle>Every registered title, in registration order. A fresh list, safe to hold.
getTitle(String titleKey)CultivationTitleThe title registered under this id, or null.
getTitle(Store<EntityStore> store, Ref<EntityStore> ref)CultivationTitleThe title the player has equipped, or null - including when the id they chose names a title nobody registers any more.
registerSectBanner(SectBanner banner)Adds a banner sects may fly over their hall, to the picker and to the beacon that stands over every claimed hall. Call from your plugin's setup(); load order does not matter, since nothing reads the registry until a hall pulses or a sect opens the picker.
unregisterSectBanner(String bannerKey)Removes a previously registered banner - for a plugin unloading cleanly. Sects still flying it are not migrated and do not need to be: a sect stores the id, never the banner, and getSectBanner answers null for an id nobody claims - which the hall beacon reads as "use the vein-tier default".
getSectBanners()List<SectBanner>Every registered banner, in registration order. A fresh list, safe to hold.
getSectBanner(String bannerKey)SectBannerThe banner registered under this id, or null if nothing claims it.
registerPalette(CultivationPalette palette)Adds a look players may wear, to the picker and to every page build. Call from your plugin's setup(); load order does not matter.
unregisterPalette(String paletteKey)Removes a previously registered palette - for a plugin unloading cleanly. Players still wearing it are not migrated and do not need to be: nothing stores a palette object, only its id, and getPalette(Store, Ref) falls back to the default for an id nobody claims.
getPalettes()List<CultivationPalette>Every registered palette, in registration order. A fresh list, safe to hold.
getPalette(String paletteKey)CultivationPaletteThe palette with this id, or null.
getPalette(ComponentAccessor<EntityStore> store, Ref<EntityStore> ref)CultivationPaletteThe palette the player is wearing, or null for Cultivation's own default look (also null when the stored id names a palette nobody registered). Resolution order: a registered CultivationPaletteLock wins outright, then the player's own choice, then a registered CultivationPaletteDefault.
getExplicitPaletteKey(accessor, ref)StringThe palette id the player actually has stored, before any lock or default is applied - null when they have never chosen at all.
resolvePaletteDefault(accessor, ref)StringThe key the first registered CultivationPaletteDefault names for this player, already checked against the palette registry, or null if none has an opinion.
registerPaletteDefault(String key, CultivationPaletteDefault rule)Registers a rule that supplies a default look for players who have not chosen one. The soft counterpart to registerPaletteLock: a lock overrides a choice, a default only fills the gap. The first rule to name a registered palette wins.
unregisterPaletteDefault(String key)Withdraws a default palette rule - for a plugin unloading cleanly. Players who were being drawn in its look are not migrated and do not need to be: nothing was ever written to them, so they simply return to Cultivation's own look the next time a page is built.
registerPaletteLock(CultivationPaletteLock lock)Registers a rule that can force a specific palette on some players - an off-ring Dao root claiming its own robes, say - overriding their own theme choice entirely while it applies. Call from your plugin's setup().
unregisterPaletteLock(CultivationPaletteLock lock)Removes a previously registered palette lock - for a plugin unloading cleanly.
resolveActivePaletteLock(accessor, ref)CultivationPaletteLockThe registered lock currently forcing a palette on this player, resolved to an existing registered palette - or null if no lock applies, or every applicable lock names a palette nobody has (yet) registered, in which case every caller falls through to this player's own settings exactly as an unclaimed palette id does everywhere else in this API.
document(Store<EntityStore> store, Ref<EntityStore> ref, String basePath)StringThe document to draw for this player in place of basePath - their palette's variant of it, or basePath unchanged. Route every commandBuilder.append(...) through it, not just the root document.
document(CultivationPalette palette, String basePath)StringThe same resolution against an already-looked-up palette, for a build that appends many documents. null means the default look and returns basePath as-is.
setPalette(Store<EntityStore> store, Ref<EntityStore> ref, String paletteKey)booleanSets the player's chosen palette and puts their HUD back in it - the whole sequence the settings page's theme dropdown performs. The key is re-validated, not trusted. Returns true if applied; false if they have no settings component, a palette lock applies, or the key names an unregistered palette or one they are not offered.
registerMeditationAura(CultivationMeditationAura aura)Adds a meditation aura players may choose, to the picker and to every meditation tick, breakthrough ritual and advancement ritual that spawns a particle.
unregisterMeditationAura(String auraKey)Removes a previously registered meditation aura - for a plugin unloading cleanly.
getMeditationAuras()List<CultivationMeditationAura>Every registered meditation aura, in registration order. A fresh list, safe to hold.
getMeditationAura(String auraKey)CultivationMeditationAuraThe aura registered under this id, or null.
getMeditationAura(accessor, ref)CultivationMeditationAuraThe aura the player has chosen, or null if they meditate under Cultivation's own particles (also when the stored id names an aura nobody registered).
setMeditationAura(Store<EntityStore> store, Ref<EntityStore> ref, String auraKey)booleanSets the player's chosen meditation aura, re-validating the key first. null or empty returns to the built-in particles. Returns true if applied.
registerOffRingRoot(DaoRoot root)Claims an off-ring DaoElement slot (Cultivation ships one, the joke DUCK), naming it and optionally overriding the counter-cycle bonus. Until claimed, the element stays unselectable.
unregisterOffRingRoot(DaoElement element)Releases a previously claimed off-ring element - for a plugin unloading cleanly. Any cultivator already walking it is not migrated; the element simply becomes unselectable again, same as any other root the server has turned off.
getOffRingRoot(DaoElement element)DaoRootThe root claiming this off-ring element, or null if nothing claims it.
isOffRingRootClaimed(DaoElement element)booleanTrue if a mod currently claims this off-ring element.

Standing modifiers and stat bonuses

registerModifierSource is the hook for anything that changes what a cultivator is - a bloodline, a constitution, a physique. See Registries for the 18 typed channels and how they combine. applyStatBonus is the lower-level tool: a keyed flat bonus on one of the player's engine stats, the same mechanism the skill tree uses.

MethodReturnsDescription
registerModifierSource(String key, CultivationModifierSource source)Registers a standing, per-player modifier over Cultivation's own systems - the hook for anything that changes what a cultivator IS rather than what they do: a bloodline, a constitution, a physique. See CultivationModifierSource for the channels and how they combine.
unregisterModifierSource(String key)Withdraws a modifier source - for a plugin unloading cleanly.
newBonus(String sourceKey, String statKey, float amount, boolean percent)CultivationBonusBuilds one Overview bonus row for CultivationModifierSource.bonuses. sourceKey and statKey are server.lang keys; reuse Cultivation's own stat key where one fits so your row sums with the mod's. Rows disagreeing on percent are never summed.
applyStatBonus(accessor, ref, int statIndex, String key, float amount)Applies a keyed flat bonus to one of the player's engine stats, the mechanism the skill tree uses. Keyed: calling again with the same key replaces that key's contribution, and 0 withdraws it. Not a heal - the pool keeps the same current-to-max fraction across the change.

Replacing the ladder and the wording

The two whole-layer takeovers, covered in full on API Addons. refreshProgression is the call a ProgressionProvider makes whenever it changes a player's numbers behind Cultivation's back.

MethodReturnsDescription
setProgressionProvider(ProgressionProvider provider)Hands the whole realm, stage and Qi ladder to another mod while every other subsystem keeps running on top. Last to register wins and a warning naming both is logged; null hands progression back. See API Addons.
getProgressionProvider()ProgressionProviderThe installed provider, or null when the built-in ladder is in charge.
refreshProgression(accessor, ref)Tells Cultivation that a player's progression numbers changed underneath it: re-applies their max-health modifier, refreshes the HUD and rankings, and re-reads the realm every gate tests. A provider must call it whenever it changes a level.
setTheme(CultivationTheme newTheme)Re-words the mod's entire player-facing vocabulary. Last to register wins; null restores Cultivation's own wording. See API Addons.
getTheme()CultivationThemeThe installed theme, or null when the mod uses its own wording.

Contribute a page to the shared nav bar, an article to the in-game Codex, a tab to the Identity page, or a system to Progressive Reveal. Each is covered with an example under Registries.

MethodReturnsDescription
registerMenuPage(CultivationMenuPage page)Adds a page to the nav bar every Cultivation menu carries. Registering an existing id replaces it - which is also how a mod takes over one of Cultivation's own entries.
unregisterMenuPage(String pageKey)Removes a previously registered page - for a plugin unloading cleanly.
getMenuPages()List<CultivationMenuPage>Every menu page, in the order they appear on the bar: by CultivationMenuPage.getSortOrder(), then registration order among pages that declare the same one.
getMenuPage(String pageKey)CultivationMenuPageThe page registered under this id, or null if nothing claims it.
buildMenuNav(UICommandBuilder commandBuilder, UIEventBuilder eventBuilder, PlayerRef playerRef, String currentPageKey)Draws the shared nav bar into your page's #NavBar placeholder, with your own page's button disabled to mark where the player is.
buildMenuNav(UICommandBuilder commandBuilder, UIEventBuilder eventBuilder, PlayerRef playerRef, String currentPageKey, Store<EntityStore> store, Ref<EntityStore> ref)The same bar, drawn in the viewer's chosen palette. Prefer this one; the overload without store and ref always draws the default crimson and gold.
handleMenuNav(Store<EntityStore> store, Ref<EntityStore> ref, PlayerRef playerRef, String navKey)Handles a nav click by opening whichever page it names. Safe to call with the null your codec field holds when the event was one of your page's own buttons, and with an id the player may not open.
tryHandleMenuNav(Store<EntityStore> store, Ref<EntityStore> ref, PlayerRef playerRef, String navKey)booleanAs handleMenuNav, but reports whether a page was opened. On false from a locking nav binding, answer the click yourself with sendUpdate(null, null, false) or the client stays on its loading state.
openMenuPage(Store<EntityStore> store, Ref<EntityStore> ref, CustomUIPage page)Swaps the player's open custom page for another - what a menu page's onOpen normally does.
registerCodexEntry(CodexEntry entry)Adds an article to the in-game Codex. Write the body against the reader so it can state this server's real numbers. Registering an existing id replaces it.
unregisterCodexEntry(String entryId)Removes a previously registered article - for a plugin unloading cleanly.
getCodexEntries()List<CodexEntry>Every article, ordered by CodexEntry.getSortOrder() and then registration order. Grouping them is the caller's job - see getCodexCategories().
getCodexEntry(String entryId)CodexEntryThe article registered under this id, or null if nothing claims it.
registerCodexCategory(CodexCategory category)Adds a heading to the Codex index. Four ship with the mod (PATH, SELF, WORLD, CRAFT); filing under one of those is usually better than adding a fifth.
unregisterCodexCategory(String categoryId)Removes a Codex heading.
getCodexCategories()List<CodexCategory>Every index heading, in display order. A fresh list, safe to hold.
registerIdentitySection(IdentitySection section)Adds a tab to the Identity page after the built-in Race and Titles tabs. Registering an existing id replaces it.
unregisterIdentitySection(String sectionId)Removes a previously registered section - for a plugin unloading cleanly.
getIdentitySections()List<IdentitySection>Every registered section, ordered by IdentitySection.getSortOrder() and then registration order. Visibility is per-player (IdentitySection.isVisible), so unlike getAdminConfigSections() this does not filter - the Identity page checks each section against the viewer itself.
getIdentitySection(String sectionId)IdentitySectionThe section registered under this id, or null if nothing claims it.
newIdentitySection(String id, Message label, int sortOrder, Consumer<IdentitySectionContext> buildCallback, IdentitySection.ActionHandler actionHandler)IdentitySectionBuilds an IdentitySection from its parts so contributing a tab is one call: an id, a label, a sort order, a build callback that appends your document and binds its actions, and an action handler.
newIdentitySection(String id, Message label, int sortOrder, Consumer<IdentitySectionContext> buildCallback)IdentitySectionAs above, for a section with no interactive content (no action handler).
registerRevealableSystem(RevealableSystem system)Adds a system Progressive Reveal can keep out of the menus and Codex until the player's highest-ever realm, or an unlockedBy or engagedBy predicate, makes it relevant.
unregisterRevealableSystem(String systemId)Removes a previously registered system - for a plugin unloading cleanly, or an addon that never wants it hidden at all.
getRevealableSystems()List<RevealableSystem>Every registered system, ordered by RevealableSystem.getSortOrder() and then registration order. A fresh list, safe to hold.
getRevealableSystem(String systemId)RevealableSystemThe system registered under this id, or null if nothing claims it.
isSystemRevealed(accessor, ref, String systemId)booleanWhether the player currently sees that system in their menus and Codex. false only while Progressive Reveal is hiding it; true for an unregistered id.

Admin menus

Put your own settings and per-player actions into /cultivation admin. See Registries for the five row kinds. PlayerAdminAction is an interface you implement; there is no factory for it.

MethodReturnsDescription
registerAdminConfigSection(AdminConfigSection section)Adds a group of tunable rows to the admin menu's Config tab (and, for admins, to the settings menu). Registering an existing key replaces it - which is how an add-on reorders, hides or replaces one of Cultivation's own sections.
unregisterAdminConfigSection(String sectionKey)Removes a previously registered section - for a plugin unloading cleanly.
getAdminConfigSections()List<AdminConfigSection>Every registered section, Cultivation's own included, ordered by sort order then registration, with anything currently hiding itself left out.
getAdminConfigSection(String sectionKey)AdminConfigSectionThe section registered under this key, or null if nothing claims it.
newAdminConfigSection(String key, String labelKey, String hintKey, int sortOrder, Runnable save, List<AdminConfigField> fields)AdminConfigSectionBuilds a section from its parts - key, label and hint lang keys, sort order, a save callback and the list of rows. See Registries for a worked example.
newAdminConfigField(String key, Message label, DoubleSupplier getter, DoubleConsumer setter)AdminConfigFieldBuilds one editable decimal row from a getter and a setter. Read the config through the supplier rather than capturing the config object: a reload replaces the instance behind the holder, and a captured one would edit a discarded copy.
newAdminIntField(String key, Message label, DoubleSupplier getter, DoubleConsumer setter)AdminConfigFieldAs newAdminConfigField, but rendered as a whole-number field - the right shape for a count, a level cap or a tier, where the two decimal places a normal number row shows are noise.
newAdminBooleanField(String key, Message label, BooleanSupplier getter, Consumer<Boolean> setter)AdminConfigFieldA real checkbox row, so a master on/off switch no longer has to masquerade as a 0/1 number - which is what every boolean in Cultivation's own settings had to do before this existed, and why so many of them were config-file only.
newAdminChoiceField(String key, Message label, Supplier<List<AdminConfigChoice>> choices, Supplier<String> getter, Consumer<String> setter)AdminConfigFieldA dropdown row over a fixed set of options, for anything enum-valued. choices is re-read on every render; the setter is handed the chosen option's id, which arrived from a client - re-resolve it.
newAdminTextField(String key, Message label, Supplier<String> getter, Consumer<String> setter)AdminConfigFieldA free-form text row. Use newAdminChoiceField instead whenever the valid values are a known set - a dropdown cannot be typed wrong.
withTooltip(AdminConfigField field, String tooltip)AdminConfigFieldAdds a one-line explanation to any row. A plain String, so it cannot be translated; put anything that must be readable in every language in the row's label.
adminPageOnly(AdminConfigSection section)AdminConfigSectionWraps a section so it appears on the admin page only, not the settings menu - for a mod contributing several sections of real balance numbers.
registerPlayerAdminAction(PlayerAdminAction action)Adds a dropdown-plus-apply-button row to /cultivation admin's Players tab, targeting the selected player. PlayerAdminAction is an interface you implement. Its key must contain a :; a bad key is rejected with a logged warning.
unregisterPlayerAdminAction(String actionKey)Removes a previously registered action - for a plugin unloading cleanly.
getPlayerAdminActions()List<PlayerAdminAction>Every registered action, ordered by PlayerAdminAction.getSortOrder() and then registration order, with anything currently hiding itself (PlayerAdminAction.isVisible()) left out.

Update, build and compatibility checks

Three independent checks an addon can join with one call each, all riding the same background thread and the server owner's Update-Check-Enabled switch. All fail open. See Registries for the manifest formats.

MethodReturnsDescription
registerUpdateCheck(String modId, String displayName, String currentVersion, String manifestUrl, String downloadUrl)UpdateCheckPuts your mod into Cultivation's update check: the manifest you name is fetched on a background thread, and administrators are told as they join when the installed version is behind the published one. Returns the registration so you can read what the last check found.
registerUpdateCheck(String modId, String displayName, String currentVersion, String manifestUrl)UpdateCheckAs above, naming no download destination - the message then states the versions alone.
unregisterUpdateCheck(String modId)Removes a previously registered mod from the sweep - for a plugin unloading cleanly.
getUpdateChecks()List<UpdateCheck>Every registered mod - Cultivation's own registration included, since that is an ordinary one too - in registration order.
getAvailableUpdates()List<UpdateCheck>Only the registrations currently known to be behind and not suppressed by their manifest's ignore list. Empty until the first sweep lands, a few seconds after startup.
registerBuildCheck(String modId, String displayName, String version, Path jarPath, String manifestUrl)BuildCheckPuts your mod into the build check: your jar's compiled classes are hashed and compared against the digests you published as official. Detection, not protection. The verdict goes to the console and to getBuildStatus(modId).
unregisterBuildCheck(String modId)Removes a previously registered mod from the build check - for a plugin unloading cleanly.
getBuildChecks()List<BuildCheck>Every registered mod's build check, in registration order. A fresh list, safe to hold.
getBuildStatus(String modId)BuildStatusWhat the build check concluded about one registered mod, or BuildStatus.UNKNOWN if nothing is registered under that id.
getBuildStatus()BuildStatusWhat the build check concluded about Cultivation itself.
isOfficialBuild()booleangetBuildStatus() reduced to a boolean. false only on a positive mismatch - a build nobody could check is reported as official - so it cannot tell verified from unknown; ask getBuildStatus() for that.
registerCompatCheck(String modId, String displayName, String version, String manifestUrl, Consumer<CompatCheck> onVerdict)CompatCheckRegisters an addon in the compatibility check: which Cultivation versions its installed version is published as working against. onVerdict runs once, on the check's own daemon thread, when the pairing resolves to anything but UNKNOWN; pass null and poll getCompatStatus instead.
registerCompatCheck(String modId, String displayName, String version, String manifestUrl)CompatCheckAs above, with nothing to run on a verdict.
unregisterCompatCheck(String modId)Removes a previously registered addon from the compatibility check - for a plugin unloading cleanly.
getCompatChecks()List<CompatCheck>Every registered addon's compatibility check, in registration order. A fresh list, safe to hold.
getCompatStatus(String modId)CompatStatusWhat the compatibility check concluded about one registered addon, or CompatStatus.UNKNOWN if nothing is registered under that id - which is also the answer before the first check lands, and the answer on any server that could not reach the network.

Store benefits

Ties a product in the Treasure Pavilion to something that happens in game. See Registries.

MethodReturnsDescription
registerStoreBenefit(StoreBenefit benefit)Adds a purchasable web-store benefit to the entitlement sync. Delivery, caching, the operator's config switches and /cultivation store recheck all come with registration.
unregisterStoreBenefit(String key)Removes a previously registered store benefit - for a plugin unloading cleanly.
getStoreBenefits()List<StoreBenefit>Every registered store benefit, in registration order. A fresh list, safe to hold.
hasStoreBenefit(UUID playerUuid, String productSlug)booleanWhether this player bought this product on the web store, per the last sync. false when the operator disabled the system or that product. Readable from any thread.

Which mods are installed

v0.6.1 See Compatibility for what each of these changes.

MethodReturnsDescription
isEndlessLevelingInstalled()booleanWhether Endless Leveling is installed. When it is, Cultivation hands max health and outgoing damage to EL rather than applying them itself, so the two progressions add rather than multiply - see CultivationConfigs.endlessLeveling() for the switches.
isPlaceholderApiRegistered()booleanWhether Cultivation's %cultivation_...% PlaceholderAPI expansion is registered and answering - PAPI installed AND it accepted the registration.
isMarriageInstalled()booleanWhether the Marriage mod is installed, which is what Partnered Cultivation is gated on. False on a server without it, in which case nothing in CultivationConfigs.partner() has any effect.

Config files

v0.6.1 plugin.siren.API.CultivationConfigs makes 49 of Cultivation's 88 config files (91 counting the three per-race files, reached through race()) readable and writable, so an add-on never has to import the mod's internals to ask what this server is running. Each accessor returns the live Config<T> holder; race(PlayerRace) returns that race's RaceConfig directly.

// Read
boolean daoOn = CultivationConfigs.dao().get().isDaoEnabled();

// Write - persisting is the caller's job, so a batch of edits costs one file write
var holder = CultivationConfigs.spiritVein();
holder.get().setSpiritVeinRegenPerSecond(2.5f);
holder.save();

A value changed without saving is live for this session and lost on restart, which is occasionally what you want. Reads are safe from anywhere. Writes are safe from a plugin's setup() and from any world thread, on the same terms Cultivation's own admin menu writes them: the config objects are plain mutable holders with no cross-thread coordination, so two threads racing on one field is last-writer-wins rather than corrupt.

Hold the holder, not the config

A reload - an admin pressing Save, or the file changing on disk - replaces the instance behind the holder, so a captured DaoConfig field becomes a discarded copy whose edits go nowhere. Write CultivationConfigs.dao().get().isDaoEnabled() at the point of use.

To change a number for one player or one moment instead of for the whole server, use the matching Pre* event (API Events).

AccessorReturnsFileHolds
cultivation()Config<CultivationConfig>Config.jsonThe XP curve and the per-level health and damage bonuses.
spiritCores()Config<SpiritCoreConfig>Cultivation/SpiritCoreConfig.jsonThe three core tiers' drop chances and Qi values.
spiritVein()Config<SpiritVeinConfig>Cultivation/SpiritVeinConfig.jsonVein seeding, regeneration, drain, tiers and Spirit Sense.
breakthrough()Config<BreakthroughConfig>Cultivation/BreakthroughConfig.jsonBreakthrough and advancement rituals, tribulation and the Heart-Devil Trial.
bodyTempering()Config<BodyTemperingConfig>Cultivation/BodyTemperingConfig.jsonThe body-tempering ladder: its curve, how armor blunts it, and the reduction it pays back.
skillTree()Config<SkillTreeConfig>Cultivation/SkillTreeConfig.jsonSkill point grants, the stat caps and respec.
raceSystem()Config<RaceSystemConfig>Cultivation/RaceSystemConfig.jsonCross-race behavior - notably whether an admin's setrace bypasses the realm gate.
race(PlayerRace race)RaceConfigone file per raceOne race's own stat block. Every registered race has one, including races another mod added through registerRace. Returns the RaceConfig itself, not a holder.
dao()Config<DaoConfig>Arts/DaoConfig.jsonElements, drift, Yin-Yang, moral paths, karma and weather resonance.
daoComprehension()Config<DaoComprehensionConfig>Arts/DaoComprehensionConfig.jsonHeavenly Dao (天道), the open Personal Dao registry and Dao Enlightenment (悟道).
technique()Config<TechniqueConfig>Arts/TechniqueConfig.jsonThe technique system's master switches, keybind gating and per-technique rule overrides.
manual()Config<ManualConfig>Arts/ManualConfig.jsonLootable manuals and what they teach.
alchemy()Config<AlchemyConfig>Arts/AlchemyConfig.jsonPills - their effects, durations and charges.
talisman()Config<TalismanConfig>Arts/TalismanConfig.jsonTalismans - the desk ritual, the grade roll and the mastery ladder.
array()Config<ArrayConfig>Society/ArrayConfig.jsonThe Teleportation Array network: master switch, realm gate, per-destination toggles, the Qi cost and cooldown model, and the activation radius.
forging()Config<ForgingConfig>Arts/ForgingConfig.jsonForging - the Forge Anchor ritual, the material ladder and the grade roll.
refinement()Config<RefinementConfig>Arts/RefinementConfig.jsonWeapon refinement and dao affinity tempering.
lifeBound()Config<LifeBoundConfig>Arts/LifeBoundConfig.jsonLife-Bound Treasures - per-item weapon and armor growth.
weaponSpirit()Config<WeaponSpiritConfig>Arts/WeaponSpiritConfig.jsonWeapon Spirits - a Life-Bound Treasure's awakening, level ladder and bonded art.
beast()Config<BeastConfig>Arts/BeastConfig.jsonSpirit beasts - species, taming, growth and the companion roles.
breeding()Config<BreedingConfig>Arts/BreedingConfig.jsonSpirit beast breeding - the pen, the two-cultivator ritual and incubation.
sect()Config<SectConfig>Society/SectConfig.jsonSects - size, halls, bonuses, invites and inscriptions.
formation()Config<FormationConfig>Society/FormationConfig.jsonSpirit arrays - what each formation costs and does.
dwelling()Config<DwellingConfig>Society/DwellingConfig.jsonCave Abodes, Spirit Springs, upkeep and seclusion.
war()Config<WarConfig>Society/WarConfig.jsonSect wars and vein sieges.
duel()Config<DuelConfig>Society/DuelConfig.jsonDuels and their wagers.
alliance()Config<AllianceConfig>Society/AllianceConfig.jsonInter-sect diplomacy: the non-aggression, trade and alliance ladder and the separate unilateral rivalry table.
party()Config<PartyConfig>Society/PartyConfig.jsonThe ad-hoc party system - max size and invite expiry.
dungeon()Config<DungeonConfig>Society/DungeonConfig.jsonThe party dungeon: master switch, entrance gate, party sizing, instance asset and the boss encounter's thresholds and magnitudes.
rival()Config<RivalConfig>Society/RivalConfig.jsonWandering Rival Cultivators - spawn schedule, lifetime, max health and the winner's payout.
treasure()Config<TreasureConfig>Society/TreasureConfig.jsonBuried Caches and Ruin Vaults, discoverable once Spirit Sense reaches its precise stage.
faction()Config<FactionConfig>Society/FactionConfig.jsonThe NPC-faction reputation system - tiers, rivalry pairs and opposing-path gain.
oath()Config<OathConfig>Cultivation/OathConfig.jsonHeavenly Oath offer and accept, and the breach penalty (Qi loss, karma, the Dao-Heart Flaw).
partner()Config<PartnerConfig>Society/PartnerConfig.jsonPartnered Cultivation. Inert unless the Marriage mod is installed - see isMarriageInstalled().
tea()Config<TeaConfig>Cultivation/TeaConfig.jsonThe Tea Ceremony (茶道) - rounds, timing windows, herb cost, harmony bands and the post-ceremony Qi buff.
weiqi()Config<WeiqiConfig>Society/WeiqiConfig.jsonWeiqi (围棋) - the master switch, turn and reconnect timing, and the Merit payout for a finished match.
secretRealm()Config<SecretRealmConfig>Society/SecretRealmConfig.jsonSecret Realms and the ascension-gated Immortal Court (仙庭) tier - schedule, duration, Qi multipliers, beast stocking and manual-drop bonuses, plus each tier's weather.
dreamTrial()Config<DreamTrialConfig>Cultivation/DreamTrialConfig.jsonThe Dream Trial (幻境劫) at the Hollow Mirror - site anchor, entry gating, the composure dials and the Waking Bell charm's charge economy.
campaign()Config<CampaignConfig>Cultivation/CampaignConfig.jsonThe narrative campaign: master switch, each chapter's quest-chain numbers and where each recurring character appears.
market()Config<MarketConfig>Society/MarketConfig.jsonThe player-run Auction House and the scheduled Traveling Merchant NPC.
tide()Config<TideConfig>Society/TideConfig.jsonThe Beast Tide (兽潮): the automatic scheduler, the warning, wave and resolution phases, assault integrity, and the suppression penalty or contribution reward.
masterDisciple()Config<MasterDiscipleConfig>Society/MasterDiscipleConfig.jsonMaster-Disciple bonds (师徒) - offer and accept, the radius-gated Qi bonus, teaching and breakthrough rewards.
qiDeviation()Config<QiDeviationConfig>Cultivation/QiDeviationConfig.jsonQi Deviation (走火入魔) - the lingering post-Heart-Devil affliction, its penalties and cures.
tournament()Config<TournamentConfig>Society/TournamentConfig.jsonThe sect tournament (论道大会) - bracket size, the champion payout and manual or automatic start cadence.
celestial()Config<CelestialConfig>Cultivation/CelestialConfig.jsonCelestial Events (天象) - the shared scheduler, plus Spirit Tide, Meteor Shower, Blood Moon and Starfall Convergence.
pathWar()Config<PathWarConfig>Society/PathWarConfig.jsonPath War (道途之战) - the Righteous vs Devil kill-count contest: scoring, anti-farm, the Heavenly Schism window and title thresholds.
season()Config<SeasonConfig>Society/SeasonConfig.jsonThe Seasonal Cycle: master switch, season length and the Hall of Fame's tuning.
endlessLeveling()Config<EndlessLevelingConfig>Compatibility/EndlessLevelingConfig.jsonHow Cultivation shares the stats it raises with Endless Leveling. Inert unless EL is installed - see isEndlessLevelingInstalled().
update()Config<UpdateConfig>UpdateConfig.jsonWhether the server checks for mod updates, how often, and whether administrators are told on join. Governs every mod registered through registerUpdateCheck.
webStore()Config<WebStoreConfig>WebStoreConfig.jsonThe Treasure Pavilion benefit sync: whether purchases on xianxia.dev are applied here, how often the lists are re-read, and whether the recheck command is available.

saveAll() writes the 46 holders it knows about to disk - rarely what you want, since saving the one holder you edited is cheaper. It does not cover daoComprehension(), secretRealm() or webStore(), and race(...) is not a holder; save those yourself.

The other 39 of the 88 config files have no accessor, so they are not part of the public API: ReincarnationConfig, GrandDaoConfig, MeridianConfig, LifespanConfig, CombatDepthConfig, FistConfig, DivinationConfig, HearthConfig, ForageConfig, SolarTermConfig, HerbScatterConfig, MeritConfig, GuardianConfig, SermonConfig, SoulEscapeConfig, RogueConfig, LandConfig, DaoDuelConfig, DaoDuelTournamentConfig, WagerConfig, LegacyConfig, ReclusiveConfig, RetreatConfig, CompassConfig, RevealConfig, DaoInheritanceConfig, GrandTournamentConfig, DepthsConfig, WorldBossConfig, RiftConfig, HeavenlyRealmConfig, SeaOfConsciousnessConfig, PagodaConfig, BountyConfig, LeaderboardConfig, DaoMasteryConfig, QuestConfig, InnerDemonConfig, CleanseRiteConfig. Read their keys on the Config pages; an addon that needs one has to ask the server owner to edit the file.

Registration index

Every register* method on the facade, what it adds, and where it is explained. 23 of them have a matching unregister* to call from your shutdown(); the other seven - registerRace, registerTechnique, registerQiAbsorptionItemModifier, registerBeastArt, registerMasteryStage, registerSectBuildingType and registerLifeBoundTrait - cannot be withdrawn. Re-registering an id is safe in every case; for a race, technique or beast art it returns the existing entry rather than replacing it, and for most others it replaces it.

MethodRegistersWithdraw withSection
registerRaceA playable race.-Registries
registerTechniqueA performable technique.-Registries
registerTechniqueGate (2 overloads)A runtime condition a technique can require.unregisterTechniqueGateRegistries
registerQiAbsorptionItemModifierA Spirit Vein absorption multiplier for a held item.-Registries
registerAdminConfigSectionA group of tunable rows on the admin menu's Config tab.unregisterAdminConfigSectionRegistries
registerPlayerAdminActionA per-player row on the admin menu's Players tab.unregisterPlayerAdminActionRegistries
registerIdentitySectionA tab on the Identity page, after Race and Titles.unregisterIdentitySectionRegistries
registerMenuPageA button on the nav bar every Cultivation menu carries.unregisterMenuPageRegistries
registerCodexEntryAn article in the in-game Codex.unregisterCodexEntryRegistries
registerCodexCategoryA heading in the Codex index.unregisterCodexCategoryRegistries
registerRevealableSystemA system Progressive Reveal can hide until it becomes relevant.unregisterRevealableSystemRegistries
registerTitleA cosmetic title.unregisterTitleRegistries
registerSectBannerA banner a sect can fly over its hall.unregisterSectBannerRegistries
registerPaletteA menu and HUD palette.unregisterPaletteRegistries
registerPaletteDefaultA rule that picks a palette for players who have not chosen one.unregisterPaletteDefaultRegistries
registerPaletteLockA rule that forces a palette on some players.unregisterPaletteLockRegistries
registerMeditationAuraA meditation, breakthrough and advancement particle look.unregisterMeditationAuraRegistries
registerOffRingRootA claim on an off-ring DaoElement slot.unregisterOffRingRootRegistries
registerPersonalDaoA Personal Dao (a Way comprehended through deeds).unregisterPersonalDaoRegistries
registerBeastArtA companion-side art.-Registries
registerSectBuildingTypeA kind of sect building.-Registries
registerLifeBoundTraitA nature a bound treasure can reveal.-Registries
registerMasteryStageAn extra rung on the technique mastery ladder.-Registries
registerModifierSourceA standing per-player modifier over Cultivation's systems.unregisterModifierSourceRegistries
registerStoreBenefitA web-store product tied to an in-game benefit.unregisterStoreBenefitRegistries
registerTechniquePresetCapA higher cap on keybind layouts per player.unregisterTechniquePresetCapRegistries
registerProfileCapA higher cap on profiles per player.unregisterProfileCapRegistries
registerUpdateCheck (2 overloads)Your mod in the update check.unregisterUpdateCheckRegistries
registerBuildCheckYour mod in the build check.unregisterBuildCheckRegistries
registerCompatCheck (2 overloads)Your addon in the compatibility check.unregisterCompatCheckRegistries

Two further registries are not on the facade: celestial events are registered on CelestialManager.registerEventType, and sect-mission metrics are credited through MissionManager.creditProgress - both are in API Registries.

See also