Table of Contents

The host and its settings

ServerHost builds and runs a dedicated server. It sets up dependency injection, finds your game's code, listens for connections and runs the game loop. A server's Program is short:

using BSGameLib.Server;

using var host = new ServerHost(new ServerSettings("MyGame"));
host.Run();

Run() blocks until the host is stopped (by Ctrl+C, or by systemd or the Windows service manager), and then returns. Running the server covers each way of running it. The host needs exactly one IGameServer to start; see How it finds your code.

Constructors

Constructor Settings
ServerHost() The defaults in ServerSettings: application ID BSGameLib.Server, port 55237.
ServerHost(IServerSettings) Yours.

Settings

ServerSettings is the settings class. Set its properties in code: BSGameLib does not read them from a configuration file.

Property Default What it does
ApplicationId The argument to new ServerSettings(...), or BSGameLib.Server A name for the server. It appears in the log and in the name of the generated certificate. Clients don't have to match it.
ListenPort 55237 The UDP port the server listens on. The unreliable traffic uses this port plus 2; see Running the server.
BindAddress null, which means every address The IP address the server listens on, for both of those ports. "127.0.0.1" or "::1" listens on that address only. Give an IP address, not a host name: anything else stops the server at startup.
QuicConfiguration null The transport configuration, including your certificate. When it is null, the server makes its own certificate.
CertificateStorePath null, which means certs Where the generated certificate is kept. Used only when the server makes one.

ServerSettings has no DataFolder, so the server keeps its files in the executable's folder. To choose another folder, implement IServerSettings in a class of your own, as here:

using System.Security.Cryptography.X509Certificates;
using BSGameLib.Networking;
using BSGameLib.Server;

public class MySettings : IServerSettings
{
  public string ApplicationId { get; set; } = "MyGame";
  public int ListenPort { get; set; } = 55237;
  public QuicTransportConfiguration QuicConfiguration { get; set; } = new()
  {
    ServerCertificate = X509CertificateLoader.LoadPkcs12FromFile("server.pfx", "secret"),
  };
  public string CertificateStorePath { get; set; }
  public string DataFolder => Path.Combine(AppContext.BaseDirectory, "data");
}

DataFolder is the folder for the server's own files. When it is null, the executable's folder is used. A relative CertificateStorePath is taken from the data folder.

The generated certificate

Without a QuicConfiguration, the server makes a self-signed certificate the first time it starts. It is:

  • an elliptic-curve P-256 key, with the subject CN=<ApplicationId> Dev;
  • valid for a year from the day it is made;
  • stored without a password at certs/quic-dev-cert.pfx in the data folder. CertificateStorePath replaces certs.

On later starts the server loads that file, so the certificate stays the same, and clients that remember it keep trusting the server. Nothing renews it: a file that has run out of date is still loaded as it is. To get a new certificate, delete the file. Clients that remember the old one will then refuse the server, so this is a change of identity.

This certificate is for development and a LAN. For a server on the internet, give the server a certificate of your own through QuicConfiguration; see Server certificates. A QuicConfiguration without a ServerCertificate stops the server at startup with an InvalidOperationException.

Changing the host

ServerHost has two methods a subclass can override:

  • Configure(IServiceCollection) registers your own services. It runs after the registrations the host makes, so a service registered here replaces a scanned one of the same type.
  • ConfigureHost(IHostBuilder) changes the host itself, which is where logging goes. See Logging.
using BSGameLib.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class MyServerHost(IServerSettings settings) : ServerHost(settings)
{
  protected override void Configure(IServiceCollection services) =>
    services.AddSingleton<WorldClock>();

  protected override void ConfigureHost(IHostBuilder builder) =>
    builder.ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Debug));
}

public class WorldClock
{
  public long Now => Environment.TickCount64;
}

Program then runs your host instead:

using BSGameLib.Server;

using var host = new MyServerHost(new ServerSettings("MyGame"));
host.Run();