-
Notifications
You must be signed in to change notification settings - Fork 0
For NET Developers
ffredyk edited this page Jul 24, 2026
·
3 revisions
How to embed SQ# scripting into your .NET application or game engine.
| Package | Purpose |
|---|---|
SQSharp.Hosting |
High-level host API — all you usually need |
SQSharp.Core |
SqValue, SqArray, bytecode types |
SQSharp.Compiler |
Lexer, parser, bytecode compiler |
SQSharp.VM |
Stack VM + runtime |
SQSharp.Scheduler |
Fiber scheduler |
using SQSharp.Host;
var host = new SqHost();
host.OnPrint += msg => Console.WriteLine(msg);
host.ExecuteString(@"
_arr = [1, 2, 3, 4, 5];
_arr pushBack 6;
print format ['Array: %1 elements', count _arr];
");
host.TickMain();host.RegisterNular("getFPS", () => new SqValue(1.0 / deltaTime));host.RegisterUnary("spawnEnemy", type => {
SpawnEnemy(type.AsString());
return SqValue.Nil;
});host.RegisterBinary("damageEntity", (entity, amount) => {
var id = (int)entity.AsNumber();
_entities[id].Damage(amount.AsNumber());
return SqValue.Nil;
}, precedence: 4);// ReadOnly — safe from any thread (pure computation)
host.RegisterUnary("getPosition", entity => {
return PackVector3(_entities[(int)entity.AsNumber()].Position);
}, ThreadSafety.ReadOnly);
// Exclusive — host must serialize access
host.RegisterBinary("modifyWorld", (target, action) => {
_world.Modify(target.AsNumber(), action.AsString());
return SqValue.Nil;
}, threadSafety: ThreadSafety.Exclusive);var host = new SqHost();
// 1. Register custom commands
host.RegisterNular("playerCount", () => new SqValue(_players.Count));
host.RegisterUnary("getPlayer", id => new SqValue(_players[(int)id.AsNumber()].ToSqValue()));
// 2. Create additional schedulers (optional)
host.CreateScheduler("AI", budgetMs: 4.0);
host.CreateScheduler("Physics", budgetMs: 5.0);
// 3. Load and execute scripts
host.ExecuteFile("scripts/init.sqf");
host.ExecuteString("print 'Hello from C#';");
// 4. Game loop
while (_running)
{
host.TickAll(); // pump all schedulers
UpdateGameLogic(deltaTime);
Render();
}// Create values
var num = new SqValue(42.0);
var str = new SqValue("hello");
var boolean = new SqValue(true);
var nil = SqValue.Nil;
// Read values
double d = num.AsNumber();
string s = str.AsString();
bool b = boolean.AsBool();
// Check types
SqType type = value.Type;
if (value.Type == SqType.Number) { ... }
if (value.Type == SqType.Array) { ... }// Create
var arr = new SqArray();
arr.PushBack(new SqValue(1.0));
arr.PushBack(new SqValue(2.0));
// Access
SqValue elem = arr[0];
// From C# collection
var fromList = SqArray.FromEnumerable(myList.Select(x => new SqValue(x)));// Execute string — synchronous (compiles and starts, doesn't wait)
host.ExecuteString("sleep 2; print 'done';");
// Execute file
var handle = host.ExecuteFile("scripts/mission.sqf");
// Await completion (C# async)
var result = await handle.ToTask();
Console.WriteLine($"Result: {result.AsString()}");
// Compile without executing
var chunk = host.CompileString("1 + 2");
// chunk is BytecodeChunk — can be cached, serialized, executed latertry
{
host.ExecuteString("_undefinedVar;");
}
catch (SqParseException ex)
{
// Compile-time error
Console.WriteLine($"Parse error at line {ex.Line}: {ex.Message}");
}
catch (SqRuntimeException ex)
{
// Runtime error
Console.WriteLine($"Runtime error: {ex.Message}");
Console.WriteLine($" at {ex.File}:{ex.Line}:{ex.Col}");
}// Register an opaque type
host.RegisterObjectType("Entity", entityId => new SqValue((double)entityId));
// Commands that work with the custom type
host.RegisterUnary("getHealth", entity => {
var e = ResolveEntity((int)entity.AsNumber());
return new SqValue(e.Health);
}, ThreadSafety.ReadOnly);// Create schedulers
host.CreateScheduler("AI", budgetMs: 3.0);
host.CreateScheduler("Physics", budgetMs: 5.0);
// Tick all in game loop
host.TickAll();
// Or tick individually
host.TickMain(); // Main scheduler only
host.TickScheduler(2); // Specific scheduler by ID# Build and run from command line
dotnet run --project src/SQSharp.CLI -- run script.sqf
# Compile to binary
dotnet run --project src/SQSharp.CLI -- compile script.sqf --binary -o script.sqfcSQ# enforces data ownership:
- Each mutable array/hashmap is owned by one scheduler
- Cross-scheduler access throws errors
- Use
freeze/thawfor immutable sharing - Use
sharedfor CAS-based atomics
// When registering commands, declare thread safety:
// ReadOnly — safe from any scheduler
// Exclusive — host handles serialization- Read the Host API for complete embedding guide
- Explore the samples/ directory
- Study
HostMinimal/andHostGame/for real-world host examples - Read Concurrency for multi-scheduler architecture
- Host API — complete embedding reference
- Getting Started — installation and quick start
- Type System — SqValue and type mapping
- Concurrency — scheduler architecture
- Thread Safety — data sharing model
- Getting Started
- Language Guide
- Type System
- Control Flow
- Functions & Code
- Strings & Text
- Arrays & Collections
- Concurrency
- Promise System
- Thread Safety
- Host API & Embedding
- CLI Tool
- Bytecode Reference
- Multiplayer
- Syntax Sugar
- Optimization Guide
- Benchmarks
- count
- select
- pushBack
- append
- deleteAt
- deleteRange
- resize
- reverse
- sort
- find
- in
- forEach
- freeze
- thaw
- isFrozen
- currentScheduler
- clientOwner
- allSchedulers
- schedulerName
- schedulerExists
- schedulerBudget
- setSchedulerBudget
- fiberCount
- readyFiberCount
- waitingFiberCount
- schedulerLoad
- sendTo