Skip to main content
Version: 0.4.1

Server events

Systems tick every frame, and RPC handlers run when a client asks for something. Server events are the third way a mod gets control: the relay tells you when something happened.

New in 0.4.0.

Subscribing

Derive from ServerEventHandlersBase and implement Subscribe and Unsubscribe. The base class hooks both into the mod lifecycle, so you never unsubscribe by hand:

EventHandlers.cs
public class EventHandlers(ServerEventsApi events, EcsApi ecs, ILogger logger)
: ServerEventHandlersBase(events)
{
protected override void Subscribe(ServerEventsApi e)
{
e.OnPlayerJoinedArea += OnPlayerJoinedArea;
e.OnAreaDeleted += OnAreaDeleted;
}

protected override void Unsubscribe(ServerEventsApi e)
{
e.OnPlayerJoinedArea -= OnPlayerJoinedArea;
e.OnAreaDeleted -= OnAreaDeleted;
}

private void OnPlayerJoinedArea(PlayerId player, AreaId area)
=> logger.LogInformation("{Player} entered {Area}", player, area);

private void OnAreaDeleted(AreaId area, Entity entity)
{
// last player left, tear down whatever we hung off this area
}
}

Register it as a singleton in your mod's Init, the same as a system:

Services.RegisterSingleton<EventHandlers>();

The events

EventArgumentsFires when
OnWorldEntityCreatedentitythe world entity exists, once at startup
OnPlayerConnectedplayer, entitya player finishes connecting
OnPlayerDisconnectedplayer, entity, reasona player drops or leaves
OnAreaCreatedarea, entitythe first player enters an area
OnAreaDeletedarea, entitythe last player leaves an area
OnPlayerJoinedAreaplayer, areaa player enters an area that already exists
OnPlayerLeftAreaplayer, areaa player leaves an area others are still in

Where an event hands you an Entity, that is the entity for the thing the event is about, so you can write to its components without going looking for it first.

OnWorldEntityCreated

Your mod's Init runs before the world entity exists, so this is the earliest point at which you can write world components:

protected override void Subscribe(ServerEventsApi e)
=> e.OnWorldEntityCreated += OnWorld;

private void OnWorld(Entity world)
{
ref var settings = ref ecs.GetComponentRef<MatchSettingsComponent>(world.Id);
settings.Rounds = 3;
}

If you find yourself wanting to seed state at startup and not finding anywhere to put it, this is the hook you want. See Archetypes and components for what the world entity is.

Threading

Event handlers run on the server's update thread, the same one your systems tick on. That means you can query and modify entities directly, with no marshalling, and it also means a slow handler delays the tick. Keep them short.