hopefully fixed the crash on exit, keep sectors script bundled in the sector and...
[supertux.git] / src / script_manager.cpp
1 //  $Id: main.cpp 3275 2006-04-09 00:32:34Z sommer $
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
5 //
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.
10 //
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.
15 // 
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
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "script_manager.hpp"
23
24 #include <stdarg.h>
25 #include <stdexcept>
26 #include <sstream>
27 #include <fstream>
28 #include <sqstdaux.h>
29 #include <sqstdblob.h>
30 #include <sqstdmath.h>
31 #include <sqstdstring.h>
32
33 #include "timer.hpp"
34 #include "console.hpp"
35 #include "scripting/wrapper.hpp"
36 #include "scripting/wrapper_util.hpp"
37 #include "scripting/squirrel_error.hpp"
38 #include "physfs/physfs_stream.hpp"
39
40 using namespace Scripting;
41
42 ScriptManager* ScriptManager::instance = NULL;
43
44 static void printfunc(HSQUIRRELVM, const char* str, ...)
45 {
46   char buf[4096];
47   va_list arglist; 
48   va_start(arglist, str); 
49   vsprintf(buf, str, arglist);
50   Console::output << (const char*) buf << std::flush;
51   va_end(arglist); 
52 }
53
54 ScriptManager::ScriptManager()
55   : parent(NULL)
56 {
57   vm = sq_open(1024);
58   if(vm == 0)
59     throw std::runtime_error("Couldn't initialize squirrel vm");
60   sq_setforeignptr(vm, (SQUserPointer) this);
61
62   // register squirrel libs
63   sq_pushroottable(vm);
64   if(sqstd_register_bloblib(vm) < 0)
65     throw SquirrelError(vm, "Couldn't register blob lib");
66   if(sqstd_register_mathlib(vm) < 0)
67     throw SquirrelError(vm, "Couldn't register math lib");
68   if(sqstd_register_stringlib(vm) < 0)
69     throw SquirrelError(vm, "Couldn't register string lib");
70   // register supertux API
71   register_supertux_wrapper(vm);
72   sq_pop(vm, 1);
73
74   // register print function
75   sq_setprintfunc(vm, printfunc);
76   // register default error handlers
77   sqstd_seterrorhandlers(vm); 
78 }
79
80 ScriptManager::ScriptManager(ScriptManager* parent)
81 {
82   this->parent = parent;
83   vm = parent->vm;
84   parent->childs.push_back(this);
85 }
86
87 ScriptManager::~ScriptManager()
88 {
89   for(SquirrelVMs::iterator i = squirrel_vms.begin();
90       i != squirrel_vms.end(); ++i)
91     sq_release(vm, &(i->vm_obj));
92
93   if(parent != NULL) {
94     parent->childs.erase(
95         std::remove(parent->childs.begin(), parent->childs.end(), this),
96         parent->childs.end());
97   } else {
98     sq_close(vm);
99   }
100 }
101
102 HSQUIRRELVM
103 ScriptManager::create_thread(bool leave_thread_on_stack)
104 {
105   HSQUIRRELVM new_vm = sq_newthread(vm, 1024);
106   if(new_vm == NULL)
107     throw SquirrelError(vm, "Couldn't create new VM");
108   sq_setforeignptr(new_vm, (SQUserPointer) this);
109
110   // retrieve reference to thread from stack and increase refcounter
111   HSQOBJECT vm_obj;
112   sq_resetobject(&vm_obj);
113   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_obj))) {
114     throw SquirrelError(vm, "Couldn't get coroutine vm from stack");
115   }
116   sq_addref(vm, &vm_obj);
117
118   if(!leave_thread_on_stack)
119     sq_pop(vm, 1);
120   
121   squirrel_vms.push_back(SquirrelVM(new_vm, vm_obj));
122
123   return new_vm;
124 }
125
126 void
127 ScriptManager::update()
128 {
129   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ) {
130     SquirrelVM& squirrel_vm = *i;
131     int vm_state = sq_getvmstate(squirrel_vm.vm);
132     
133     if(vm_state == SQ_VMSTATE_SUSPENDED && squirrel_vm.wakeup_time > 0 && game_time >= squirrel_vm.wakeup_time) {
134       squirrel_vm.waiting_for_events = WakeupData(NO_EVENT);
135       try {
136         if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) {
137           throw SquirrelError(squirrel_vm.vm, "Couldn't resume script");
138         }
139       } catch(std::exception& e) {
140         std::cerr << "Problem executing script: " << e.what() << "\n";
141         sq_release(vm, &squirrel_vm.vm_obj);
142         i = squirrel_vms.erase(i);
143         continue;
144       }
145     }
146         
147     if (vm_state != SQ_VMSTATE_SUSPENDED) {
148       sq_release(vm, &(squirrel_vm.vm_obj));
149       i = squirrel_vms.erase(i);
150     } else {
151       ++i;
152     }
153   }
154 }
155
156 void
157 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupData event, float timeout)
158 {
159   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
160   // find the VM in the list and update it
161   for(SquirrelVMs::iterator i = squirrel_vms.begin();
162       i != squirrel_vms.end(); ++i) {
163     SquirrelVM& squirrel_vm = *i;
164     if(squirrel_vm.vm == vm) 
165       {
166         squirrel_vm.waiting_for_events = event;
167
168         if(timeout < 0) {
169           squirrel_vm.wakeup_time = -1;
170         } else {
171           squirrel_vm.wakeup_time = game_time + timeout;
172         }
173         return;
174       }
175   }
176 }
177
178 void
179 ScriptManager::fire_wakeup_event(WakeupData  event)
180 {
181   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
182   for(SquirrelVMs::iterator i = squirrel_vms.begin();
183       i != squirrel_vms.end(); ++i) {
184     SquirrelVM& vm = *i;
185     if(vm.waiting_for_events.type == event.type
186         && vm.waiting_for_events.type != NO_EVENT) {
187       vm.wakeup_time = game_time;
188       break;
189     }
190   }
191
192   for(std::vector<ScriptManager*>::iterator i = childs.begin();
193       i != childs.end(); ++i) {
194     ScriptManager* child = *i;
195     child->fire_wakeup_event(event);
196   }
197 }
198
199 void
200 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupEvent event, float timeout)
201 {
202   set_wakeup_event(vm, WakeupData(event), timeout);
203 }
204
205 void
206 ScriptManager::fire_wakeup_event(WakeupEvent event)
207 {
208   fire_wakeup_event(WakeupData(event));
209 }
210
211 ScriptManager::SquirrelVM::SquirrelVM(HSQUIRRELVM arg_vm, HSQOBJECT arg_obj)
212   : vm(arg_vm), vm_obj(arg_obj)
213 {
214   waiting_for_events = WakeupData(NO_EVENT);
215   wakeup_time        = 0;
216 }
217