FAQ
How do I update SnCompanions?
Download the newer sncompanions-v* release and replace the jar. config.yml, the language files
and the menu layouts auto-merge on restart, so new keys appear while your values and comments
stay. Your companions/ folder and your eggs.yml file are
never overwritten.
A player unequipped the companion in slot 2 and the others moved. Is that a bug?
No, that is by design. Equipped companions are kept in slots 1..n with no gap, so the free
slots are always the last ones. The order of the companions is unchanged and the formation around the
player looks exactly the same; only the slot numbers shift. An unequip performed while the
player is OFFLINE leaves the gap until they next log in, where it is closed automatically.
How do I colour a companion's menu lines by its group?
Give the group a color: in config.yml and use {group-color} in the menu templates. See
Configuration for the full recipe. Existing configs do not receive the new
key: until you add one, {group-color} reuses the colour codes that group's display already
starts with.
My egg has [rgb] in its display-name and chat shows the tag instead of the gradient.
It does not any more. [rgb] is a prefix tag: SnLib reads it at the START of a finished line, and an
egg or companion name spliced into a message as {egg} or {companion} sits in the middle of one, so a
raw tag would be left as text (The [rgb]Basic Egg gave you...) while a line whose name IS
the whole line rendered fine. Both eggs.yml and companions/<id>.yml have their display-name
tags applied when the file is read, so the name renders the same in the menu and in chat. Nothing
to change on your side - those files are seed-only and are not touched.
My empty bulk delete buttons are grey glass instead of the group icon. Why?
Because the merge adds missing keys but never overwrites a value your file already carries, and
that material is a value. Open guis/bulk_delete.yml, set templates.group-empty.material to
"{icon}", and restart. Deleting the file and restarting reseeds the whole thing instead. A fresh
install ships "{icon}" already.
Can I run an admin command without telling anyone?
Yes. Add -s to skip the message the target player would get, -sf to skip your own confirmation,
or both, in any order:
/companions admin give Bob ember_fox 3 5 -s -sf
/companions admin openegg Bob basic_egg 10 -s
/companions admin clear Bob all -sf-sf only hides confirmations of things that WORKED. If the command is refused - a full storage,
a companion that does not exist, an unknown egg - or if a query fails, you are told regardless. Silence
under -sf therefore means "it worked", never "something went wrong and you missed it".
Put the flags at the END of the line. Anything typed after the first flag is ignored, so
/companions admin give Bob ember_fox -s 5 gives one companion rather than five.
Is the receiver told when I give them a companion?
Yes. /companions admin give sends the receiver a line of their own,
messages.companion-received, so you can restyle or blank it like any other message and suppress it with
-s. Offline players are never messaged. /companions admin openegg is different on purpose: what
the player reads is the egg's own reward line, announced by the egg engine exactly as it would be
for a bought egg, so -s does not touch it.
What does formation.shape: LINE do, and how do I get the arc back?
LINE is what the plugin ships. Every equipped companion stands on ONE straight line
perpendicular to the owner's facing, formation.line.distance blocks behind them
(1.0 shipped), with formation.line.spacing blocks between neighbours (0.8 shipped), ordered
by equip slot from the owner's RIGHT. A lone companion stands dead centre behind the owner, and
the row is always centred on the owner, so equipping one more slides the whole row half a spacing
across instead of growing it out to one side. The arc keys - radius, back-radius,
arc-degrees, arc-center-offset - are ignored under LINE.
For the arc, set formation.shape to CIRCLE (every companion formation.radius blocks out) or
OVAL (radius at the owner's sides, back-radius straight behind, so the arc hugs their back)
and run /companions reload. Those four arc keys are still in your file and still mean what they
always did.
Two behaviours are worth knowing before you pick the oval. The companions take an even share of
the angle, which is an even spacing on the ground only on a circle - on an oval the ones near the
ends bunch up (about 23% at four companions, 28% at six; one, two and three companions are unaffected). And
formation.facing: OUTWARD / CENTER point along the circle's radius rather than the oval's true
normal, off by up to 10 degrees; that costs nothing under the default OWNER_YAW, which ignores
the angle entirely.
An unknown value is not fatal: the plugin logs one warning per load and falls back to CIRCLE.
An absent value falls back to CIRCLE silently - that is deliberate, and it is what leaves a
config you commented the key out of on the arc rather than dragging it onto whatever the current
default happens to be. So deleting the key gives you CIRCLE, not the shipped LINE.
My companions float in an arc although LINE is what the plugin ships. Why?
Because that is deliberate. config.yml is managed, which means SnLib inserts missing KEYS into
your existing file but never overwrites a VALUE you already had. formation.line.spacing and
formation.line.distance arrive on the next boot because they are keys your file lacks, but a
shape, a height-offset and a bounce.height your file already carries are values, and values
are yours. Set the three by hand and run /companions reload:
formation:
shape: LINE
height-offset: 0.08
animation:
bounce:
height: 0.0height-offset is what puts them on the ground rather than at head height. It is the blocks above
the owner's FEET the companion origin sits at, and a player head drawn as a ground item hangs 1/16
of its scale below its origin - so the number you want is 0.0625 x animation.head-size, which is
0.08 at the shipped head-size: 1.3. Scale the head up and scale this with it: head-size: 4.0
gives a companion about one block tall and wants height-offset: 0.25.
One to watch: models.height-offset is SUMMED with formation.height-offset and moves only the
companions drawn as models. A model's own origin is at its feet, so at the shipped values 0.0
leaves it standing 0.08 above the floor, and -0.08 - the negative of whatever
formation.height-offset is on your server - puts its feet exactly on the ground.
Can I announce only SOME fusions?
Yes. config.yml's fusion.broadcast is only the DEFAULT. Any companion can override
it in its own companions/<id>.yml:
fusion:
into: gale_sprite
chance: 35.0
cost: 25000
broadcast: trueThe flag belongs to the PARENT - the companion being consumed, the one whose file declares into - so
you announce a fusion by writing the key on the companion players fuse AWAY, not on the one they get.
Leave the key out and that companion follows the global setting. companions/ is seed-only, so a companion file
you already have never receives the key on its own: it keeps following fusion.broadcast until
you write it yourself.
On a fresh install the two shipped companion files already set it - stone_golem.yml has
broadcast: true and ember_fox.yml has broadcast: false - and seed-only means they stay that
way. So turning the global on will not make Ember Fox announce; edit or delete the key in its file
for that. A value that is present but not a boolean (broadcast: 1, where yes and on would
have worked) reads as false and logs a warning naming the file.
Fuse All never announces, whatever any companion file says. It rolls per pair and would otherwise post one line for every winning pair.
How do players trade companions with each other?
By turning the companion into an item. In the main menu, shift + right click a companion in the storage grid - or press Q over it, the drop key, Ctrl+Q included: it leaves the storage and becomes a player head in the player's inventory, wearing that companion's own texture and carrying its whole state - companion type, level and experience. A right click with that head puts the companion into the clicker's storage, with everything it had. Drop it, put it in a chest, or sell it on a shop plugin: the head is an ordinary item.
One limit to know before you build an economy on it: the head remembers WHO took it out, and only that player can redeem it - see Can somebody else redeem my companion item? below.
Nothing is destroyed by a refusal. A redeem into a full storage, into a profile that is still
loading, in creative mode while allow-creative is off, or with the feature switched off hands
the item straight back with its state intact. Taking a companion out is refused - and the companion is left
exactly where it was - when it is EQUIPPED (unequip it first), when its companions/<id>.yml is gone,
or when the player has no free inventory slot. A companion is never dropped on the floor to make the
click work.
The head is not placeable, so clicking a block with it can never place it and destroy the companion on
it, and companion-items.blocked-blocks makes the redeem click step aside - clicking a chest opens the chest.
Both triggers run the same action with the same refusals. companion-items.enabled: false switches the
whole feature off, both triggers at once, and leaves the heads already in circulation redeemable.
Since 1.11.0 all of this ships OFF. companion-items.enabled defaults to false and the shipped
guis/main.yml no longer declares the two click lists at all, so on a fresh install a companion
cannot leave the storage as an item. Turning it back on is two steps: set the switch, and put
shift-right-click-actions and drop-click-actions back on the companion-entry template, both
running "[companions-extract] {instance}". The commented block left in that file spells it out.
Because the shipped file no longer declares them, deleting those lists now HOLDS - the merge has nothing to put back. That is the opposite of how it behaved before 1.11.0.
A server upgrading from 1.10.0 keeps its own config.yml and guis/main.yml: values on disk are
preserved and never pruned, so the feature stays on there until you switch it off yourself.
Can somebody else redeem my companion item?
No. Extracting a companion stamps
the head with your UUID and your name; a player who right clicks a head that is not theirs is
refused with messages.companion-item-not-yours, which names the owner, and the head goes back into
their inventory untouched. Nothing is destroyed and nothing is consumed.
The UUID is what the lock matches on, never the name. Changing your nick does not cost you your companions, and a player who takes your old nick does not gain them.
A head that carries no owner tag at all stays redeemable by anyone. Nothing on the server records who took such an item out, so there is no owner to restore - it keeps the behaviour it was traded under.
There is no config switch for this: an item with no owner tag is free and an item with one is locked, which is the whole rule. Redeeming your own head always works, and the companion it creates belongs to whoever redeemed it - so a player who redeems a head and later takes the same companion out again is stamped as its owner in their turn.
This deliberately narrows item trading. The receiver of a locked head holds an item they cannot
redeem. The only way to move a locked companion to another owner is
/companions admin setowner <player> <instance> <target>, which is an admin action
by design - a lock a player could lift themselves would not be a lock.
How does somebody claim a companion item that is not theirs?
They do not - a player cannot lift the lock themselves. An admin moves the companion with
/companions admin setowner <player> <instance> <target>, which transfers the companion to another
player outright. There is no player-facing path, on purpose: a lock the holder of the item could
open would not be a lock.
A head that carries no owner tag at all is not locked, and stays redeemable by whoever holds it.
My companion cells do not mention shift + right click or Q. Is it working?
It is. guis/main.yml is managed, so your install receives the
templates.companion-entry.shift-right-click-actions and drop-click-actions keys and
both triggers work immediately. What it does NOT receive are the lore lines that advertise them,
because those are part of a lore LIST you already have and the merge never rewrites your own list
values. Add them by hand:
- ""
- "&e&lSHIFT + RIGHT CLICK"
- "&e&lQ (DROP)"
- "&6Take this companion out as an item you can trade"or delete the whole lore: list under templates.companion-entry and let the next boot write the
shipped one back.
How do I put a name above every companion?
It is built in. config.yml has a holograms band controlling how the text LOOKS -
height-offset, line-spacing, scale, background (ARGB hex, empty for none), shadow,
see-through and line-width - plus default-lines, the lines used by any companion that does not name
its own. What each companion SAYS is in its companions/<id>.yml:
hologram:
enabled: true
lines:
- "{group-color}{companion}"
- "&7Lv. &f{level}&7/&f{level-cap}"
- "&8{owner}"
# height-offset: 0.9You can use every placeholder a companion cell of any menu uses - {companion}, {level}, {level-cap},
{exp}, {exp-next}, {percent}, {group}, {group-color}, {buff},
{buff-value} - plus {owner}. PlaceholderAPI tokens work too and resolve against the companion's
OWNER, not against whoever is reading, because one label is shown to everyone who can see the companion.
Note that companions/ is seeded once and never merged again, so your existing companion files will NOT
receive a hologram: block. They use holograms.default-lines instead until you write one. Only
an explicit enabled: false silences a companion.
Every companion has text over it. How do I turn that off?
config.yml is managed, so a server that lacks the holograms band receives the whole of it on
the next boot, with enabled: true and a two-line default. Set holograms.enabled: false and run
/companions reload to go back to bare companions, or replace holograms.default-lines with [] to keep the
feature available for the companions that declare their own lines while every other companion stays silent.
My model companions have the text inside them. Why?
Because the offset is measured from the companion, and "the companion" is not the same object in both cases. A
head companion's label rides the head itself, which already sits formation.height-offset above the
owner's feet. A model companion's label rides the invisible carrier its bones ride, which also carries
models.height-offset - so on a server that pushed models down to stand on the ground
(models.height-offset: -0.08), a label at 0.9 lands 0.9 blocks above the model's FEET.
Give those companions a bigger hologram.height-offset of their own. The shipped stone_golem.yml does
exactly that, with 1.2.
Why is there no DecentHolograms option? I already run it.
Because a DecentHolograms hologram cannot ride the companion, and riding the companion is the whole point. It
is a server-side hologram with its own tick and its own teleports, per line and per viewer: a
second stream of position updates on a second clock, which is exactly the drift that makes a
follower hologram look wrong when the server hitches. SnLib's own HologramUtil cannot be used
either - it has no move call at all and does not expose the entity id, so following a companion would
mean deleting and respawning a real entity several times a second.
What SnCompanions sends instead is a client-side text entity MOUNTED on the companion. The client positions it from the companion on every one of its own ticks, and the plugin never sends a single position packet for a label. Nothing has to be installed and nothing can fall behind.
One deliberate limit comes with that: the text does not bob along with a head companion's bounce (the bounce is a rendering transformation, which passengers do not inherit). The DecentHolograms LOOK is not a limit though - see the next question.
The text above my companions leans towards me and it looks bad. Can it stay straight?
Yes, and it does by default. Labels are drawn with the vertical billboard: they turn
only around the vertical axis, so the lines stay upright however far above or below them you stand,
which is the DecentHolograms look.
config.yml is managed, so holograms.billboard: vertical arrives on the
next boot of a server whose file lacks the key. If you want the stack tilted towards the camera on
both axes, set that key to center and everything else stays as it is.
horizontal and fixed are also accepted; neither reads well on a name plate. The setting is
global, with no per-companion override, so a server has one label aesthetic rather than a mix.
Does it support Folia?
No, SnCompanions is not Folia-compatible. Run it on Paper 1.20.4 or newer. Both the 1.20 and 1.21 lines are supported.
Do companions lag the server with many players online?
No. Companions are packets, not entities, so they never enter the entity table, never tick physics and never get saved to the region files. The whole server shares one animation tick rather than one task per companion or per owner, so adding players adds work linearly, not a scheduler task each. An unequipped companion costs nothing at all.
Do I need BetterModel?
No. Without it every companion renders as a player head, which is the built-in default. Install it only if you want a companion type to render as an animated model with separate idle and moving animations.
A player says their companions vanished. What happened?
Check /companions toggle first: it hides the player's own companions and is the usual answer. If other
players cannot see them either, check /companions hide on the viewer's side, then the worlds
section in config.yml, which can disable rendering in a named world. If they vanished on a
world change specifically, see the next question. If it is only your BEDROCK players who see
nothing, the cause is a different one - see
My Bedrock players cannot see their companions.
My companions go invisible when I change world, and a second world change brings them back
Fixed in 1.11.1. Update, and there is nothing to configure.
Before that release, changing world (and respawning) re-sent the companions at the position they were last drawn at, which is a point in the world you had just left. When the two worlds put you at very different coordinates, the companions were created thousands of blocks away, in ground your client had never loaded, so it never showed them and never mounted a model companion's parts onto the invisible carrier they ride. They stayed gone for the rest of the session.
That is also why it looked like one particular world was broken rather than the hop itself. On a
jump whose two worlds happen to drop you at similar coordinates, the stale spawn landed right on
top of you and nothing looked wrong, which made the same journey work or fail depending only on
where each world puts you. From 1.11.1 the formation is rebuilt where you land, the way
/companions reload already rebuilt it, so every world change and every respawn is clean. It
applies to companions drawn as player heads too, not only to BetterModel ones.
Why does a player receive no buff in one world?
Buffs have a per-world gate in config.yml. In a gated world the placeholder reports zero
because the player genuinely receives nothing there. That is configuration, not a bug.
Can two players see each other's companions?
Yes, unless the viewer ran /companions hide or the world gates rendering off. Each viewer's own
preference is respected, so one player hiding companions never affects anyone else's view.
My Bedrock players cannot see their companions
Install Floodgate next to Geyser and update to 1.12.0. There is nothing to switch on: the
bedrock band of config.yml ships with enabled: true, and config.yml is managed, so a server
whose file lacks the band receives the whole of it on the next boot.
What those players were seeing is the whole diagnosis. Everything this plugin draws a companion's BODY with is a Display entity - a head companion is an item display, and a model companion's bones ride one too - and Geyser has no Bedrock definition for an item or block display at all, so it discards the spawn in silence. Text displays it DOES translate, which is why a Bedrock player was left with the companion's NAME hanging frozen in mid-air over an empty spot: the label lines were arriving the whole time, riding a vehicle that never came.
From 1.12.0 a viewer on Bedrock is sent something else instead - a fake, client-side baby zombie wearing that companion's own head in the helmet slot plus leather armour, moved by the exact same arithmetic as the real companion and carrying the name plate. It is a baby on purpose: about half height, with the oversized head that makes the worn companion head legible at that size. Java viewers receive byte for byte what they received before and see no change whatsoever.
Two knobs are then yours, if the substitutes do not look right on your server:
bedrock.height-offset, if the zombie sits at the wrong height. It ships at0.0, which is right for a VISIBLE zombie: this plugin draws companions at the owner's FEET, so at0.0the zombie STANDS on the ground where the companion is, with its head naturally above it. About-0.8instead makes the zombie's HEAD land exactly where a Java player sees the floating head - but with a visible body that sinks it into the floor, so only do that together withbedrock.invisible: true. That switch shipsfalse, and it comes with a warning of its own: some Bedrock versions hide an invisible entity's ARMOUR along with its body, which would hide the very head the substitute exists to show. Check it on a test account before you keep it.bedrock.scale, if they come out the wrong size. It ships at1.0and MULTIPLIES the companion's ownmodel.scalefromcompanions/<id>.yml, so on a server whose companion files declare a scale below1.0the shipped1.0renders small - raise this one value and the whole collection keeps its relative sizes. Clients below 1.20.5 ignore it entirely, because the attribute it rides does not exist there yet, andbaby: truealready halves the zombie on its own.
Without Floodgate every player counts as a Java client and the whole band does nothing. With
bedrock.enabled: false the substitute is never sent either, which on a Geyser server means those
players go back to seeing nothing at all.
Installing or removing Floodgate on a running server needs no restart either way: the plugin re-asks every online player and rebuilds every formation one tick later, so the substitutes appear or disappear on their own.
Leave bedrock.mount-label: true alone. Turning it off does NOT remove the name plate: the
label lines are sent to every viewer before the companion is, so off only stops them being attached
to anything and they stay frozen in the air at the spot the companion was built - the exact bug this
feature exists to fix. The switch is there only to prove whether a broken Geyser version is
mishandling the mount itself.
This is new in 1.12.0 and has NOT been verified on a live SnCompanions server yet. It is a port of the same change in SnPets, where it IS confirmed working on Bedrock, and the two plugins share the renderer, the tracker and the formation code line for line - but check it on a test account before you announce it. Nothing a Java player sees can change either way.
Can each companion's Bedrock substitute wear a colour that matches its head?
Yes, since 1.13.0. Give the companion file its own leather colour:
# companions/<id>.yml
bedrock:
armor-color: "DD7430"It overrides bedrock.armor.color from config.yml for that companion alone. Write it as RRGGBB hex and
quote it - an unquoted 123456 is a number to YAML and is rejected with a warning. Only leather takes a
colour, so keep bedrock.armor.chestplate, leggings and boots on LEATHER_*. A good starting point is
the most common colour of the head itself, a little brighter than it looks, because dyed leather renders
darker than a flat swatch. Companions in one family that share a head should share the colour too. Whether
the colour survives Geyser's translation to Bedrock is not verified yet; if it does not, the substitute
simply wears plain leather.
My Bedrock players see their companions, but the companions never turn and they bump into them
All of that is expected, and it is the honest cost of the substitute. A Display entity is a rendering object and a zombie is a living entity, so here is the whole of what a Bedrock viewer gets and what they do not:
- It does not TURN. Facing is applied to a head as a Display transform quaternion, and a living
entity has no equivalent, so on Bedrock the zombie always faces one direction while it moves. This
ALWAYS applies and there is nothing to switch off:
formation.facingisOWNER_YAW,OUTWARDorCENTERand has no "off" value.config.ymlshipsOWNER_YAW, and a value the plugin does not know warns once in the console and falls back toOWNER_YAWanyway. - It does not BOUNCE, for the same reason. Moot at the shipped
animation.bounce.height: 0.0. - It is never the MODEL. The substitute's helmet is always built from that companion's
head-texture, whichever backend the companion uses, so a BetterModel companion reaches a Bedrock viewer as its HEAD and never as the animated model. The consequence is worth acting on before it bites you: a companion that declares a model and NOhead-texturedresses its substitute in the DEFAULT head, so on a server with Bedrock players give every companion ahead-textureeven if it renders as a model. All three shipped companions already do. - It DOES have a hitbox, the one item here that is something the substitute gains rather than
loses. A Display has no collision, a living entity does, and the Java protocol
carries no per-entity "no collision" flag to turn it off -
bedrock.invisible: truedoes not help either, the box stays. Bedrock also does its own client-side entity-push prediction, so a Bedrock player may well find their companions nudging them. Keeping it a baby halves the box, and the formation already stands the companions behind their owner. This is the first thing to watch on your own server.
One more thing on a model server, and it is a cost rather than a symptom: a model companion whose ONLY viewers are on Bedrock still holds a live BetterModel engine handle that nobody is being shown. The wrapped renderer has to keep being called for its tracked position to keep advancing - otherwise a Java viewer arriving later would be sent the companion at a spot it left long ago - so the handle is kept on purpose. Accepted cost, not a bug.
One gap comes with it: the egg hatch show is not translated for Bedrock players, and what they get is more confusing than nothing at all. The show never goes through the Bedrock substitute path, so its reveal is spawned for EVERY viewer exactly as it always was - and the Dragon Egg and the revealed head are Display entities, which Geyser discards, while the reveal's name line is a text display, which it delivers. So a Bedrock buyer is shown the reveal's floating NAME with nothing underneath it: the same orphan-label symptom this release removes everywhere else. The sounds and the break particles are not Display entities, so they are expected to arrive. The companions they won are granted and saved exactly as always. That is a known remaining gap, deliberately left out of scope for 1.12.0.
A player has more companions stored than their capacity allows. How?
Not from opening eggs: each open holds the places it was granted for as long as its companions are in flight, so a simultaneous open sees them as already taken and the one that does not fit is refused outright and costs nothing. A storage genuinely over its limit got there another way - an admin grant, or a capacity you lowered afterwards.
Nothing is ever deleted to make the numbers agree. Nothing new enters until the player is back under their limit, which is the same rule that applies after unequipping a companion into a full storage.
A refusal quotes a number above the capacity only when it is real - (107/54) on a storage left
over its limit. Places held for companions still being written can raise the quoted number up to
the capacity and no further, so a spammed open reads (54/54) rather than counting a companion
twice.
How do I give a rank more companion slots or storage?
Grant sncompanions.slots.<n> or sncompanions.storage.<n> in your permissions plugin. The highest value
a player holds wins, so stacking nodes across ranks is safe. The value is read on join, so a
rank change applies the next time the player logs in. The permission is the FLOOR:
it replaces the config base when higher, and everything sold with /companions admin slots|storage give adds on top of it, so giving 1 slot on a stock install takes the total from 1 to 2.
I gave someone 100 slots and they only got 7. Why?
slots.max-count in config.yml, which ships at 7. It is the ceiling on the
TOTAL slots an admin command may leave, and a command past
it is clamped rather than cancelled - so the grant went through, it just stopped at a total of 7,
and the admin was told so before the usual confirmation. 7 is the number of companion cells the
shipped guis/main.yml layout can draw; anything past it would be bought and never usable.
Raise the key if you widened the menu layout, or set it to 0 to remove the ceiling entirely.
storage.max-capacity is the same knob for storage and ships at 0, so storage grants are
unlimited by default.
Two things it does not do: it does not cap sncompanions.slots.<n> permission grants, so a rank can
still grant more than a command can; and it never lowers a row by itself. Lowering the key on a
live server leaves players above it alone until the next slots give/set on them.
Can I add my own companions?
Yes. Copy a file in companions/ and rename it: the file name is the companion id. The folder is yours
after the first boot, so nothing you add or delete there is ever undone by an update. Eggs
work the same way, except they are keys inside eggs.yml rather than separate files: copy a
whole top-level block and rename the key.
Why does a brand new companion read level 0?
Because since 1.10.0 that is the level a companion is created at. It earns its first level like
every level after it: opening an egg, a fusion result that inherits no level, and
/companions admin give with no [level] token all produce a level-0 companion.
Nothing that already exists moved. Every companion on your server kept the level it had, and a level
is worth exactly what it was worth on 1.9.0 - the buff ramp, the edtools-boosts ramp and the
buff-display ramp are all still initial + (level - 1) x per-level and were not touched. What
moved is where a companion starts, so a companion at level 0 is worth initial - per-level: one
written with initial equal to per-level is worth nothing until it earns its first level, and one
whose initial is larger still grants something at 0.
Level 0 and level 1 cost the same experience, and that is deliberate. experience.base in a
companions/<id>.yml is the cost of leaving the FIRST level, and both of them are it; every level
above still adds per-level exactly as before, so the only change to a curve is one extra rung of
base over a companion's lifetime. Extending the curve one step down instead would ask for
base - per-level to leave level 0, and a requirement of 0 is the sentinel that means "this
companion does not progress at all" - the shipped ember_fox.yml declares base: 500 with
per-level: 500, so that extension lands on exactly 0 and every fresh copy of it would sit at level
0 forever with nothing in its file to blame.
Two things follow from it that can look like their own bugs. A companion ITEM no longer mints a free
level: take a brand new companion out as a head, redeem it, and it comes back at level 0, because
the level tag on the head is floored at 0 rather than 1 when it is read back. And
/companions admin setlevel <player> <instance> 0 is accepted now, which is how you put a companion
back to fresh. No configuration key changed, so there is nothing for you to edit - see
A companion is created at level 0.
Do I need EdTools?
No. It is optional, exactly like BetterModel. Without it no companion grants a booster, no companion can use the
EDTOOLS_BLOCK_BREAK experience source, no EdTools class is ever loaded and everything else works
unchanged. Install it only if you want an equipped companion to boost your server's currencies or its
global enchant multiplier, or to level from the blocks your omnitools break.
I gave a companion edtools-boosts and it grants nothing. Why?
Almost certainly the one-decimal rule. SnCompanions never hands EdTools more than one decimal of the
fraction it wants, so the granted boost moves in steps of 10%: a summed total below 5% rounds to
zero and the booster is removed rather than written at nothing. A companion with initial: 2.0 at level 1
is worth 2%, which rounds away.
| Summed percent across every equipped companion | What EdTools receives |
|---|---|
| under 5% | nothing |
| 5% to 14% | +10% |
| 15% to 24% | +20% |
| 25% to 34% | +30% |
Write your companions in tens if you want what you wrote. Two other things to check: the console line at
boot must say EdTools detected, and an unknown currency id logs one warning naming it.
Check the entry's max: too if it has one. The ceiling is applied to that companion
BEFORE the totals are summed and before this rounding, so a companion capped at max: 4 contributes 4
and, on its own, still rounds away to nothing. That ordering is also what makes the ceiling safe to
use: three companions capped at 4 sum to 12 and grant +10%, which capping after the rounding would have
lost. A max: of 0, a negative one, or no max: at all all mean no ceiling.
My companion's menu line says "Buff Damage: 0.0%" even though the companion boosts currencies
Then that companion's edtools-boosts block grants nothing at its current level - see the
one-decimal question above. {buff} and {buff-value} name the companion's EFFECT: a companion
that declares a real vanilla buff: shows that buff, and a companion whose vanilla buff grants
nothing resolves both placeholders from its edtools-boosts block instead. {buff} then shows the
boosted currencies (named per your messages.edtools-currency-<id> lang entries, the raw id
otherwise) and {buff-value} the live value at the companion's current level, with the per-entry
max: already applied.
A companion that declares neither defaults to a damage buff worth zero at every level, which is the
"Damage 0.0%" line with nothing behind it. Since 1.9.0 a companion whose effect is paid by another
plugin through the API can declare a buff-display: block in its file (a name and an
initial/per-level/max ramp) and those two placeholders show that instead; SnCompanions never
applies it, it only shows it.
Why do the companion files I already have carry no edtools-boosts block?
Because companions/ is seeded once and never merged again. The commented example ships in
companions/ember_fox.yml for a fresh install only; on a server you already run you paste the block
into the companion files yourself. The edtools band of config.yml does arrive on its own,
because that file is managed.
How do I turn the companion boosters off without uninstalling EdTools?
Set edtools.enabled: false and run /companions reload. The boosters already granted are removed
immediately; you do not need to restart. Turning it back on and reloading writes them again.
Can a companion boost one specific enchant?
No, only the global enchant multiplier. That is the whole of what EdTools exposes to other
plugins, so it is a limit of the integration rather than a decision SnCompanions made. Use one of
enchants, enchant, global-enchants or encantamientos as the key.
My companions get no experience from EdTools mining. Why?
Because BLOCK_BREAK does not see it. EdTools consumes the blocks its omnitools break without ever
firing a vanilla BlockBreakEvent, so on a farming server those two count completely different
things. Set the companion's experience.source to EDTOOLS_BLOCK_BREAK instead, and leave
experience.sources.edtools-block-break on in config.yml. companions/<id>.yml is seed only, so you
edit the companion files you already have by hand; the config key arrives on its own.
Can a companion level from only one of my EdTools tools?
Yes. Add an optional experience.tools list to that companion's file, holding the EdTools tool ids as
EdTools itself names them:
experience:
source: EDTOOLS_BLOCK_BREAK
ratio: 0.5
tools:
- crop-toolThe ids are matched case-insensitively and are never validated against EdTools, so one that names no
tool simply never matches. Leave the list out (or empty) and every omnitool counts. The
experience.materials whitelist still applies on top, exactly as it does for BLOCK_BREAK.
Does the EdTools experience source lag the server?
No. It is the busiest event a farming server produces - thousands a second with bulk enchants - and
EdTools fires it off the main thread, so the handler does one cancel check and one counter
increment and nothing else. A single shared task pays the totals out coalesced per
(player, block, tool) every 5 ticks. There is no task per block, no one-tick timer, and no task
per player. On a server without EdTools the listener is never registered and the task never runs.
I turned EdTools off mid-session. Do I have to restart?
No. Disabling or enabling EdTools unregisters or registers the break listener on its own, and stops or starts the drain with it. The same is true if you install EdTools after SnCompanions has already started.
Can players skip the egg animation?
Yes, each player decides for themselves. The eggs menu has a switch in slot 36 - Animation: On /
Animation: Off - and clicking it flips their own preference and saves it, so it survives a relog
and a restart. With it off, opening an egg gives exactly the same companions and the summary goes
straight to chat with no Dragon Egg drawn at all.
That is the PLAYER's switch. Yours is animation.enabled in the animation: block of an egg in
eggs.yml: set it to false and nobody sees that egg hatch whatever they chose. The two are
independent, and the server's wins.
The eggs menu closes itself when a show starts, since the animation happens in the world and the shop would cover it. With the animation off it stays open, and the summary arrives with the balance already redrawn.
The show itself costs nothing to skip. The companions are granted and written to the database
before the first frame is drawn, so quitting, crashing or changing world in the middle loses the
animation and nothing else - and a player who buys a second egg while the first is still hatching
is refused with messages.egg-animating before a coin moves, rather than being given two eggs on
top of each other.
Does anything happen to my database when I update?
An owner table that has no egg_animation column - the one that stores each player's animation
switch - gets it added automatically on the next boot. Every existing player keeps the animation
ON, which is the default the column ships with, so nobody's setup changes.
There is nothing to run by hand. The plugin asks the driver whether the column is already there before trying to add it, and swallows the "duplicate column" answer underneath in case the driver's metadata lags, so the boot after the first one runs one catalogue read and no statement at all. Both SQLite and MySQL are covered. If the column genuinely cannot be added the plugin disables itself rather than running with a table it cannot read - the same rule the initial table creation already follows.
Since 1.10.0 a fresh companions table declares level ... DEFAULT 0; a table that already exists
keeps DEFAULT 1 and neither notices, because every INSERT names the column.
Take your usual backup before any update, as always. Nothing else about the schema changed.
How do I add a second egg?
Write a second top-level key in eggs.yml and reload. There is nothing else to do: guis/eggs.yml
is ONE menu for every egg, so the new key gets its shop page automatically and
/companions eggs <id> tab-completes it from the moment it loads.
rare_egg:
display-name: "&b&lRare Egg"
price:
currency: vault
opens:
- amount: 1
price: 25000
- amount: 10
price: 220000
drops:
gale_sprite:
amount: 1
weight: 70
stone_golem:
amount: 1
weight: 30The egg button of the storage menu always opens eggs.default from config.yml - point it at the
egg you want players to land on. To reach the others, give them a /companions eggs rare_egg
button in another menu, a command block, or a shop entry.
eggs.yml is seed-only, so the file is yours: adding, renaming and deleting keys is never undone
by an update. An id may not contain a dot, which is the yml path separator.
The eggs menu shows only five companions and my egg has eight. Where are the rest?
The pool is sized by the menu mask, not by the egg. guis/eggs.yml ships with five p cells, so
the first five rows of the drop table are drawn and the rest are not - that is configuration, and
the plugin never invents a slot of its own. Widen the run:
layout:
- " i "
- " ppppppp "
- " ppppp "
- " "
- " oo<oo "That mask shows twelve. The rows are drawn in the order eggs.yml writes them, so the ones you
want visible go first. The odds are unaffected either way: {chance} is normalized over the WHOLE
table, and a companion that is not drawn can still be won.
The same rule governs the o run and the price buttons: four cells, four opens: entries drawn.
Can one egg cost two currencies at once?
Yes. Write the button's price as a map instead of a number, and it is charged in every currency it
names, together:
premium_egg:
display-name: "&6&lPremium Egg"
price:
currency: vault
opens:
- amount: 1
price: 5000 # a plain number: 5000 of price.currency
- amount: 10
price: # a map: BOTH, on the same click
vault: 45000
"edtools:essence": 250It is an AND, never a choice. The button locks unless every wallet covers its part, all of them
are taken on the click, and if the open then fails they all come back. There is no "pay in whichever
you have" - and two buttons with the same amount will not give you one either, because a click
resolves its bundle by that number and both would charge the first one's price.
Quote a currency inside the map: the colon is the yml key separator. price.currency still says
which wallet a plain NUMBER comes out of; a button that writes a map ignores it.
The button shows the whole price on one line, built from your language file:
menus:
eggs:
currency-vault: "Monedas"
currency-essence: "Esencia"
price-entry: "{price} {currency}"
price-separator: "&7 + &f"which reads 45.000 Monedas + 250 Esencia. Change price-entry to "{price} de {currency}" and
every button and every chat line follows.
The EdTools currencies are charged first and the vault one last, because vault is the only leg
that cannot be handed back to a player who has already logged off. If any leg refuses, whatever was
already taken is returned and the console names it.
Can eggs cost an EdTools currency?
Yes, and it does not depend on edtools.enabled. Give the egg a price.currency of
edtools:<id> in eggs.yml, where <id> is the currency id EdTools itself knows:
rare_egg:
display-name: "&b&lRare Egg"
price:
currency: edtools:orbs
opens:
- amount: 1
price: 25The balance check, the charge and any refund all go through the EdTools currency API, so the
balance a player sees in EdTools is the balance the egg spends. edtools.enabled governs the
companion BOOSTERS and nothing else, so an egg priced this way is charged even on a server that has
turned boosters off - only EdTools itself has to be installed and running.
An id EdTools does not serve is named in the console the moment eggs.yml loads, and until you fix
it the egg refuses every click with messages.egg-no-currency rather than opening for free. To
show a friendlier word than the raw id, add menus.eggs.currency-<id> to your language file:
menus:
eggs:
currency-orbs: "Orbs"A player clicked an egg and was told there is no economy. Why?
The egg is priced in vault and the server has no economy backend for SnLib to charge - no Vault
plugin, or Vault with no economy provider behind it. The purchase is REFUSED, on purpose: opening
an egg produces companions, so handing one out because the economy is missing would mint companions
nobody paid for. Install an economy plugin, or price the egg in an EdTools currency instead.
This is deliberately different from a costed fusion, which IS free when there is no economy: a
fusion consumes companions the player already owns, so a missing economy only removes a brake.
/companions admin openegg is unaffected either way - it never charges anybody.
Tab completion of /companions admin give stops offering companions after the hundredth. Typing the name works.
Update SnLib to SnLib 1.34.1 or later. The companion ids are suggested by SnLib, which until 1.34.1 cut the option list at 100 before matching what you typed, so with more than 100 companion files every id past the hundredth (alphabetically) was unreachable from tab even by its own first letters - typing it in full still worked, because parsing reads the whole set. SnCompanions itself needs no change; the fix is in the SnLib jar.
My console warns Could not save messages_es.yml ... because messages_es.yml already exists on every startup
Fixed in 1.11.3. Update, and the line is gone.
SnCompanions writes lang/messages_es.yml itself on the first boot, because SnLib only ever seeds
the English file and the Spanish one has to be on disk before the language module reads it. Bukkit's
own copy call does not fail when the file is already there, it logs that warning instead, and the
plugin only ever handled the other outcome (a jar carrying no such file). So from the second startup
onwards a perfectly healthy install kept warning about the file its own first startup had written on
purpose.
Nothing was ever wrong with the file: no message was lost, your edits were never touched and
lang: es worked the whole time. Only the line was wrong. From 1.11.3 the copy is skipped when the
file is already there, and a lang/messages_es.yml you have deleted is still written on the next
boot exactly as before.