
BeenThere
Resources drop more in biomes you have been through: per player, by boss kill, with a catch-up tier for returning players.BeenThere
BepInEx 5 plugin for the Valheim server and every client on it (#308).
Resources drop more in biomes you have been through: a player who has
killed a tier's boss gets the tier rate on that tier's items, a player who
names a catch-up tier gets the catch-up rate on every tier up to it until
they kill its boss, everyone else gets vanilla. Both rates default to 3x.
Plugin GUID syxe.BeenThere, assembly BeenThere.dll, version 0.1.0.
Progress is per character, not per world: the group killing the Queen does nothing for a player who was not there. The server decides everything and tells the clients; a client's own cfg only matters in single player.
Tiers
Eight tiers, first to last biome. Each names the boss prefab whose death
completes it, the vanilla world key, and the items it governs (verified
against the 1.0 client in docs/research/valheim-loot-progression-prefabs.md).
| Tier | Boss | Items |
|---|---|---|
| Meadows | Eikthyr | Wood, FineWood |
| BlackForest | gd_king | RoundLog, Stone, CopperOre, TinOre |
| Swamp | Bonemass | ElderBark, IronScrap |
| Mountain | Dragon | SilverOre |
| Plains | GoblinKing | BlackMetalScrap (creature drop; CreatureDrops, default off) |
| Mistlands | SeekerQueen | BlackMarble, YggdrasilWood |
| Ashlands | Fader | Grausten, FlametalOreNew, Blackwood |
| DeepNorth | FrozenKing_p3 | Frostwood, GoldOre |
The table is config ([Tiers]): Order lists the tier names and each name
has a bosses|world key|items line, so a tier can be added or an item moved
without a rebuild. An item may belong to one tier only.
What decides a drop
The player on the damage packet that broke the tree, log, rock, ore deposit or creature, looked up by character name in the server's state:
- killed this tier's boss:
TierRate; - else catch-up tier at or above this tier:
CatchUpRate; - else 1x.
Trees, logs, rocks, ore deposits and loot spawners go through
DropTable.GetDropList; pickables (branches, loose stone, shoreline tin,
snow branches, ash stone) through Pickable.Drop; creatures through
CharacterDrop.GenerateDropList (off unless CreatureDrops). Vanilla's own
resourcerate key still applies underneath; run the world at 1x.
Drops nobody hit (a tree felled by a falling tree, ore collapsing when its
support goes, fire, timed debris) follow NoAttackerRule: WorldKey
(default) gives the tier rate once the world's boss key is set and never the
catch-up rate, ZoneOwner uses the record of the player whose client
generated the drop, None gives 1x.
State
<BepInEx config dir>/BeenThere/state.txt on the server, so on TOWER
/mnt/user/appdata/valheim/BepInEx/config/BeenThere/state.txt. One
character per line; edit it while the server runs and it reloads within a
second and pushes to every client.
world=equinoxv1
player=Syxe;kills=BlackForest,Meadows,Mountain,Plains,Swamp;catchup=Mistlands
player=Myrrdin;kills=BlackForest,Meadows,Mountain,Plains,Swamp;catchup=none
No catchup field means the server's DefaultTier applies; none means
none even if a default is set. A character with no line is a new player: no
kills, the default catch-up. A file for another world is parked as
state.txt.other and a fresh one started. The directory is excluded from
the modpack's config harvest (config_exclude in mods.yml).
Kill credit
When a boss dies, the client that owns it tells the server, and the server
credits the tier to every connected character (KillCreditRadius > 0
restricts it to those within that many metres of the death). Any kill
counts, not only the world's first; the Frozen King counts at phase three.
Everyone sees a top-left message naming who was credited.
Console
On any client, F5:
beenthereorbeenthere status: your record and the rate you get per tier.beenthere catchup <tier|none>: ask the server for a catch-up tier. The server checksAllowedTiersandMaxTierand answers top-left.
Config
syxe.BeenThere.cfg: [Rates] TierRate, CatchUpRate; [CatchUp]
DefaultTier, AllowedTiers, MaxTier; [Rules] NoAttackerRule,
CreatureDrops, KillCreditRadius; [Tiers] as above. The server's values
travel with the state, so clients need no matching cfg.
Build and ship
./build.sh runs the tests (pure logic, no game dlls) and writes
dist/BeenThere.dll beside dist/manifest.json. The csproj references the
game and BepInEx dlls from the local Steam install and the seat's r2modman
profile. The server gets the dll from dist/ through the source: repo row
in mods.yml, as ValheckCountdown does.
Players get mods through the r2modman profile code, which resolves them from Thunderstore, so the client half ships only as a published package:
- Thunderstore team
syxeis the namespace; the package issyxe-BeenTherein the valheim community. A team service account namedsyxe_fleet_publishowns the upload token (shown once in the team's Service accounts tab). - The token lives in the vault at
fleet/thunderstore/publish, fieldtoken(docs/openbao.md). Nothing in the repo or on TOWER holds it. ./publish.shreads the token withbao kv get, builds the zip withtclifromthunderstore.toml(icon, README, generated manifest, the dll at the zip root) at the csproj<Version>, and uploads it. Bump the version inBeenThere.csproj,Plugin.csanddist/manifest.jsontogether; Thunderstore never accepts a version twice.tcliis a dotnet tool (dotnet tool install -g tcli).- After publishing, pin the
mods.ymlrow to the same version; the digest posts the new profile code to the players.