World seeding
The items lying around Cyrodiil, the plants you can harvest, the chests and the furniture all start out in the game's own data. For everyone to see the same world, the server turns them into entities: the first time a cell is created on the server, it reads that cell's placed objects from the game data and creates one entity for each. This is called seeding. Later the server respawns what players took, the way the single-player game refills a cell after a few days. A mod can read where a seeded entity came from, keep it from respawning, and move a cell's respawn timer.
Example
A server service that keeps loot from ever coming back. Seeded items are marked persistent, and persistent items are never respawned:
using OblivionMp.Sdk.Common.Archetypes;
using ReadyM.SDK.Attributes;
using ReadyM.SDK.Server.Entities;
namespace MyMod.Server;
[Service]
public sealed partial class NoLootRespawn(IEntities entities)
{
private void Update()
{
foreach (var item in entities.Query<DroppedItem>())
{
if (item.SeededCount > 0 && !item.IsPersistent)
item.IsPersistent = true;
}
}
}
How it works
Seeding a cell
Seeding is done by the seed loader, a server-only mod that comes
with the server download. Its data is the seed, a snapshot of the game's data in the mod's server/content/ folder.
Without the mod, or without the seed, cells are not seeded.
When a cell entity is created on the server, the seed loader loads that cell's placed objects from the seed. Every
object whose base form is one of the types in SeededFormTypes.All becomes an entity in the cell:
| Base form type | Becomes |
|---|---|
Furniture | a Furniture |
Container | a Container, with its contents rolled from the game's container data |
Flora | a Flora, if harvesting it gives an ingredient |
Weapon, Armor, Clothing, Misc, Ingredient, Key, Book, AlchemyItem, Ammo, SoulGem, SigilStone, Apparatus, Light | a DroppedItem |
Each seeded entity gets the object's ref id, base id, position, rotation and whether it starts disabled. An object that already has an entity, found by its ref id, is left alone, so a cell is never seeded twice. Everything else in the cell (doors, NPCs, statics and the rest) stays the game's.
A seeded DroppedItem also records where it came from, in two values it takes from the
SeedOrigin
mixin:
| Value | Meaning |
|---|---|
SeededCount | How many the game data placed. 0 for an item that was not seeded, such as one a player dropped. |
IsPersistent | Whether the game data marks the object persistent. Persistent items are never respawned. |
One server tick after a cell's entities are created, the cell is marked seeded. The
CellSeeding
mixin adds two values to every Cell:
| Value | Meaning |
|---|---|
IsSeeded | Whether the cell has been seeded. A client that sees it true already has every seeded entity of that cell. |
RespawnedAtGameHours | When the cell was seeded or last respawned, in game hours since the game started. |
Respawning
Every tick, the server looks for seeded cells whose RespawnedAtGameHours is 72 or more game hours in the past, and
respawns them, whether or not anyone is there:
- A seeded
DroppedItemthat was picked up or used gets its stack back toSeededCountand is enabled again. Items withSeededCountof0and persistent items are skipped. - Harvested plants grow back.
- Containers whose game data says they respawn are emptied and rolled again.
- The cell's spawn points are reset, so their creatures spawn again.
The cell's RespawnedAtGameHours is then set to now. Game hours come from the world's clock, the same one
SessionClock.NowGameHours()
reads.
What a mod can change
Change seeding values on the server. On a client they are read only.
| To | Do this |
|---|---|
| Keep one item from respawning | Set its IsPersistent to true, or its SeededCount to 0. |
| Change how many of an item come back | Set its SeededCount. |
| Put off a cell's respawn | Set the cell's RespawnedAtGameHours to now. The 72 hours start again. |
| Respawn a cell on the next tick | Set its RespawnedAtGameHours to 72 or more hours before now. |
| Wait until a cell's world items are all there | Check the cell's IsSeeded. Works on the client too. |
| Know whether objects of a form type get an entity | SeededFormTypes.IsSeeded(formType). |
CellSeeding extends Cell, so its values read straight off a cell. Its setters are methods, as on every mixin that
extends another shape:
using OblivionMp.Sdk.Common.Archetypes.Mixins;
using OblivionMp.Sdk.Serverside;
using ReadyM.Api.Idents;
using ReadyM.SDK.Archetypes.Core;
using ReadyM.SDK.Attributes;
using ReadyM.SDK.Server.Entities;
namespace MyMod.Server;
[Service]
public sealed partial class RespawnHold(IEntities entities, SessionClock clock)
{
// Restarts the cell's 72 game hours, for example while players are still fighting in it.
public void PutOffRespawn(FullCellId id)
{
if (entities.TryLookup(id, out Cell cell) && cell.IsSeeded)
cell.SetRespawnedAtGameHours(clock.NowGameHours());
}
}
Not in 1.0 yet: changing the respawn interval for the whole server, or seeding a cell from a mod. The interval is 72 game hours, as in the single-player game.
Related diagnostics
| Id | What it means | How to fix it |
|---|---|---|
| READYM017 | A client project assigns a replicated value, such as IsPersistent, directly. | Change seeding values from your server project. |