-
-
Notifications
You must be signed in to change notification settings - Fork 5
Host Objects
engine.SetGlobal lets you expose a C# object to scripts without writing a custom provider. Public members opt in with attributes.
| Attribute | Target | Description |
|---|---|---|
[ScriptVisible] |
Property or Method | Makes the member readable from script |
[ScriptWritable] |
Property | Also allows script-side assignment |
Both attributes are in the DScript namespace.
using DScript;
public class AppConfig
{
[ScriptVisible]
public string Environment { get; set; } = "production";
[ScriptVisible]
[ScriptWritable]
public int MaxConnections { get; set; } = 100;
[ScriptVisible]
public string GetVersion() => "2.1.0";
[ScriptVisible]
public bool IsFeatureEnabled(string name)
=> name == "dark-mode";
// Not visible to scripts — no attribute
public string InternalSecret { get; set; } = "hidden";
}var config = new AppConfig();
engine.SetGlobal("config", config);// In script
console.log(config.Environment); // "production"
console.log(config.GetVersion()); // "2.1.0"
console.log(config.IsFeatureEnabled("dark-mode")); // true
config.MaxConnections = 50; // allowed — [ScriptWritable]
// config.Environment = "staging"; // would throw — not [ScriptWritable]
// config.InternalSecret; // undefined — not [ScriptVisible]Methods and properties may return:
| C# type | Script value |
|---|---|
int, long, byte, short
|
Integer |
double, float
|
Float |
string |
String |
bool |
Boolean (1/0) |
ScriptVar |
Passed through directly |
void |
Undefined |
Script calls pass arguments positionally. The C# method receives them via normal parameter binding. If fewer arguments are passed than declared parameters, missing values are default(T).
[ScriptVisible]
public int Add(int a, int b) => a + b;config.Add(3, 4); // 7-
SetGlobaluses reflection at call time. For performance-critical paths, consider writing a typed native function withAddNativeinstead. -
[ScriptWritable]without[ScriptVisible]has no effect — the property is not exposed at all. - Static methods are not currently supported via
SetGlobal; useAddNativefor those.
ScriptEngine.RaiseEvent lets the host application fire named events into a running script. Scripts subscribe using the global on / once / off functions registered by EngineFunctionLoader.RegisterFunctions.
This is the primary mechanism for building an application that uses DScript as a scripting host: run the script once to register handlers, then fire events from C# whenever the application state changes.
Call EngineFunctionLoader.RegisterFunctions before running any script. This automatically creates the global host-event emitter and registers the global event helpers.
var engine = new ScriptEngine();
new EngineFunctionLoader().RegisterFunctions(engine);
// Run the script once — it registers handlers and returns
engine.Run(ScriptEngine.Compile(File.ReadAllText("game.ds")));
// Later, raise events from application code
engine.RaiseEvent("playerDied", new ScriptVar("Alice"), new ScriptVar(3));
engine.RaiseEvent("levelCompleted", new ScriptVar(7));| Function | Description |
|---|---|
on(event, fn) |
Register a persistent listener for event
|
once(event, fn) |
Register a one-shot listener (auto-removed after first fire) |
off(event, fn) |
Unregister a previously registered listener |
removeAllListeners(event?) |
Remove all listeners for event, or all listeners if omitted |
// game.ds
on('playerDied', function(name, lives) {
console.log(name + ' died, ' + lives + ' lives left');
if (lives === 0) on('gameOver', handleGameOver);
});
once('levelCompleted', function(level) {
console.log('Level ' + level + ' complete!');
// this handler is automatically removed after firing once
});
function handleGameOver() {
console.log('Game over!');
}Fires name, invoking all matching on / once handlers in registration order. once handlers are removed before being called (so re-registration inside a handler is safe). After all handlers run, the Promise microtask queue is drained, so any async handlers that resolved a Promise will have their .then callbacks executed.
If no handler is registered for name, the call is a no-op.
The Action<string, ScriptVar[]> delegate that RaiseEvent calls. Set automatically by RegisterFunctions. You can replace it with a custom implementation if you need different dispatch semantics.
The backing emitter is exposed as __hostEmitter__ on the global scope. Scripts can use it directly if they need features like .listenerCount() or .listeners():
console.log(__hostEmitter__.listenerCount('playerDied')); // 1The global on / off / once / removeAllListeners functions operate on the same underlying emitter, so mixing both styles is safe.