Longhouse 1.0.6
25 August 2026. After 1.0.5.
Five pins move. Two of them fix a bug that was doing damage on the server, and the other three are work that had been sitting unreleased.
The one that matters
A biome could latch open for the whole group off a half-loaded world, permanently. It did: on 25 August the Swamp opened while seven of the nine characters on the roster had never met the Elder, and because the gate never regresses, it stayed open.
ZoneSystem.RPC_GlobalKeys clears every global key and re-adds them one at a time, on every
client, every time anybody sets any key. Yoke's hook on GlobalKeyAdd fires inside that loop
and asked Utangard whether the group had cleared a boss - once per key, against a world that
was still filling in. With a partial roster the counted members can be exactly the two who had
just killed the Elder, and the latch saw a cleared group.
The same window made Yoke write vanilla stack sizes for bosses the group had already killed, and nothing arrived afterwards to correct them, so they stayed wrong for the session.
Utangard 1.2.1 refuses to latch while the keys are settling and no longer caches a roster built from a half-filled list. Yoke 1.0.1 marks on a key and acts once at end of frame. Neither changes a rule, a number or a saved value. A gate already latched open stays open - that is what never-regresses means, and unpicking it afterwards would be the worse promise.
The other three
- Core 1.1.0 - a host no longer takes your keybinds, and
Prefabs.csmoves out of the DLL into shared source. The save-on-inventory-change guard is deliberately not in it. - Vaettir 1.1.0 - the sapling half. A planted seed draws greydwarfs in ramping waves out of the treeline, costs fifty, will not go in a base, and is Black Forest only.
- Dyrr 1.1.1 - a refusal now says who was turned away, name and platform id, so the line that reaches Discord names a person rather than only a rule.
Updating
Everyone has to. All five are inside Core's version gate, so a client on the old set is refused rather than merely out of date. The Utangard fix in particular only does anything with more than one player connected - it needs a key broadcast arriving at a client that did not set it.
What moved · 5
Core 1.0.2 → 1.1.0
1.1.0
Two changes, both about who owns what. Neither touches a prefab name or a saved value, so nothing already in a world is at stake.
Not in this release: the save-on-inventory-change guard. It is written and it works, and
it is held back on the saveguard branch rather than shipped, because it changes when every
player's character is written to disk and this release is not the one to find that out in.
Fixed
A host no longer takes your keybinds. Registering a mod syncs its whole config file, which is right for anything a mismatch could desync and wrong for the one class of setting the readme had already warned against in as many words. Three mods here bind a key - Tether, Vaettir's stow and Devkit - so for as long as this stood, joining a server moved your keys to whatever the host had chosen, greyed the entry out, and put the value back if you tried to change it. Keybinds are now held back by type. A mod that genuinely needs one to match can still say so with
discussSuite.Sync.
Added
discussSuite.Local, the other half of that. Keybinds are the only thing Core can recognise on its own, and they are not the only thing a player would resent losing - a UI scale, a colour, a hover-text toggle. Anything a mismatch cannot desync belongs to the player, and only the mod knows which of its settings those are.shared\Prefabs.cs: one runtime prefab registry for the suite, as shared source rather than as part of this DLL. Five mods have their own copy, and the copies are not the point: the wrong version of this destroys saved objects in silence. ZNetScene and ObjectDB are rebuilt on every world load, including a trip to the menu and back, so a mod answering "registered yet?" from a static bool says yes to a scene that has never heard of the prefab, registration early-returns, and every ZDO of it is discarded as junk with nothing written to any log. Stow lost a built piece that way on 2026-08-16. Everything here asks the live scene instead.Prefabs.Keeptakes a name and a builder and holds the thing registered for whatever world is loaded - both of ZNetScene's lookups, ObjectDB when it is an item, a tool's build menu when it is a piece.It is linked into each mod's csproj and excluded from this project, so Core gains no new responsibility and the mods gain no new dependency. That is the whole reason it is a file and not a class in here: Core is soft everywhere, and a mod that could not register its prefab would load, patch nothing into the world and look broken - so owning this would have made Core mandatory for five mods to do anything at all. A runtime fallback was considered and rejected for costing two code paths, the second of which only ever runs where nobody tests, which is how the bug above survived in the first place.
Linked so far by Taum; the five mods carrying their own copy are a change each.
discuss
Dyrr 1.1.0 → 1.1.1
1.1.1
A refusal says who was turned away. The line was "Refused a connection: has played on 1 other world(s)", which is fine in a server log you are reading beside the connection you just watched fail, and useless the moment it is forwarded to Discord - where it arrives as a rule nobody can attach to a person.
It now reads:
Refused a connection: Balder (76561198662440314) has played on 1 other world(s)
Both halves, because each covers the other's gap. The character name is what the other players know and the only part worth reading in a channel, but it is self-reported and a character can be renamed. The platform id comes off the socket, cannot be chosen, and survives a report that failed to parse.
The name is not added to the reason sent back to the client. That string is shown to the person being refused, and telling somebody their own name back is noise - they know who they are, they want to know which rule they broke.
Why it was missing
Judge is a prefix on ZNet.RPC_PeerInfo, so it runs before vanilla has parsed the
package the player's name arrives in. All it holds at that moment is the socket. The name
therefore had to come from Dyrr's own report, which the client sends first - so the report
format moves to 3 and the name is the first field after the version, deliberately:
everything below it can throw on a read and leave the report unreadable, and a report that
failed halfway is exactly the one worth naming.
A format mismatch was already handled and still is, with the same clear line rather than a garbled read.
Utangard 1.2.0 → 1.2.1
1.2.1
A gate could latch open off a half-loaded world. It did, on the live server: the Swamp opened permanently while seven of the nine characters on the roster had never met the Elder.
The bug
ZoneSystem.RPC_GlobalKeys clears every global key and re-adds them one at a time, and it
runs on every client every time anybody sets any key, because SetGlobalKey ends in
SendGlobalKeys(Everybody). For the length of that loop the dictionary this mod reads its
roster and its credits out of is incomplete.
Vanilla never notices - the refill is synchronous, no frame boundary falls inside it. A
Harmony postfix on GlobalKeyAdd does notice, and Yoke has one, hooked there deliberately so
it catches the bulk list a server sends on connect. So every key in that list made Yoke ask
this mod whether the group had cleared a boss, once per key, while the answer was built from
whatever fraction had arrived.
With a partial roster the counted members can be exactly the ones who hold the key - the two
who had just killed the Elder, whose credits were already in - and LatchIfGroupCleared then
finds a group that has cleared it. The !anyCounted guard only ever caught a completely
empty roster; a partial one walked straight through it. The open key is permanent by design,
RPC_SetGlobalKey has no permission check, and so one client's half-loaded view became
everyone's, for good.
The two-second roster cache is what let one frame of that outlive itself.
Fixed
The latch refuses to run while the world's keys are settling. A prefix and postfix on
discussRPC_GlobalKeyshold a flag across the rebuild; while it is up, nothing latches. This is the irreversible half of the mod, so it is the half that must decline to answer early rather than answer wrongly.The roster is never cached from a half-filled key list, and is invalidated on every key that arrives rather than only on a publish. A cache can no longer outlive the world state it was built from.
discuss
Vaettir 1.0.0 → 1.1.0
1.1.0
The sapling half. Four things, and the first of them is the one that matters.
The seed calls the forest to it
A planted sapling now draws greydwarfs to itself, and calls harder the closer it is to opening. Without this it was entirely passive, and the quest it actually set was "go and find a place greydwarfs already walk through, then stand in it" - a scouting problem rather than a defending one, and the wrong half of what the piece is for. Now the place you chose is the thing that matters, and what it summons is what feeds it: a seed planted in an empty meadow is slow rather than impossible, and one planted beside a real camp is still faster, because the camp counts too.
discussIt ramps, and it is a raid rather than wildlife. The interval falls from 20 seconds at an unfed seed to 6 at one about to open, so the loudest part of the fight is the end of it. A constant rate is a wave you learn to stand in; a rising one can be heard without looking at anything. 90 and 30 were the first numbers and they were far too polite - one greydwarf every minute and a half is something you deal with between other jobs.
discussRidden on vanilla's own
discussSpawnArea, which is the component a greydwarf nest is made of. That buys the near and total caps, the floor-finding, the spawn effect, and - the part that matters most - its own guards: owner only, inside the active area, with a player in range. Nothing happens while you are asleep, which was the sapling's first principle and is the one thing a spawner could most easily have broken.They arrive from a distance and run in rather than appearing around the seed. Found in play: they were materialising on top of it. Widening the radius does not fix that, because vanilla's FindSpawnPoint draws its distance from Random.Range(0, radius) - uniform across a disc, so most points land near the middle however wide it is. A prefix replaces that search with one that draws from a band, 35 to 60 metres by default, making the same two ZoneSystem checks in the same order so nothing lands inside a rock. Every other SpawnArea in the game runs vanilla's own method untouched.
discussGetting them to actually come is a second thing. SetPatrolSpawnPoint is off, so they do not treat the trees they appeared in as home, and a sweep every two seconds calls SetHuntPlayer on everything from the roster within reach - which is what a vanilla raid does and means "stop wandering, go and find someone". They hunt the player rather than the sapling: there is no vanilla "attack this object" to ride, the player is standing at the sapling anyway, and it puts the raid on the person holding the ground instead of on the thing with 500 health.
discussIt says so. "The forest is enraged." goes to everyone within BeckonRange, centre screen, once per sapling per load - fired from the first wave actually landing rather than from planting, so a sapling that can find nowhere to spawn never announces a siege that is not coming. Everyone nearby rather than whoever planted it, because it is a warning about a place and on that frame there is nothing on screen to account for the noise. The text is a config line and blank turns it off.
discussThe first wave leaves immediately. Vanilla counts its spawn timer up from zero on a two-second repeat and fires when it exceeds the interval, so an unfed seed produced nothing at all for 22 seconds and then the wave still had to walk in - half a minute of silence after planting, which reads as the mod not working. The timer now starts full, so the first wave goes out on the next tick. It does the same on every later load, and since the timer only advances with a player in range, walking back to a half-fed sapling restarts the siege as you arrive rather than after another silent interval.
discussThe band came in from 35-60m to 25-40m with it, so the first wave is on you in roughly fifteen seconds rather than forty. Still far enough to be out of sight in forest.
discussThey arrive in waves, from one side. Vanilla's spawner produces exactly one creature per interval, which is a queue rather than a raid - a greydwarf, a wait, another greydwarf, and nothing that has to be handled as a group. A wave is 2 at an unfed seed rising to 5 at one about to open, and the whole wave comes out of the trees within 40 degrees of one bearing so it can be turned to face. The extra members go through the game's own SpawnOne, so MaxNear and MaxTotal police themselves and a wave that would breach them simply comes up short.
discussA sapling now costs 50 greydwarf deaths, not 30. Thirty was chosen when the sapling was passive and thirty was roughly one raid happening to arrive. It does not wait for a raid any more, it makes one, so the number is the length of a fight you started rather than the odds of one finding you. Ten of them around you at a time, twenty-four alive in the area.
discussThe trigger range is 48m rather than vanilla's 256m. A nest filling a forest you are nowhere near is one thing; a sapling quietly getting itself killed by what it summoned while you are two zones away is another.
discussSix standing around it at once, sixteen alive in the area. Deliberately more than the sapling survives being ignored for - it has 500 health and about ten brute hits in it - which is the trade the whole feature makes: the forest comes to you instead of you going out to find it, and the price is having to hold the ground.
discussAll of it is off with one setting, and every number above is config.
discuss
Black Forest only
A seed refuses to go in anywhere but the Black Forest, and refuses the last five metres before the edge of it too. It is a greydwarf ritual - what it calls and what feeds it both live there - and one that works in the meadows makes the biome a backdrop rather than the reason.
discussThe margin is checked on a ring of eight points as well as under the cursor, so the whole circle has to be inside. Planting one step past the treeline and then summoning the forest would put half the fight in the meadow, and a boundary is where a raid is least interesting.
discussBoth the biome list and the margin are config, and the refusal says which of the two reasons it was.
discuss
It will not go in anybody's base
A sapling cannot be planted inside a base, and one already standing goes quiet if a base grows around it. This is the price of the seed calling: passive, one planted in someone else's home was rude; summoning waves of greydwarfs at it is a weapon, and a dozen around a stranger's longhouse is the obvious grief on a public server.
discussA ward already refused it and always did - the sapling is an ordinary piece, so PrivateArea turns it down like anything else. The gap was unwarded bases, which is most of them.
discussFilled with the game's own EffectArea.PlayerBase, which is what a workbench or fire radiates and the same test vanilla uses to keep creatures from spawning in your house. So the counter-play to a sapling planted next to you is to put a workbench down rather than to fight it, and "is this someone's home" stays the game's question rather than a guess of ours.
discussAny base, including your own. Working out whose it is means reading Piece.m_creator off whatever is nearby, which is more code for a worse answer - it would still stop you at a friend's base in co-op - and a wilderness ritual has no business in your own hall either.
discussThe refusal says which reason. Vanilla would show "invalid placement", which is true and useless to somebody standing in their own garden.
discussClient-side, honestly. A modded client could ignore it; what makes it stick on a server is Core's version gate.
discuss
Everyone defending it can see the count
Fixed: only whoever landed the killing blow saw the counter.
discussCharacter.OnDeathruns on the client that owns the creature and nowhere else, and the message went toPlayer.m_localPlayer- so with two players clearing greydwarfs around one sapling, each of them saw roughly half the kills register and neither could tell whether the other's were counting at all. They always were; only the message was missing. It now goes to every player within the sapling's own feed range, which needs no networking of ours becausePlayer.Messagealready RPCs to a player this client does not own.
A marker on the map
A planted sapling puts a pin on your map, and takes it off again when it opens or is destroyed. This is the one piece in the mod you are meant to walk away from, and a seed in bare ground is not findable from fifty metres away - losing one to "I know it was around here" costs the same ancient seed as losing it to a brute.
discussClient-side and per player, saved in your own profile like a pin you placed by hand. Nothing about it is networked and a server needs to know nothing about it.
discussThe pin comes off by reconciling against the world - a pin whose zone is loaded with no sapling under it is stale - rather than from the sapling's own OnDestroy. Found in play: the pins never went away. OnDestroy fires on a destroyed sapling and an unloaded one alike, and the test meant to tell them apart asked ZDOMan whether the ZDO was gone, which it never is on that frame - DestroyZDO only queues the uid on m_destroySendList and the ZDO leaves m_objectsByID some frames later. Both endings read as "merely unloaded". Asking whether a sapling is standing there needs no guess, covers every ending including one another player removed, and clears the pins the first version stranded.
discuss
The spirit's parting has an effect again
The parting effect shipped blank in 1.0 because the name it wanted was a guess about the game and the honest answer was "not confirmed". It is now a list walked in order -
discussvfx_ghost_death, then two fallbacks - and the first name that resolves is used, so the wrong ones cost nothing and are skipped in silence. Looked up through PropIndex as well as ZNetScene, because plenty of effect prefabs carry no ZNetView and are invisible to ZNetScene however loaded they are.
Not in this yet
The sapling's staged growth and the sowing of a rank of seeds are still on
discussv1.1-sowing, cut from 1.0 for their art. They are 1.2 now: the roadmap in the 1.0 entry below was renumbered when this release took the 1.1 slot, and that branch keeps its old name. Nothing here touches it, and merging is a separate job - it predates the 1.0 tidy-up and does not fast-forward.
Yoke 1.0.0 → 1.0.1
1.0.1
Stack sizes could revert to vanilla for a boss the group had already killed, and stay there for the rest of the session.
ZoneSystem.RPC_GlobalKeys clears the world's key list and re-adds it one key at a time,
on every client, every time anybody sets any key. This mod's hook is on GlobalKeyAdd - the
right place, because it is the only one that catches the bulk list a server sends on connect -
but it acted there, which meant asking "what has the group earned?" once per key against a
dictionary that was still filling up.
With Utangard installed that question goes to its roster, built from the same half-filled dictionary. On the live server it answered "not earned" mid-refill, this mod wrote vanilla stack sizes off the back of it, and no further key ever arrived to trigger a correction.
Fixed
The key hook marks rather than acts. The flag is drained in
discussUpdate, which is the first moment the whole list is certainly in. As a side effect a connect's worth of keys is now one walk of the item database instead of one walk per key.
Discussion
Nothing said yet.
Sign in or make an account to join in.