builtin scripts
This commit is contained in:
+60
-37
@@ -58,6 +58,7 @@ use crate::board::Board;
|
||||
use crate::game::SAY_DURATION;
|
||||
use crate::log::LogLine;
|
||||
use crate::map_file::{color_to_hex, parse_color};
|
||||
use crate::object_def::ObjectDef;
|
||||
use crate::utils::{Direction, ObjectId, RegistryValue, ScriptArg};
|
||||
use rhai::{
|
||||
AST, Array, CallFnOptions, Dynamic, Engine, FuncArgs, ImmutableString, Module,
|
||||
@@ -102,11 +103,24 @@ struct CompiledScript {
|
||||
/// The runtime state of one scripted object.
|
||||
struct ObjectRuntime {
|
||||
object_id: ObjectId,
|
||||
script_name: String,
|
||||
/// Key into [`ScriptHost::scripts`] for this object's compiled script — the
|
||||
/// pool name for a named script, or the source text for an embedded built-in.
|
||||
script_key: String,
|
||||
scope: Scope<'static>,
|
||||
output_queue: ObjQueue,
|
||||
}
|
||||
|
||||
/// The compile-key for an object's script: the embedded source itself for a
|
||||
/// built-in (so identical built-ins share one compiled AST), or the world-pool
|
||||
/// name for a named script. `None` if the object has no script.
|
||||
fn script_key(obj: &ObjectDef) -> Option<String> {
|
||||
if let Some(src) = obj.builtin_script {
|
||||
Some(src.to_string())
|
||||
} else {
|
||||
obj.script_name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ── Rhai types exposed to scripts ────────────────────────────────────────────
|
||||
|
||||
/// A snapshot of one board object, returned by `Board.tagged`, `Board.named`,
|
||||
@@ -205,44 +219,53 @@ impl ScriptHost {
|
||||
|
||||
let board = board_cell.borrow();
|
||||
|
||||
// Compile each referenced script once.
|
||||
// Compile each referenced script once, keyed by `script_key` (pool name for
|
||||
// named scripts, source text for embedded built-ins so identical ones share
|
||||
// one AST).
|
||||
let mut scripts: HashMap<String, CompiledScript> = HashMap::new();
|
||||
let mut failed: HashSet<String> = HashSet::new();
|
||||
for obj in board.objects.values() {
|
||||
let Some(name) = obj.script_name.as_ref() else {
|
||||
let Some(key) = script_key(obj) else {
|
||||
continue;
|
||||
};
|
||||
if scripts.contains_key(name) || failed.contains(name) {
|
||||
if scripts.contains_key(&key) || failed.contains(&key) {
|
||||
continue;
|
||||
}
|
||||
match script_sources.get(name) {
|
||||
Some(src) => match engine.compile(src) {
|
||||
Ok(ast) => {
|
||||
let defines = |n: &str, params: usize| {
|
||||
ast.iter_functions()
|
||||
.any(|f| f.name == n && f.params.len() == params)
|
||||
};
|
||||
scripts.insert(
|
||||
name.clone(),
|
||||
CompiledScript {
|
||||
has_init: defines("init", 0),
|
||||
has_tick: defines("tick", 1),
|
||||
has_bump: defines("bump", 1),
|
||||
ast,
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
failed.insert(name.clone());
|
||||
// Source: the embedded built-in, or a lookup in the world script pool.
|
||||
let source: &str = if let Some(src) = obj.builtin_script {
|
||||
src
|
||||
} else {
|
||||
match script_sources.get(&key) {
|
||||
Some(src) => src,
|
||||
None => {
|
||||
failed.insert(key.clone());
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{name}' failed to compile: {err}"
|
||||
"object references unknown script '{key}'"
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
failed.insert(name.clone());
|
||||
}
|
||||
};
|
||||
match engine.compile(source) {
|
||||
Ok(ast) => {
|
||||
let defines = |n: &str, params: usize| {
|
||||
ast.iter_functions()
|
||||
.any(|f| f.name == n && f.params.len() == params)
|
||||
};
|
||||
scripts.insert(
|
||||
key,
|
||||
CompiledScript {
|
||||
has_init: defines("init", 0),
|
||||
has_tick: defines("tick", 1),
|
||||
has_bump: defines("bump", 1),
|
||||
ast,
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
failed.insert(key.clone());
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"object references unknown script '{name}'"
|
||||
"script '{key}' failed to compile: {err}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -251,17 +274,17 @@ impl ScriptHost {
|
||||
// One runtime per object whose script compiled.
|
||||
let mut objects = Vec::new();
|
||||
for (&id, obj) in board.objects.iter() {
|
||||
let Some(name) = obj.script_name.as_ref() else {
|
||||
let Some(key) = script_key(obj) else {
|
||||
continue;
|
||||
};
|
||||
if !scripts.contains_key(name) {
|
||||
if !scripts.contains_key(&key) {
|
||||
continue;
|
||||
}
|
||||
let queue: ObjQueue = Rc::new(RefCell::new(VecDeque::new()));
|
||||
queues.borrow_mut().insert(id, queue.clone());
|
||||
objects.push(ObjectRuntime {
|
||||
object_id: id,
|
||||
script_name: name.clone(),
|
||||
script_key: key,
|
||||
scope: new_object_scope(board_cell, &queue, id),
|
||||
output_queue: queue,
|
||||
});
|
||||
@@ -302,7 +325,7 @@ impl ScriptHost {
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else {
|
||||
let Some(compiled) = scripts.get(&obj.script_key) else {
|
||||
return;
|
||||
};
|
||||
if !compiled.has_bump {
|
||||
@@ -318,7 +341,7 @@ impl ScriptHost {
|
||||
) {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' bump error: {err}",
|
||||
obj.script_name
|
||||
obj.script_key
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -344,7 +367,7 @@ impl ScriptHost {
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else {
|
||||
let Some(compiled) = scripts.get(&obj.script_key) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -387,7 +410,7 @@ impl ScriptHost {
|
||||
if let Err(err) = result {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' send('{}') error: {err}",
|
||||
obj.script_name, fn_name
|
||||
obj.script_key, fn_name
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -456,7 +479,7 @@ impl ScriptHost {
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
if let Some(compiled) = scripts.get(&obj.script_name)
|
||||
if let Some(compiled) = scripts.get(&obj.script_key)
|
||||
&& defined(compiled)
|
||||
{
|
||||
let options = CallFnOptions::default().with_tag(obj.object_id as i64);
|
||||
@@ -469,7 +492,7 @@ impl ScriptHost {
|
||||
) {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' {hook} error: {err}",
|
||||
obj.script_name
|
||||
obj.script_key
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user