DEV Community

Cover image for Porting Mistreevous to C#: A High-Performance Behavior Tree Library for Modern .NET
Diego Teles
Diego Teles

Posted on

Porting Mistreevous to C#: A High-Performance Behavior Tree Library for Modern .NET

Porting Mistreevous to C#: A High-Performance Behavior Tree Library for Modern .NET

When I started building dedicated servers for multiplayer games, I ran into a very real problem: server-side entities needed rich, structured, and consistent AI behavior. In single-player games, the client usually handles this. In modern multiplayer titles with authoritative servers, the AI must run server-side — and that completely changes the performance and architectural demands.

Behavior Trees were the obvious choice. I fell in love with Mistreevous, a beautifully designed TypeScript library by nikkorn, thanks to its clean, modular, and highly expressive DSL. The challenge? Bring that same elegance to the .NET world — but optimized for the harsh reality of server tick loops.

This post is the story of that port: MistreevousSharp, a ground-up rewrite focused on zero-allocation, predictability, and raw performance.


Why Behavior Trees?

Behavior Trees shine in games, robotics, simulations, and any agent-based system because they offer:

  • Hierarchical composition
  • Excellent readability and intent clarity
  • Strong modularity
  • Easy debugging
  • Predictable execution order

For multiplayer servers specifically, they deliver:

  • Extremely low cost per tick
  • Deterministic outcomes
  • Clean separation of logic and state
  • Horizontal scaling across hundreds of entities

Mistreevous already nailed the design in TypeScript. The C# version had to be just as expressive — but truly server-ready.


Why Mistreevous Specifically?

The original Mistreevous stands out with:

  • A fluid, readable DSL
  • Well-defined node types
  • Simple parsing
  • Full JSON compatibility
  • An intuitive API

But it was built for a JavaScript runtime. In .NET (especially .NET 9 and .NET Standard 2.1), we have far more control over memory and performance.

The goal: keep the spirit and compatibility, but eliminate allocations and make it scream on the server.


Not Just a Port — A Performance-Focused Rewrite

While staying 100% semantically compatible with the original, almost every internal detail was re-engineered.

Zero-Allocation: The Core Principle

The primary objective was simple: calling Step() (one AI tick) must generate zero garbage.

On a server with dozens or hundreds of entities ticking 30–60 times per second, even tiny repeated allocations become a GC nightmare.

Key techniques applied:

  1. Eliminated LINQ in hot paths

    No enumerators, closures, or temporary collections.

  2. Reusable buffers and pools

    Pre-allocated, thread-safe lists reused across ticks.

  3. Manual Span-based parsing

    Character-by-character processing instead of Split() or regex.

  4. No captured closures

    Avoided delegate allocations wherever possible.

  5. Classic for loops

    Bypassing enumerator overhead.

  6. Compact node design

    Fixed arrays, lightweight structs, minimal fields.


Full DSL and JSON Compatibility

A non-negotiable requirement: any tree from the original Mistreevous must work unchanged.

Example DSL:

root {
    sequence {
        action [CheckHealth]
        selector {
            action [Flee]
            action [Fight]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

MistreevousSharp supports:

  • Identical SUCCESS / FAILURE / RUNNING semantics
  • Same guard evaluation
  • Subtree references
  • Decorator behavior
  • Execution order

You can literally copy-paste trees between TypeScript and C# projects.


Project Architecture Overview

The repository is organized to mirror the conceptual structure while making optimizations explicit:

MistreevousSharp/
├── assets/                      → Images and thumbnails
├── example/                     → Full working demo (MyAgent + Program.cs)
├── src/Mistreevous/
│   ├── Agent.cs
│   ├── BehaviourTree.cs         → Core execution engine
│   ├── BehaviourTreeBuilder.cs
│   ├── MDSLDefinitionParser.cs  → Zero-alloc DSL parser
│   ├── Nodes/                   → Full node hierarchy
│   │   ├── Composite/ (Sequence, Selector, Parallel, etc.)
│   │   ├── Decorator/ (Repeat, Retry, Flip, etc.)
│   │   └── Leaf/ (Action, Condition, Wait)
│   ├── Attributes/              → Guards and callbacks
│   └── Optimizations/           → Zero-allocation helpers
├── .github/workflows/           → Automated NuGet publish
└── README.md
Enter fullscreen mode Exit fullscreen mode

The Optimizations/ folder is unique to the C# version — it centralizes all performance-critical tricks that don't exist in the TS original.


Rough Performance Comparison

Early benchmarks on modest trees:

Operation Original Mistreevous (TS) MistreevousSharp (C#) Improvement
DSL Parsing ~0.8 ms ~0.35 ms ~2.3× faster
Per-tick execution Variable (GC pauses) Stable ~0.00x ms Near-zero overhead
Allocations per tick Multiple objects Zero Completely eliminated

The real gains scale dramatically with entity count.


Usage Example in C

var definition = @"
root {
    sequence {
        action [CheckHealth]
        selector {
            action [Flee]
            action [Fight]
        }
    }
}";

var agent = new MyAgent(); // Implements your action callbacks

var tree = new BehaviourTree(definition, agent);

while (gameIsRunning)
{
    tree.Step(); // One AI tick — zero allocations
}
Enter fullscreen mode Exit fullscreen mode

See the full example in the repo's example/ folder.


Where It Shines

Beyond multiplayer servers, MistreevousSharp is perfect for:

  • Unity games (no GC spikes)
  • Godot C# projects
  • Simulations and autonomous agents
  • Robotics and drone control
  • Any system needing modular, readable AI

Final Thoughts

Porting Mistreevous to C# was far more than translation — it required rethinking every allocation, every loop, and every object for the .NET reality. The result is a library that keeps the original's elegance and compatibility while being truly ready for high-performance, server-scale scenarios.

If you're building anything with complex agent behavior in .NET, give it a try!

GitHub: https://github.com/guinhx/MistreevousSharp

NuGet: https://www.nuget.org/packages/MistreevousSharp

Original Mistreevous (TS): https://github.com/nikkorn/mistreevous

Feedback, stars, and contributions very welcome! Let me know if you've used behavior trees on the server side — what pains did you hit?

Tags: #dotnet #csharp #gamedev #behaviortrees #performance #opensource #multiplayer

Top comments (0)