Facepunch.Breen
1.0.0-rc.14
dotnet add package Facepunch.Breen --version 1.0.0-rc.14
NuGet\Install-Package Facepunch.Breen -Version 1.0.0-rc.14
<PackageReference Include="Facepunch.Breen" Version="1.0.0-rc.14" />
<PackageVersion Include="Facepunch.Breen" Version="1.0.0-rc.14" />
<PackageReference Include="Facepunch.Breen" />
paket add Facepunch.Breen --version 1.0.0-rc.14
#r "nuget: Facepunch.Breen, 1.0.0-rc.14"
#:package Facepunch.Breen@1.0.0-rc.14
#addin nuget:?package=Facepunch.Breen&version=1.0.0-rc.14&prerelease
#tool nuget:?package=Facepunch.Breen&version=1.0.0-rc.14&prerelease
Breen
Breen is a scripting language for C# applications. It looks and works like C#, because that's the idea.
Use the Facepunch.Breen package with .NET 10. Scripts run as compiled code, with inputs and C# APIs supplied by your application.
Example script
Save this as score.breen:
// A global keeps its value between runs. This initializer runs once.
[Global]
int TotalScore = 0;
// Define a struct to keep a round's data and scoring logic together.
struct RoundScore
{
public int Kills;
public int Bonus;
// Structs can have methods too.
public int Total() => Kills * 100 + Bonus;
}
// Define a function we can call for each round.
int AddRound(int kills, int bonus)
{
// This local struct belongs to this call.
var round = new RoundScore { Kills = kills, Bonus = bonus };
// Call its method and add the result to our running total.
TotalScore += round.Total();
return TotalScore;
}
// Kills and Bonus are inputs supplied by the application.
// With 3 kills and a bonus of 5, each run adds 305 to the total.
return AddRound(Kills, Bonus);
Quick start
dotnet add package Facepunch.Breen --prerelease
using Breen;
var system = new ScriptSystem(new ScriptSystemOptions
{
Resolver = new DefaultResolver()
});
system.SetStore("global", new ScriptStore());
var script = system.CreateScript(File.ReadAllText("score.breen"));
script.Set("Kills", 3);
script.Set("Bonus", 5);
var result = script.Run();
if (!result.Success)
{
throw new InvalidOperationException(result.Error!.Message, result.Exception);
}
int score = script.ReturnValue.Get<int>(); // 305
[Breen.Store("global")]
[AttributeUsage(AttributeTargets.Field)]
public sealed class GlobalAttribute : Attribute { }
Keep the system and script around for repeated runs; Run() compiles on first use. Change inputs with Set() and run again. Use them sequentially on one thread.
DefaultResolver exposes public C# APIs. Supply your own IResolver to restrict which types and members scripts can access.
Temporary inputs
Use With() to supply values for an invocation without keeping them on the script. Chain multiple inputs, including ref struct values:
var invocation = script
.With("writer", writer)
.With("reader", reader)
.With("amount", 10);
var compilation = invocation.Compile(); // Optional; does not execute the script.
var result = invocation.Run(instructionLimit: 1_000);
With() returns a new invocation, so keep its result or chain it directly into Run(). Names must be referenced by the script, and the last value supplied for a name wins. Inputs assigned with Set() remain available; temporary values override them during execution and the original values are restored even if execution fails.
The invocation is a ref struct and follows C# lifetime rules. Values are copied for each run: borrowed buffers and host objects remain shared, while mutations to a supplied struct's own fields affect only its execution copy. Temporary input assignments are discarded; ordinary values explicitly returned or stored by the script follow the usual persistence rules.
Stack-only values cannot be boxed, returned, or stored for later execution. Scripts receiving stack-only inputs currently cannot declare local functions or perform nested struct assignments through those inputs. Use ordinary With() inputs or Set() inputs when those features are needed.
Features
| ✅ Use classes | ✅ Define classes | ✅ Use structs | ✅ Define structs |
| ✅ Use methods | ✅ Define methods | ✅ Use enums | ✅ Define enums |
| ✅ Locals | ✅ Globals | ✅ Loops | ✅ Arrays |
| ✅ Constructors | ✅ Overloads | ✅ Recursion | ✅ Try catch |
| ✅ Static methods | ✅ Public/private fields | ✅ Collections | ✅ String interpolation |
| ✅ Use generic classes | ✅ Use generic structs | ✅ Use generic methods | ✅ Sandboxed APIs |
| ✅ Use properties | ✅ Use indexers | ✅ Use interfaces | ✅ Execution budgets |
| ✅ Null checks | ✅ Persistent state | ✅ Editor completion | ✅ Calls from C# |
| 🔴 Define generic classes | 🔴 Define generic structs | 🔴 Define generic methods | 🔴 Define interfaces |
| 🔴 Inheritance | 🔴 Define properties | 🔴 Define indexers | 🔴 Define operators |
| 🔴 Define static fields | 🔴 Define readonly fields | ✅ Lambdas | 🔴 Async/await |
| 🔴 Define records | 🔴 Define nested types | 🔴 Switch | 🔴 Named arguments |
Script.Call() invokes a top-level function and keeps initialized script state between calls. Run() reruns top-level setup. To call an object's method from C#, wrap it in a top-level script function.
Execution has a configurable instruction budget and a 128-call recursion limit. Unused function and type declarations preserve the normal execution fast paths.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- Microsoft.CodeAnalysis.CSharp (>= 5.0.0)
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.0.0-rc.14 | 1,057 | 9/19/2026 |
| 1.0.0-rc.12 | 71 | 9/18/2026 |
| 1.0.0-rc.3 | 63 | 9/11/2026 |
| 1.0.0-rc.2 | 62 | 9/10/2026 |