doukutsu-rs/src/scripting/mod.rs

148 lines
4.4 KiB
Rust
Raw Normal View History

2021-01-01 01:46:01 +00:00
use std::io::{Read, Seek};
use std::ptr::null_mut;
2021-01-27 18:20:47 +00:00
use crate::framework::context::Context;
use crate::framework::error::{GameResult, GameError};
2021-01-01 01:46:01 +00:00
use lua_ffi::{c_int, LuaFunction, LuaObject, State, ThreadStatus};
use lua_ffi::ffi::lua_pushcfunction;
use crate::scene::game_scene::GameScene;
use crate::scripting::doukutsu::Doukutsu;
use crate::shared_game_state::SharedGameState;
2021-01-27 18:20:47 +00:00
use crate::framework::filesystem::File;
use crate::framework::filesystem;
2021-01-01 01:46:01 +00:00
mod doukutsu;
mod player;
mod scene;
pub struct LuaScriptingState {
state: Option<State>,
state_ptr: *mut SharedGameState,
ctx_ptr: *mut Context,
game_scene: *mut GameScene,
}
pub static REF_ERROR: &str = "Reference went out of scope. DO NOT store/use references to game objects outside the event.";
static BOOT_SCRIPT: &str = include_str!("boot.lua");
fn check_status(status: ThreadStatus, state: &mut State) -> GameResult {
match status {
ThreadStatus::Ok | ThreadStatus::Yield => { return Ok(()); }
_ => {}
}
let error = state.to_str(-1).unwrap_or("???");
match status {
ThreadStatus::RuntimeError => Err(GameError::EventLoopError(format!("Lua Runtime Error: {}", error))),
ThreadStatus::SyntaxError => Err(GameError::EventLoopError(format!("Lua Syntax Error: {}", error))),
ThreadStatus::MemoryError => Err(GameError::EventLoopError(format!("Lua Memory Error: {}", error))),
ThreadStatus::MsgHandlerError => Err(GameError::EventLoopError(format!("Lua Message Handler Error: {}", error))),
ThreadStatus::FileError => Err(GameError::EventLoopError(format!("Lua File Error: {}", error))),
_ => Ok(())
}
}
fn print(state: &mut State) -> c_int {
if let Some(msg) = state.to_str(1) {
log::info!("[Lua] {}", msg);
}
0
}
impl LuaScriptingState {
pub fn new() -> LuaScriptingState {
LuaScriptingState {
state: None,
state_ptr: null_mut(),
ctx_ptr: null_mut(),
game_scene: null_mut(),
}
}
pub fn update_refs(&mut self, state: *mut SharedGameState, ctx: *mut Context) {
self.state_ptr = state;
self.ctx_ptr = ctx;
}
fn load_script(mut state: &mut State, path: &str, mut script: File) -> bool {
let mut buf = Vec::with_capacity(1024);
let res = script.read_to_end(&mut buf);
if let Err(err) = res {
log::warn!("Error reading script {}: {}", path, err);
return false;
}
let name = format!("@{}", path);
let res = state.load_buffer(&buf, &name);
let res = check_status(res, &mut state);
if let Err(err) = res {
log::warn!("Error loading script {}: {}", path, err);
return false;
}
state.get_global("doukutsu");
state.get_field(-1, "_initialize_script");
state.push_value(-3);
let res = state.pcall(1, 0, 0);
if let Err((_, err)) = res {
log::warn!("Error evaluating script {}: {}", path, err);
return false;
}
log::info!("Successfully loaded Lua script: {}", path);
true
}
pub fn reload_scripts(&mut self, ctx: &mut Context) -> GameResult {
let mut state = State::new();
state.open_libs();
state.push(lua_fn!(print));
state.set_global("print");
state.push(Doukutsu { ptr: self as *mut LuaScriptingState });
state.set_global("__doukutsu");
let res = state.do_string(BOOT_SCRIPT);
check_status(res, &mut state)?;
if filesystem::exists(ctx, "/scripts/") {
let mut script_count = 0;
let mut files = filesystem::read_dir(ctx, "/scripts/")?
.filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua"));
for file in files {
let path = file.clone();
match filesystem::open(ctx, file) {
Ok(script) => {
if LuaScriptingState::load_script(&mut state, path.to_string_lossy().as_ref(), script) {
script_count += 1;
}
}
Err(err) => {
log::warn!("Error opening script {:?}: {}", path, err);
}
}
}
if script_count > 0 {
log::info!("{} Lua scripts have been loaded.", script_count);
}
}
self.state = Some(state);
Ok(())
}
}