SetNet.NPC 1.2.0

dotnet add package SetNet.NPC --version 1.2.0
                    
NuGet\Install-Package SetNet.NPC -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SetNet.NPC" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SetNet.NPC" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="SetNet.NPC" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SetNet.NPC --version 1.2.0
                    
#r "nuget: SetNet.NPC, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SetNet.NPC@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SetNet.NPC&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=SetNet.NPC&version=1.2.0
                    
Install as a Cake Tool

<p align="center"> <img src="https://raw.githubusercontent.com/Povstalez/SetNet/master/assets/icon.png" alt="SetNet" width="96"> </p>

SetNet.NPC

Interactive non-living entities for SetNet — vendors, buffers, teleporters, quest-givers, healers, trainers.

Every such NPC differs only in what happens when a player interacts with it, so this package standardizes everything around that — registration, spawning, zone interest, and the interact request/response — and lets you write just the interaction logic behind one interface. Two NPCs are then built the same way: register a behaviour, spawn instances. Added by composition — no base class.

The key trick is the capability hand-off: a behaviour doesn't re-implement a shop. VendorNpcBehaviour returns Capability = "vendor:blacksmith", and the client, seeing that, opens its existing vendor UI. Behaviours with an immediate server-side effect (a buffer) skip the hand-off and just act + return Ok.

Install

dotnet add package SetNet
dotnet add package SetNet.NPC

Usage

Call NpcRuntime.Enable() once at startup on both ends (before handler discovery).

Server — register behaviours, spawn instances:

NpcRuntime.Enable();
var npc = server.UseNpc(new NpcOptions { Services = provider });   // Services lets behaviours resolve app hubs

npc.Register(new VendorNpcBehaviour(vendorId: "blacksmith", npcType: "blacksmith"));
npc.Register(new TeleporterNpcBehaviour(defaultDestination: "dungeon", npcType: "portal"));
npc.Register(new BufferNpcBehaviour(buffId: "blessing", npcType: "priest"));   // needs an IBuffApplier in Services

var id = npc.Spawn(new NpcSpawn {
    Type = "blacksmith", Position = new Vec3(10, 0, 5), Zone = "town",
    Metadata = Encoding.UTF8.GetBytes("Borin") });

Client — discover, interact, follow the hand-off:

NpcRuntime.Enable();
var npc = client.UseNpc();
npc.NpcSpawned   += inst => AddToWorld(inst);
npc.NpcDespawned += id   => RemoveFromWorld(id);

await npc.EnterZoneAsync("town");            // receive that zone's interest-scoped spawn/despawn stream

var resp = await npc.InteractAsync(npcId, action: "open");
if (resp.Ok && resp.Capability?.StartsWith("vendor:") == true)
    OpenVendorUi(resp.Capability.Substring("vendor:".Length));   // hand off to your existing SetNet.Vendor UI

Writing a behaviour

A new NPC = a new INpcBehaviour + Register + Spawn. Nothing else changes.

public sealed class HealerNpcBehaviour : INpcBehaviour
{
    public string NpcType => "healer";

    public async Task<NpcResponse> OnInteractAsync(NpcContext ctx, NpcInteraction req)
    {
        // pull whatever hubs the app registered — behaviours never touch statics
        var hp = (IHealthHub)ctx.Services.GetService(typeof(IHealthHub));
        await hp.RestoreAsync(ctx.PlayerKey);
        return NpcResponse.Success("You are healed.");   // immediate effect, no hand-off
    }
}

API

Server: server.UseNpc(NpcOptions?)NpcServer

Member Purpose
Register(INpcBehaviour) one behaviour per NPC Type
Spawn(NpcSpawn) → string id spawn an instance, push it to interested clients
Despawn(id) → bool despawn + push
InstancesInZone(zone) / InstanceById(id) tooling / respawn
KeyOf(peer) / Interest / CanInteract identity + interest + the pre-interaction gate

Client: client.UseNpc()NpcClientInteractAsync(id, action, payload?), EnterZoneAsync(zone) / LeaveZoneAsync(zone), Nearby, events NpcSpawned / NpcDespawned.

Options (NpcOptions): PlayerKey (peer → stable key, default = connection id), Services (provider handed to each behaviour), Interest (AllInterest default; ZoneInterest for larger worlds), CanInteract (server-side gate: (playerKey, instance) → bool, put a distance/faction check here for anti-cheat).

Worked behaviours (all implemented via the one interface):

Behaviour Interaction Result
VendorNpcBehaviour "open" Ok, Capability = "vendor:<id>" (no side effect — client transacts via its own vendor module)
BufferNpcBehaviour "buff" resolves IBuffApplier from Services, applies the buff, returns Ok (no hand-off)
TeleporterNpcBehaviour "teleport" (payload = dest zone) Ok, Capability = "teleport:<zone>" (client drives the migration)

NpcRuntime.Enable() — one-time bootstrap.

Notes

  • Rides the unified SetNet.Protocol messaging layer on the Channels.Npc channel — all modules share one envelope wire type, so there are no per-module wire ids to reserve. Serializer-agnostic: the control protocol is hand-framed byte[]; your Metadata and interaction Payload are opaque bytes carried through untouched.
  • Interest is push-based. A client enters a zone and receives that zone's spawn/despawn stream; AllInterest (default) tells everyone about everything, ZoneInterest scopes to the subscribed zone. Clients also filter on their own side, so co-located clients don't cross-talk.
  • CanInteract runs before the behaviour. A rejection (or a behaviour that throws) comes back to the client as an NpcResponse with Ok = false — only transport/timeout problems surface as an NpcException.
  • Composition, not hard dependencies. SetNet.NPC depends only on SetNet + SetNet.GeoData (for Vec3). Behaviours reach the economy/quest/status modules through the Services provider, so the NPC layer stays thin and each domain module keeps owning its logic (and its own channel).
  • Node-locality. Instances live on the node that owns their zone (pairs with SetNet.Sharding / SetNet.Zones); a teleporter NPC is the natural bridge between nodes.

Documentation & source

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 93 8/5/2026