Table of Contents

The chat engine

BSGameLib.Chat takes a line of text and the source it came from. A line that starts with the command character and names a command runs that command, with its arguments. Any other line is yours to deal with as chat. The engine has no networking and needs no other BSGameLib package: your code receives the lines and sends the results, so it works in a server, a client or a tool. Chat on a server covers what BSGameLib.Server adds.

dotnet add package BSGameLib.Chat --prerelease

An engine of your own

An engine is a class derived from BaseChatEngine<T>. The source type is any class implementing IMessageSource, which has no members, so the class holds what your commands need. Your engine's Handle is called with each line. The examples on the rest of this page use the engine and player below.

using BSGameLib.Chat;
using BSGameLib.Chat.Attributes;

public class Player : IMessageSource
{
  public string Name { get; set; } = "";
}

public sealed class ActivatorFactoryProvider : ICommandFactoryProvider
{
  public ICommandFactory<TCommand> GetFactory<TCommand>() where TCommand : class, ICommand =>
    new Factory<TCommand>();

  private sealed class Factory<TCommand> : ICommandFactory<TCommand>
    where TCommand : class, ICommand
  {
    public TCommand Create() => Activator.CreateInstance<TCommand>();
  }
}

public sealed class GameChat : BaseChatEngine<Player>
{
  public GameChat() : base(commandProviders: null, commandFactoryProvider: new ActivatorFactoryProvider()) { }

  public override void Handle(Player source, string message)
  {
    if (TryHandleCommand(source, ref message))
      return;

    Console.WriteLine($"{source.Name}: {message}");
  }
}

TryHandleCommand returns true when the line was consumed: a command, a command that isn't known, or a blank line. It returns false for chat, leaving message as the text to send on. The constructor takes the command character as its last argument, '/' by default.

Commands made with attributes are created by an ICommandFactoryProvider, when the engine has one. The server supplies one. On its own you can write one, as above, or pass null: the engine then creates each attribute command with its parameterless constructor, and registering a command that has none throws an InvalidOperationException. Commands made with the builder need neither.

Typing a command

  • The first word after the command character names the command, and it is matched against the command's keywords, ignoring case. A command's name is a label for your code and for help. It is typed only if you also list it as a keyword.
  • The words after it are the arguments, matched to the parameters in order.
  • Extra words after the last parameter are ignored, unless that parameter takes the rest of the line.
  • A line for a command that doesn't exist raises CommandNotFound, and the line is still consumed: it doesn't become chat.