d0656a57d3372ebcf65d2cf29bb2afee2c2698cd
[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 <sqstdio.h>
29 #include <sqstdaux.h>
30 #include <sqstdblob.h>
31 #include <sqstdsystem.h>
32 #include <sqstdmath.h>
33 #include <sqstdstring.h>
34
35 #include "timer.hpp"
36 #include "console.hpp"
37 #include "scripting/wrapper.hpp"
38 #include "scripting/wrapper_util.hpp"
39 #include "scripting/squirrel_error.hpp"
40 #include "physfs/physfs_stream.hpp"
41
42 using namespace Scripting;
43
44 ScriptManager* script_manager = 0;
45
46 static void printfunc(HSQUIRRELVM, const char* str, ...)
47 {
48   char buf[4096];
49   va_list arglist; 
50   va_start(arglist, str); 
51   vsprintf(buf, str, arglist);
52   Console::output << (const char*) buf << std::flush;
53   va_end(arglist); 
54 }
55
56 ScriptManager::ScriptManager()
57 {
58   v = sq_open(1024);
59   if(v == 0)
60     throw std::runtime_error("Couldn't initialize squirrel vm");
61
62   // register default error handlers
63   sqstd_seterrorhandlers(v);
64   // register squirrel libs
65   sq_pushroottable(v);
66   if(sqstd_register_bloblib(v) < 0)
67     throw SquirrelError(v, "Couldn't register blob lib");
68   if(sqstd_register_iolib(v) < 0)
69     throw SquirrelError(v, "Couldn't register io lib");
70   if(sqstd_register_systemlib(v) < 0)
71     throw SquirrelError(v, "Couldn't register system lib");
72   if(sqstd_register_mathlib(v) < 0)
73     throw SquirrelError(v, "Couldn't register math lib");
74   if(sqstd_register_stringlib(v) < 0)
75     throw SquirrelError(v, "Couldn't register string lib");
76
77   // register print function
78   sq_setprintfunc(v, printfunc);
79   
80   // register supertux API
81   register_supertux_wrapper(v);
82 }
83
84 ScriptManager::~ScriptManager()
85 {
86   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ++i)
87     sq_release(v, &(i->vm_obj));
88
89   sq_close(v);
90 }
91
92 HSQUIRRELVM
93 ScriptManager::create_thread()
94 {
95   HSQUIRRELVM vm = sq_newthread(v, 1024);
96   if(vm == NULL)
97     throw SquirrelError(v, "Couldn't create new VM");
98
99   // retrieve reference to thread from stack and increase refcounter
100   HSQOBJECT vm_obj;
101   sq_resetobject(&vm_obj);
102   if(SQ_FAILED(sq_getstackobj(v, -1, &vm_obj))) {
103     throw SquirrelError(v, "Couldn't get coroutine vm from stack");
104   }
105   sq_addref(v, &vm_obj);
106   sq_pop(v, 1);
107   
108   squirrel_vms.push_back(SquirrelVM(vm, vm_obj));
109
110   return vm;
111 }
112
113 void
114 ScriptManager::update()
115 {
116   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ) {
117     SquirrelVM& squirrel_vm = *i;
118     int vm_state = sq_getvmstate(squirrel_vm.vm);
119     
120     if(vm_state == SQ_VMSTATE_SUSPENDED && squirrel_vm.wakeup_time > 0 && game_time >= squirrel_vm.wakeup_time) {
121       squirrel_vm.waiting_for_events = WakeupData(NO_EVENT);
122       try {
123         if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) {
124           throw SquirrelError(squirrel_vm.vm, "Couldn't resume script");
125         }
126       } catch(std::exception& e) {
127         std::cerr << "Problem executing script: " << e.what() << "\n";
128         sq_release(v, &squirrel_vm.vm_obj);
129         i = squirrel_vms.erase(i);
130         continue;
131       }
132     }
133         
134     if (vm_state != SQ_VMSTATE_SUSPENDED) {
135       sq_release(v, &(squirrel_vm.vm_obj));
136       i = squirrel_vms.erase(i);
137     } else {
138       ++i;
139     }
140   }
141 }
142
143 void
144 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupData event, float timeout)
145 {
146   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
147   // find the VM in the list and update it
148   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ++i) {
149     SquirrelVM& squirrel_vm = *i;
150     if(squirrel_vm.vm == vm) 
151       {
152         squirrel_vm.waiting_for_events = event;
153
154         if(timeout < 0) {
155           squirrel_vm.wakeup_time = -1;
156         } else {
157           squirrel_vm.wakeup_time = game_time + timeout;
158         }
159         return;
160       }
161   }
162 }
163
164 void
165 ScriptManager::fire_wakeup_event(WakeupData  event)
166 {
167   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
168   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ++i) 
169     {
170       SquirrelVM& vm = *i;
171       if(vm.waiting_for_events.type == event.type && vm.waiting_for_events.type != NO_EVENT)
172         {
173           vm.wakeup_time = game_time;
174           break;
175         }
176     }
177 }
178
179 void
180 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupEvent event, float timeout)
181 {
182   set_wakeup_event(vm, WakeupData(event), timeout);
183 }
184
185 void
186 ScriptManager::fire_wakeup_event(WakeupEvent event)
187 {
188   fire_wakeup_event(WakeupData(event));
189 }
190
191 ScriptManager::SquirrelVM::SquirrelVM(HSQUIRRELVM arg_vm, HSQOBJECT arg_obj)
192   : vm(arg_vm), vm_obj(arg_obj)
193 {
194   waiting_for_events = WakeupData(NO_EVENT);
195   wakeup_time        = 0;
196 }
197