Table of Contents

Class ServiceBindingAttribute

Namespace
Anvil.Services
Assembly
NWN.Anvil.dll

The main attribute used to define new services and run them.
Each class flagged with a ServiceBindingAttribute will be automatically constructed on startup, and any services specified in the constructor will be injected as dependencies.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ServiceBindingAttribute : Attribute
Inheritance
ServiceBindingAttribute
Inherited Members
Extension Methods

Examples

/*
 * Service binding basics - starts two services and logs a message for each.
 */

using Anvil.Services;
using NLog;

namespace NWN.Anvil.Samples.Services
{
  // The "ServiceBinding" attribute indicates this class will be created on start, and available to other classes as "ServiceA".
  [ServiceBinding(typeof(ServiceA))]
  public class ServiceA
  {
    // Gets the logger for this service. By default, this reports to "logs.0/anvil.log"
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();

    // As "ServiceA" has the ServiceBinding attribute, this constructor will be called during server startup.
    public ServiceA()
    {
      Log.Info("Service A Loaded!");
    }
  }

  // This class will be available to other classes as "ServiceB".
  [ServiceBinding(typeof(ServiceB))]
  public class ServiceB
  {
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();

    // As "ServiceB" also has the ServiceBinding attribute, this constructor will also be called during server startup,
    // but since "ServiceA" is specified as a parameter (dependency), it will only be started after "ServiceA" has loaded.
    public ServiceB(ServiceA serviceA)
    {
      Log.Info($"Service B Loaded after {serviceA.GetType().Name}!");
    }
  }

  // Checking in the console, or "logs.0/anvil.log", the output should look like this:
  /*
[ServiceA] Service A Loaded!
[ServiceB] Service B Loaded after ServiceA!
*/
}
/*
 * Define a method (OnScriptCalled) to be called when the NwScript "test_nwscript" would be called by the game.
 */

using Anvil.API;
using Anvil.Services;
using NLog;

// The "ServiceBinding" attribute indicates this class should be created on start, and available to other classes as a dependency "MyScriptHandler"
// You can also bind yourself to an interface or base class. The system also supports multiple bindings.
namespace NWN.Anvil.Samples.Services
{
  [ServiceBinding(typeof(BasicScriptHandler))]
  public class BasicScriptHandler
  {
    // Gets the server log. By default, this reports to "anvil.log"
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();

    // This function will be called as if the same script was called by a toolset event, or by another script.
    // Script name must be <= 16 characters similar to the toolset.
    // This function must always return void, or a bool in the case of a conditional.
    // The NwObject parameter is optional, but if defined, must always be a single parameter of the NWObject type.
    [ScriptHandler("test_nwscript")]
    private void OnScriptCalled(CallInfo callInfo)
    {
      Log.Info($"test_nwscript called by {callInfo.ObjectSelf?.Name}");
    }
  }
}
/*
 * Find a trigger with the tag "mytrigger" and create a handler for its "OnEnter" event.
 */

using System.Linq;
using Anvil.API;
using Anvil.API.Events;
using Anvil.Services;
using NLog;

namespace NWN.Anvil.Samples.Services
{
  [ServiceBinding(typeof(TriggerHandlerService))]
  public class TriggerHandlerService
  {
    // Gets the server log. By default, this reports to "anvil.log"
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();

    public TriggerHandlerService()
    {
      NwTrigger? trigger = NwObject.FindObjectsWithTag<NwTrigger>("mytrigger").FirstOrDefault();
      if (trigger != null)
      {
        trigger.OnEnter += OnTriggerEnter;
      }
    }

    private void OnTriggerEnter(TriggerEvents.OnEnter obj)
    {
      if (obj.EnteringObject.IsPlayerControlled(out NwPlayer? player))
      {
        Log.Info("Player entered trigger: " + player.PlayerName);
      }
    }
  }
}
/*
 * Implement a "Chat Command System" using a common interface, and binding to the interface.
 */

using System.Collections.Generic;
using System.Linq;
using Anvil.API;
using Anvil.API.Events;
using Anvil.Services;

namespace NWN.Anvil.Samples.Services
{
  // Our base chat command interface...
  public interface IChatCommand
  {
    string Command { get; }
    void ExecuteCommand(NwPlayer caller);
  }

  // ...Each one of our commands implements the IChatCommand interface...
  [ServiceBinding(typeof(IChatCommand))]
  public class GpCommand : IChatCommand
  {
    public string Command => "!gp";
    private const int Amount = 10000;

    public void ExecuteCommand(NwPlayer caller)
    {
      caller.ControlledCreature?.GiveGold(Amount);
    }
  }

  /// ...and uses the interface type instead of the class type inside the ServiceBinding attribute.
  [ServiceBinding(typeof(IChatCommand))]
  public class SaveCommand : IChatCommand
  {
    public string Command => "!save";

    public void ExecuteCommand(NwPlayer caller)
    {
      caller.ExportCharacter();
      caller.SendServerMessage("Character Saved");
    }
  }

  [ServiceBinding(typeof(ChatHandler))]
  public class ChatHandler
  {
    private readonly List<IChatCommand> chatCommands;

    // We add a dependency to the chat commands created above by defining an IEnumerable parameter of the interface type.
    public ChatHandler(IEnumerable<IChatCommand> commands)
    {
      // Store all define chat commands.
      chatCommands = commands.ToList();

      // Subscribe to the global module chat event. When this event occurs, we call the OnChatMessage method.
      NwModule.Instance.OnPlayerChat += OnChatMessage;
    }

    public void OnChatMessage(ModuleEvents.OnPlayerChat eventInfo)
    {
      // Get the message from the event.
      string message = eventInfo.Message;

      // Loop through all of our created commands, and execute the behaviour of the one that matches.
      foreach (IChatCommand command in chatCommands)
      {
        if (command.Command == message)
        {
          command.ExecuteCommand(eventInfo.Sender);
          break;
        }
      }
    }
  }
}

Constructors

ServiceBindingAttribute(Type)

Defines this class as a service.

Fields

BindFrom