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.