C# scripts: from build to verified result
Runnable example: a dynamic call, signal, formula and UTF-8 file.
Run a complete example
Use a C# project with Reckfell.NET.Sdk 1.0.0 and net10.0. C# setup covers project creation and building.
- Save the four files below in the project with their exact names.
- Run dotnet build in the .csproj directory.
- Create a level with an Entity root, attach ScriptToolsExample.cs and save as script_tools.rscene.
- Run that level. Output should contain SCRIPT_TOOLS count=5; signal=5; damage=25; ok=True.
The example also writes user://script-tools-note.txt containing Reckfell: 25. This is a test note rather than the whole game's state. CounterService is deleted after the check.
NumericExpression.cs
using System;
using Reckfell;
public sealed class NumericExpression : IDisposable
{
private readonly Expression _expression = new();
private bool _parsed;
private int _inputCount;
public bool TryParse(string formula, string[] names, out string error)
{
_parsed = false;
error = "";
if (_expression.Parse(formula, names) != Error.Ok)
{
error = _expression.GetErrorText();
return false;
}
_inputCount = names.Length;
_parsed = true;
return true;
}
public bool TryEvaluate(double[] values, out double number, out string error)
{
number = 0;
error = "";
if (!_parsed || values.Length != _inputCount)
{
error = "Parse first and supply one value per input name.";
return false;
}
using var inputs = new Reckfell.Collections.Array();
foreach (double value in values)
{
if (!double.IsFinite(value))
{
error = "Inputs must be finite numbers.";
return false;
}
inputs.Add(value);
}
using Variant result = _expression.Execute(inputs, null, false, true);
if (_expression.HasExecuteFailed())
{
error = _expression.GetErrorText();
return false;
}
if (result.VariantType is not (Variant.Type.Int or Variant.Type.Float) ||
!double.IsFinite(result.AsDouble()))
{
error = "Expected a finite numeric result.";
return false;
}
number = result.AsDouble();
return true;
}
public void Dispose() => _expression.Dispose();
}TextNoteStore.cs
using System.Text;
using Reckfell;
public static class TextNoteStore
{
public const string Path = "user://script-tools-note.txt";
public const int MaximumBytes = 4096;
public static bool TryWrite(string text, out Error error)
{
error = Error.InvalidParameter;
if (Encoding.UTF8.GetByteCount(text) > MaximumBytes) return false;
using var file = FileAccess.Open(Path, FileAccess.ModeFlags.Write);
if (file is null)
{
error = FileAccess.GetOpenError();
return false;
}
if (!file.StoreString(text))
{
error = file.GetError();
return false;
}
file.Flush();
error = file.GetError();
return error == Error.Ok;
}
public static bool TryRead(out string text, out Error error)
{
text = "";
using var file = FileAccess.Open(Path, FileAccess.ModeFlags.Read);
if (file is null)
{
error = FileAccess.GetOpenError();
return false;
}
if (file.GetLength() > MaximumBytes)
{
error = Error.InvalidData;
return false;
}
string candidate = file.GetAsText();
error = file.GetError();
if (error != Error.Ok && error != Error.FileEof) return false;
text = candidate;
error = Error.Ok;
return true;
}
}CounterService.cs
using Reckfell;
public partial class CounterService : Entity
{
[Signal] public delegate void CountChangedEventHandler(int count);
private int _count;
public int Count => _count;
public bool TryAdd(int amount)
{
if (amount <= 0 || amount > 1000 - _count) return false;
_count += amount;
EmitSignal(SignalName.CountChanged, _count);
return true;
}
}ScriptToolsExample.cs
using Reckfell;
public partial class ScriptToolsExample : Entity
{
public bool Succeeded { get; private set; }
public override void _Ready()
{
var counter = new CounterService();
AddChild(counter);
int observed = 0;
counter.CountChanged += value => observed = value;
bool added = counter.Call(CounterService.MethodName.TryAdd, 5).AsBool();
using var expression = new NumericExpression();
bool parsed = expression.TryParse("base_damage * multiplier",
new[] { "base_damage", "multiplier" }, out string error);
double damage = 0;
bool evaluated = parsed && expression.TryEvaluate(new[] { 20.0, 1.25 }, out damage, out error);
bool saved = TextNoteStore.TryWrite("Reckfell: 25", out Error writeError);
bool read = TextNoteStore.TryRead(out string text, out Error readError);
Succeeded = added && counter.Count == 5 && observed == 5 && evaluated &&
damage == 25 && saved && read && text == "Reckfell: 25";
ReckfellApi.Print($"SCRIPT_TOOLS count={counter.Count}; signal={observed}; damage={damage}; ok={Succeeded}");
if (!Succeeded) ReckfellApi.PrintErr($"SCRIPT_TOOLS failed: {error}; write={writeError}; read={readError}");
counter.Free();
}
}Inspect each part
C# and Variant · Formulas · Files · Logging · Reading the API · Templates
If ok=False, inspect the reported stages: dynamic call, parsing/evaluation and opening/writing/reading the file. Do not keep processing a result that already reported failure.
Reckfell Engine documentationBack to top ↑