Cook crafted/smelted food spiraled to absurd saturation values.
rebuildFood was calling FoodProperties.Builder#saturationModifier(float)
with what it thought was an absolute saturation value, but that
setter stores a multiplier — build() then ran it through
FoodConstants.saturationByModifier(nutrition, modifier) which
returns nutrition * modifier * 2.0. Each Cook (and Smoking Expert)
pass fed the previous already-blown-up saturation back through the
multiplier; Mollan reported cooked steak coming out with 2639
saturation. The helper now constructs the FoodProperties record
directly so the saturation parameter remains a literal absolute
value, matching Apoli/Origins semantics. On 26.1 the rebuild also
drops eatSeconds/usingConvertsTo/effects overrides — those
moved out of FoodProperties to the Consumable data component
in 26.1, and leaving the original Consumable untouched on the
stack is the correct preservation path. Resolves GitHub #95.
Voidwalker (and other Route B resource bars) vanished on relog,
blocking abilities that gate on cur > 0. f1c492fe already
fixed onLogin so it no longer reset the stored value back to
start_value — but two residual problems remained:
(1) On 1.21.1 the RESOURCE_STATE attachment was missing
.copyOnDeath(), so any respawn wiped the saved value entirely
(master had it; 2.1 didn't).
(2) onLogin re-registered the resource meta but didn't seed the
state entry when none existed, so syncResourcesToClient
iterates state.getAll() and skipped the bar entirely — symptom
was "energy bar disappeared, abilities won't fire because cur=0".
Added .copyOnDeath() to the 1.21.1 attachment and a state-seed
step in ResourcePower#onLogin that writes start_value only when
no entry is present (existing values are preserved). Resolves
GitHub #90.
tamed_animal_boost granted a permanent attribute modifier that
outlived the source mob. The boost modifier was applied once on
tame and never cleaned up, so once a player tamed enough mobs they
effectively kept the stacked bonus forever — even after every
tamed mob died or despawned. The modifier is now recomputed each
tick against the current owned-mob count and removed when that
count drops to zero.
persistent_effect show_icon / show_particles / ambient
were ignored when set at the power root. Pack authors writing
the convenience top-level form { "type": "neoorigins:persistent_effect", "effect": "...", "show_icon": false } got the HUD icon anyway, because those fields were only
read off each EffectSpec and the root-level values were dropped.
Top-level show_icon, show_particles, and ambient now cascade
as defaults onto every nested EffectSpec that doesn't declare
its own value.
tame_mob couldn't tame non-hostile mobs. The target check
required Enemy, so packs that wanted a generalized "tame any
creature" power (animals, golems, villagers) had no JSON knob.
Added hostile_only (default true, preserving the existing
Monster Tamer feel); set hostile_only: false to allow taming
any non-player Mob. Boss rejection via canUsePortal is
unchanged.
breath_in_fluid's only drain knob (drain_rate) read as
"speed" but was actually "ticks between decrements", confusing
pack authors. Added two more intuitive aliases:
air_loss_per_second (higher = faster drain, internally converted
to 20 / value ticks) and drain_interval_ticks (the literal
meaning of the original field). Resolution priority is
air_loss_per_second > drain_interval_ticks > drain_rate >
default 20. Existing packs using drain_rate keep working
unchanged.
Cook crafted/smelted food spiraled to absurd saturation values.
rebuildFood was calling FoodProperties.Builder#saturationModifier(float)
with what it thought was an absolute saturation value, but that
setter stores a multiplier — build() then ran it through
FoodConstants.saturationByModifier(nutrition, modifier) which
returns nutrition * modifier * 2.0. Each Cook (and Smoking Expert)
pass fed the previous already-blown-up saturation back through the
multiplier; Mollan reported cooked steak coming out with 2639
saturation. The helper now constructs the FoodProperties record
directly so the saturation parameter remains a literal absolute
value, matching Apoli/Origins semantics. On 26.1 the rebuild also
drops eatSeconds/usingConvertsTo/effects overrides — those
moved out of FoodProperties to the Consumable data component
in 26.1, and leaving the original Consumable untouched on the
stack is the correct preservation path. Resolves GitHub #95.
Voidwalker (and other Route B resource bars) vanished on relog,
blocking abilities that gate on cur > 0. f1c492fe already
fixed onLogin so it no longer reset the stored value back to
start_value — but two residual problems remained:
(1) On 1.21.1 the RESOURCE_STATE attachment was missing
.copyOnDeath(), so any respawn wiped the saved value entirely
(master had it; 2.1 didn't).
(2) onLogin re-registered the resource meta but didn't seed the
state entry when none existed, so syncResourcesToClient
iterates state.getAll() and skipped the bar entirely — symptom
was "energy bar disappeared, abilities won't fire because cur=0".
Added .copyOnDeath() to the 1.21.1 attachment and a state-seed
step in ResourcePower#onLogin that writes start_value only when
no entry is present (existing values are preserved). Resolves
GitHub #90.
tamed_animal_boost granted a permanent attribute modifier that
outlived the source mob. The boost modifier was applied once on
tame and never cleaned up, so once a player tamed enough mobs they
effectively kept the stacked bonus forever — even after every
tamed mob died or despawned. The modifier is now recomputed each
tick against the current owned-mob count and removed when that
count drops to zero.
persistent_effect show_icon / show_particles / ambient
were ignored when set at the power root. Pack authors writing
the convenience top-level form { "type": "neoorigins:persistent_effect", "effect": "...", "show_icon": false } got the HUD icon anyway, because those fields were only
read off each EffectSpec and the root-level values were dropped.
Top-level show_icon, show_particles, and ambient now cascade
as defaults onto every nested EffectSpec that doesn't declare
its own value.
tame_mob couldn't tame non-hostile mobs. The target check
required Enemy, so packs that wanted a generalized "tame any
creature" power (animals, golems, villagers) had no JSON knob.
Added hostile_only (default true, preserving the existing
Monster Tamer feel); set hostile_only: false to allow taming
any non-player Mob. Boss rejection via canUsePortal is
unchanged.
breath_in_fluid's only drain knob (drain_rate) read as
"speed" but was actually "ticks between decrements", confusing
pack authors. Added two more intuitive aliases:
air_loss_per_second (higher = faster drain, internally converted
to 20 / value ticks) and drain_interval_ticks (the literal
meaning of the original field). Resolution priority is
air_loss_per_second > drain_interval_ticks > drain_rate >
default 20. Existing packs using drain_rate keep working
unchanged.
origins:exhaust drained hunger ~268× the intended amount. The
legacy compat translator routed origins:exhaust to a Route A food
modifier with op: "set", and ModifyFoodRegistry re-applies that
modifier on every food refill — so each bite of food re-stamped the
exhaustion value instead of ticking it once per interval. Moved to
Route B (parseExhaust): ticks player.causeFoodExhaustion(amount)
on a hashed offset of the configured interval so the cost lands
exactly once per period regardless of food intake. condition block
honored. Accepts both exhaustion and amount for the field name.effect_immunity ids without a namespace silently mismatched the
effect registry. Pack authors writing "wither" (Apoli-style
unqualified id) didn't equal MobEffect's registered
"minecraft:wither", so the immunity check looked up the wrong key
and the player still took the effect. translateEffectImmunity now
routes every id through a canonicalizeEffectId helper that prepends
minecraft: when the namespace is missing.origins:self_action_when_hit ignored bientity_action, cooldown,
and condition. The Route B parser only parsed entity_action,
so packs porting Apoli's bientity_action shape (where the action
needs the attacker reference) silently lost their behavior, and the
cooldown/condition gates were dropped. New
BiEntityAction + BiEntityActionParser covers damage,
add_velocity, apply_mob_effect, set_on_fire, and invert.
parseSelfActionWhenHit now wires cooldown + condition;
CombatPowerEvents publishes the active HitTakenContext to
ActionContextHolder around the onHit dispatch so the bi-entity
lambda can resolve target = source.getEntity() without re-traversing
the event.origins:modify_jump's entity_action was parsed but never fired.
The field was read into a local variable and immediately thrown
away — so Apoli-style jump-velocity boosts, "explode on jump", or
any other configured action silently no-op'd, leaving the power as
a plain attribute modifier. New JumpActionRegistry stores the
parsed action on grant; JumpEventHandler fires it from
LivingJumpEvent (server players only). Cleaned up on logout and
on power revoke.origins:recipe couldn't carry an inline recipe body (1.21.1 only).
Pack authors had to ship a separate data/<ns>/recipe/...json file
even when the recipe was Origins-specific and only used by one
power. parseRecipe now also accepts an inline JSON object in
the recipe field; InlineRecipeRegistry collects pending bodies
during the datapack reload and injects them via
RecipeManager#replaceRecipes on OnDatapackSyncEvent. Caveat:
the resulting recipe is globally craftable — the inline form only
controls recipe-book visibility, not the craft gate. The 26.1 jar
doesn't ship this: RecipeManager on 26.1 stores recipes in an
immutable RecipeMap with no replaceRecipes hook, so inline
bodies log a one-shot warning and are skipped there. String-id
pointers in the recipe field continue to work on both branches.PreventActionPower's SWIM and ELYTRA enforcement was a no-op.
Holder dispatch fires from PlayerLifecycleEvents.onPlayerTick on
the .Pre phase, but vanilla's LivingEntity#travel() and
swim-flag update run after the .Pre tick the same frame and
overwrite anything we reset — so Earth Mage's "can't swim" ability
was setting swimming = false only to have vanilla immediately set
it back to true. Moved SWIM/ELYTRA enforcement out of onTick into
a new PreventActionPostTickHandler subscribed to
PlayerTickEvent.Post, which runs after vanilla physics. FIRE
fire-tick clearing stays on onTick since it's not state vanilla
touches the same tick.shape_type and along-ray actions (1.21.1
only). parseRaycast only supported a point-hit command/
entity_action/block_action triple — packs that wanted to scan
voxel space (e.g. fire-along-ray, glow-trace) had no way to express
it. Added shape_type (collider / visual / outline) to
control the clip mode, plus command_along_ray + command_step
that walks the ray in command_step-block increments executing the
command at each step, breaking on the first command exception.
Master/26.1 doesn't have parseRaycast at all and isn't a hotfix
scope — the along-ray feature will land there as part of the next
raycast backport.origins:exhaust drained hunger ~268× the intended amount. The
legacy compat translator routed origins:exhaust to a Route A food
modifier with op: "set", and ModifyFoodRegistry re-applies that
modifier on every food refill — so each bite of food re-stamped the
exhaustion value instead of ticking it once per interval. Moved to
Route B (parseExhaust): ticks player.causeFoodExhaustion(amount)
on a hashed offset of the configured interval so the cost lands
exactly once per period regardless of food intake. condition block
honored. Accepts both exhaustion and amount for the field name.effect_immunity ids without a namespace silently mismatched the
effect registry. Pack authors writing "wither" (Apoli-style
unqualified id) didn't equal MobEffect's registered
"minecraft:wither", so the immunity check looked up the wrong key
and the player still took the effect. translateEffectImmunity now
routes every id through a canonicalizeEffectId helper that prepends
minecraft: when the namespace is missing.origins:self_action_when_hit ignored bientity_action, cooldown,
and condition. The Route B parser only parsed entity_action,
so packs porting Apoli's bientity_action shape (where the action
needs the attacker reference) silently lost their behavior, and the
cooldown/condition gates were dropped. New
BiEntityAction + BiEntityActionParser covers damage,
add_velocity, apply_mob_effect, set_on_fire, and invert.
parseSelfActionWhenHit now wires cooldown + condition;
CombatPowerEvents publishes the active HitTakenContext to
ActionContextHolder around the onHit dispatch so the bi-entity
lambda can resolve target = source.getEntity() without re-traversing
the event.origins:modify_jump's entity_action was parsed but never fired.
The field was read into a local variable and immediately thrown
away — so Apoli-style jump-velocity boosts, "explode on jump", or
any other configured action silently no-op'd, leaving the power as
a plain attribute modifier. New JumpActionRegistry stores the
parsed action on grant; JumpEventHandler fires it from
LivingJumpEvent (server players only). Cleaned up on logout and
on power revoke.origins:recipe couldn't carry an inline recipe body (1.21.1 only).
Pack authors had to ship a separate data/<ns>/recipe/...json file
even when the recipe was Origins-specific and only used by one
power. parseRecipe now also accepts an inline JSON object in
the recipe field; InlineRecipeRegistry collects pending bodies
during the datapack reload and injects them via
RecipeManager#replaceRecipes on OnDatapackSyncEvent. Caveat:
the resulting recipe is globally craftable — the inline form only
controls recipe-book visibility, not the craft gate. The 26.1 jar
doesn't ship this: RecipeManager on 26.1 stores recipes in an
immutable RecipeMap with no replaceRecipes hook, so inline
bodies log a one-shot warning and are skipped there. String-id
pointers in the recipe field continue to work on both branches.PreventActionPower's SWIM and ELYTRA enforcement was a no-op.
Holder dispatch fires from PlayerLifecycleEvents.onPlayerTick on
the .Pre phase, but vanilla's LivingEntity#travel() and
swim-flag update run after the .Pre tick the same frame and
overwrite anything we reset — so Earth Mage's "can't swim" ability
was setting swimming = false only to have vanilla immediately set
it back to true. Moved SWIM/ELYTRA enforcement out of onTick into
a new PreventActionPostTickHandler subscribed to
PlayerTickEvent.Post, which runs after vanilla physics. FIRE
fire-tick clearing stays on onTick since it's not state vanilla
touches the same tick.shape_type and along-ray actions (1.21.1
only). parseRaycast only supported a point-hit command/
entity_action/block_action triple — packs that wanted to scan
voxel space (e.g. fire-along-ray, glow-trace) had no way to express
it. Added shape_type (collider / visual / outline) to
control the clip mode, plus command_along_ray + command_step
that walks the ray in command_step-block increments executing the
command at each step, breaking on the first command exception.
Master/26.1 doesn't have parseRaycast at all and isn't a hotfix
scope — the along-ray feature will land there as part of the next
raycast backport.Built for Minecraft 26.1 / 26.1.1 / 26.1.2 and 1.21.1 with NeoForge.
origins:set_resource action was a silent no-op. The legacy
compat layer rewrote origins:set_resource to
neoorigins:set_resource but the action dispatcher had no arm
for it, so packs porting from Origins++ saw the action listed
as "unknown" with no effect. Wired the dispatch arm to read
resource + value (or fallback change) and write through
CompatAttachments.resourceState(). Same commit aliases
xp_levels to the existing xp_level condition (Apoli's
plural form) and adds a new saturation_level entity condition
reading FoodData.getSaturationLevel().origins:flame_particles failed codec validation. Route B's
WELL_KNOWN synth emitted a fieldless neoorigins:particle stub
and ParticlePower.CODEC rejected the entry with
particle: missing or unknown 'particle' field, dropping the
whole power. Synth now passes minecraft:flame as the default
particle id so legacy flame_particles references load.forge: attribute ids resolved to nothing on
NeoForge. Packs authored against Forge-era Origins++ pinned
attributes like forge:generic.entity_reach, which NeoForge
re-registered under neoforge:entity_reach.
AttributeModifierPower.resolveAttribute now retries any
unresolved forge:* id under the neoforge: namespace with
generic. / player. prefix permutations, so existing packs
don't need to be rewritten.name field. OriginInfoScreen.getLayerDisplayName
and OriginEditorScreen.drawLayerLabels both fell back to the
translation key / capitalized path even when the layer had a
populated name. Both screens now read layer.name() first,
only falling back to the translation key / prettified path if
blank.forEachOfType, which doesn't consult
AbstractTogglePower.isToggledOff — turning the power off in
the HUD still suppressed mob spawns. New
forEachOfTypeActive helper on ActiveOriginService gates
toggleable powers; event handlers reading toggle-style powers
must use it. (Only onTick honors the toggle automatically.)MobCategory.MONSTER 24-block player-distance
spawn rule — so monsters were already blocked by vanilla
and the power did nothing for the case authors actually
wanted. Stoneguard's JSON now sets radius 36 explicitly; the
code default stays at 24 to force pack authors to opt in to
a meaningful radius.longer_potions didn't extend the first dose.
MobEffectEvent.Added fires before vanilla
LivingEntity#addEffect inserts the instance into
activeEffects. The old handler re-called sp.addEffect(extended)
from inside the event; the nested put ran first, then the
outer vanilla put overwrote with the original un-extended
instance. Net result: first dose looked normal, subsequent
doses extended correctly via the merge path. Now mutates the
incoming MobEffectInstance via update() so vanilla's
subsequent put sees the already-extended values.eatSeconds, consume effects, and
usingConvertsTo (1.21.1 only). The bonus rebuilt
FoodProperties through FoodProperties.Builder, which only
exposes nutrition / saturation / canAlwaysEat — so suspicious
stew lost its random potion effect when boosted, golden apple
lost regen, bowls/bottles no longer returned an empty container.
New rebuildFood() helper goes through the builder for the
bonus fields then constructs the record directly to copy the
remaining fields through unchanged. Wayfarer's smoker bonus
(MoreSmokerXpPower) gets the same treatment, and re-reads
the FOOD component before rebuilding so it stacks cleanly on
top of Cook's earlier rebuild when a player has both. The bug
is 1.21.1-specific: 26.1's FoodProperties record no longer
carries eatSeconds / usingConvertsTo / effects (Mojang
moved them to DataComponents.CONSUMABLE and
DataComponents.USE_REMAINDER), so the boost path never
touches them and the original symptom can't reproduce.MobsIgnorePlayer had no way to disable retaliation. By
default the power lets vanilla's getLastHurtByMob() window
apply — once the player hits an "ignoring" mob, the mob is
allowed to target back briefly so combat feedback loops still
work. Pack authors who wanted true peace (Bonewalker, druidic
neutrality, etc.) had no knob to turn that off. New
passive: true config flag bypasses the retaliation window
so the mob never targets the player even if attacked first.
Default false keeps existing pack behavior intact.[CompatB] parser warnings deduped per reload. Pack-port
sessions were dumping hundreds of WARN lines during datapack
load — one per occurrence of every unsupported
action/condition/modifier op. One porting log had 268 such
lines (22% of bootup output) that collapsed into 6 unique
action types + 4 unique condition types. New
CompatWarningCollector batches parser-side warnings
(unsupported actions / conditions / item-actions /
item-conditions, modifier defaults, parse errors, malformed
SNBT, per-power compile failures) during a session held open
by OriginsCompatPowerLoader.apply(), then emits one sorted
summary block at the end. Outside a session each record*
call falls back to immediate LOGGER.warn. Wrapped in
try/finally so a mid-reload throw can't leak the session.skeleton_apex_hp,
skeleton_rattling, merling_no_drowning) with no power JSON
or origin reference, and broadened the stamina power
description across all 10 pools from "Physical energy for
powerful attacks." to "Physical reserves spent on active
abilities." (The old line lied for Stoneguard (utility-only)
and Shulk (CC + mobility) and was misleadingly narrow for the
rest.)EdibleItemPower configs for stone, iron,
gold, diamond, netherite, and bone meal shipped with
always_edible: true, so the hunger check at
InteractionPowerEvents.java:115 short-circuited and the
right-click animation fired even at a full food bar — players
could spam-eat the mineral indefinitely. Flipped to false on
all six. The codec default in EdibleItemPower.java stays
true so external packs that omit the field keep their
current behavior.caveborn_*_bonus powers listened on
event: item_use_finish, but food_item_in_tag reads the
dispatch context as a FoodContext. Per
InteractionPowerEvents.java:172–177, ITEM_USE_FINISH
dispatches the raw ItemStack; only FOOD_FINISHED wraps it
in FoodContext. The cast failed silently so the if_else
action took the noop branch and no status indicator appeared.
Switched all 4 to food_finished, matching the working
aquatic_fish_diet_bonus template.docs/ORIGINS.md — per-origin reference covering every
default origin and its evolution tree. Each entry has impact
rating, icon, spawn behavior (where applicable), base-power
list with one-line glosses, and a tier-delta table for the
evolution path. Catalogues all 49 non-class origins.docs/POWER_TYPES.md and field_docs.json aligned with the
new no_mob_spawns_nearby 24-block code default. The old
docs advertised 48 and used a 48-block example; now reflect
the 24 default, call out the vanilla 24-block cutoff, and use
a 36-block example matching Stoneguard's own JSON.MobsIgnorePlayer field docs updated to add the passive row
#tag syntax with passive, and to
clarify that entity_types already accepts #tag references
and that an empty list matches every mob (both were true
before but undocumented).Download the JAR matching your Minecraft version and drop it into your mods/ folder.
neoorigins-2.1.3+26.1.jar — for MC 26.1, 26.1.1, and 26.1.2neoorigins-2.1.1+1.21.1.jar — for MC 1.21.1Drop Origins mod content packs (.jar, .zip, or folder) into the originpacks/ folder in your game directory.