4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 #include "script_manager.hpp"
29 #include <sqstdblob.h>
30 #include <sqstdmath.h>
31 #include <sqstdstring.h>
34 #include "console.hpp"
36 #include "scripting/wrapper.hpp"
37 #include "scripting/wrapper_util.hpp"
38 #include "scripting/squirrel_error.hpp"
39 #include "physfs/physfs_stream.hpp"
41 using namespace Scripting;
43 ScriptManager* ScriptManager::instance = NULL;
45 static void printfunc(HSQUIRRELVM, const char* str, ...)
49 va_start(arglist, str);
50 vsprintf(buf, str, arglist);
51 Console::output << (const char*) buf << std::flush;
55 ScriptManager::ScriptManager()
60 throw std::runtime_error("Couldn't initialize squirrel vm");
61 sq_setforeignptr(vm, (SQUserPointer) this);
66 debugger = sq_rdbg_init(vm, 1234, SQFalse);
68 throw SquirrelError(vm, "Couldn't initialize suirrel remote debugger");
70 sq_enabledebuginfo(vm, SQTrue);
71 log_info << "Waiting for debug client..." << std::endl;
72 if(!SQ_SUCCEEDED(sq_rdbg_waitforconnections(debugger))) {
73 throw SquirrelError(vm, "Waiting for debug clients failed");
75 log_info << "debug client connected." << std::endl;
79 // register squirrel libs
81 if(sqstd_register_bloblib(vm) < 0)
82 throw SquirrelError(vm, "Couldn't register blob lib");
83 if(sqstd_register_mathlib(vm) < 0)
84 throw SquirrelError(vm, "Couldn't register math lib");
85 if(sqstd_register_stringlib(vm) < 0)
86 throw SquirrelError(vm, "Couldn't register string lib");
87 // register supertux API
88 register_supertux_wrapper(vm);
91 // register print function
92 sq_setprintfunc(vm, printfunc);
93 // register default error handlers
94 sqstd_seterrorhandlers(vm);
96 // try to load default script
98 std::string filename = "scripts/default.nut";
99 IFileStream stream(filename);
100 Scripting::compile_and_run(vm, stream, filename);
101 } catch(std::exception& e) {
102 log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
106 ScriptManager::ScriptManager(ScriptManager* parent)
111 this->parent = parent;
113 parent->childs.push_back(this);
116 ScriptManager::~ScriptManager()
118 for(SquirrelVMs::iterator i = squirrel_vms.begin();
119 i != squirrel_vms.end(); ++i)
120 sq_release(vm, &(i->vm_obj));
123 parent->childs.erase(
124 std::remove(parent->childs.begin(), parent->childs.end(), this),
125 parent->childs.end());
128 sq_rdbg_shutdown(debugger);
135 ScriptManager::create_thread(bool leave_thread_on_stack)
137 HSQUIRRELVM new_vm = sq_newthread(vm, 64);
139 throw SquirrelError(vm, "Couldn't create new VM");
140 sq_setforeignptr(new_vm, (SQUserPointer) this);
142 // retrieve reference to thread from stack and increase refcounter
144 sq_resetobject(&vm_obj);
145 if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_obj))) {
146 throw SquirrelError(vm, "Couldn't get coroutine vm from stack");
148 sq_addref(vm, &vm_obj);
150 if(!leave_thread_on_stack)
153 squirrel_vms.push_back(SquirrelVM(new_vm, vm_obj));
159 ScriptManager::update()
163 sq_rdbg_update(debugger);
166 for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ) {
167 SquirrelVM& squirrel_vm = *i;
168 int vm_state = sq_getvmstate(squirrel_vm.vm);
170 if(vm_state == SQ_VMSTATE_SUSPENDED
171 && squirrel_vm.wakeup_time > 0
172 && game_time >= squirrel_vm.wakeup_time) {
173 squirrel_vm.waiting_for_events = WakeupData(NO_EVENT);
174 squirrel_vm.wakeup_time = 0;
177 if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) {
178 throw SquirrelError(squirrel_vm.vm, "Couldn't resume script");
180 } catch(std::exception& e) {
181 std::cerr << "Problem executing script: " << e.what() << "\n";
182 sq_release(vm, &squirrel_vm.vm_obj);
183 i = squirrel_vms.erase(i);
188 if (vm_state != SQ_VMSTATE_SUSPENDED) {
189 sq_release(vm, &(squirrel_vm.vm_obj));
190 i = squirrel_vms.erase(i);
198 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupData event, float timeout)
200 assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
201 // find the VM in the list and update it
202 for(SquirrelVMs::iterator i = squirrel_vms.begin();
203 i != squirrel_vms.end(); ++i) {
204 SquirrelVM& squirrel_vm = *i;
205 if(squirrel_vm.vm == vm)
207 squirrel_vm.waiting_for_events = event;
210 squirrel_vm.wakeup_time = -1;
212 squirrel_vm.wakeup_time = game_time + timeout;
220 ScriptManager::fire_wakeup_event(WakeupData event)
222 assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
223 for(SquirrelVMs::iterator i = squirrel_vms.begin();
224 i != squirrel_vms.end(); ++i) {
226 if(vm.waiting_for_events.type == event.type
227 && vm.waiting_for_events.type != NO_EVENT) {
228 vm.wakeup_time = game_time;
233 for(std::vector<ScriptManager*>::iterator i = childs.begin();
234 i != childs.end(); ++i) {
235 ScriptManager* child = *i;
236 child->fire_wakeup_event(event);
241 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupEvent event, float timeout)
243 set_wakeup_event(vm, WakeupData(event), timeout);
247 ScriptManager::fire_wakeup_event(WakeupEvent event)
249 fire_wakeup_event(WakeupData(event));
252 ScriptManager::SquirrelVM::SquirrelVM(HSQUIRRELVM arg_vm, HSQOBJECT arg_obj)
253 : vm(arg_vm), vm_obj(arg_obj)
255 waiting_for_events = WakeupData(NO_EVENT);