display a big warning for people who try to install the game
[supertux.git] / src / script_manager.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 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 "log.hpp"
36 #include "scripting/wrapper.hpp"
37 #include "scripting/wrapper_util.hpp"
38 #include "scripting/squirrel_error.hpp"
39 #include "physfs/physfs_stream.hpp"
40
41 using namespace Scripting;
42
43 ScriptManager* ScriptManager::instance = NULL;
44
45 static void printfunc(HSQUIRRELVM, const char* str, ...)
46 {
47   char buf[4096];
48   va_list arglist; 
49   va_start(arglist, str); 
50   vsprintf(buf, str, arglist);
51   Console::output << (const char*) buf << std::flush;
52   va_end(arglist); 
53 }
54
55 ScriptManager::ScriptManager()
56   : parent(NULL)
57 {
58   vm = sq_open(64);
59   if(vm == 0)
60     throw std::runtime_error("Couldn't initialize squirrel vm");
61   sq_setforeignptr(vm, (SQUserPointer) this);
62
63 #ifdef ENABLE_SQDBG
64   debugger = NULL;
65   /*
66   debugger = sq_rdbg_init(vm, 1234, SQFalse);
67   if(debugger == NULL)
68     throw SquirrelError(vm, "Couldn't initialize suirrel remote debugger");
69
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");
74   }
75   log_info << "debug client connected." << std::endl;
76   */
77 #endif
78
79   // register squirrel libs
80   sq_pushroottable(vm);
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);
89   sq_pop(vm, 1);
90
91   // register print function
92   sq_setprintfunc(vm, printfunc);
93   // register default error handlers
94   sqstd_seterrorhandlers(vm); 
95
96   // try to load default script
97   try {
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;
103   }
104 }
105
106 ScriptManager::ScriptManager(ScriptManager* parent)
107 {
108 #ifdef ENABLE_SQDBG
109   debugger = NULL;
110 #endif
111   this->parent = parent;
112   vm = parent->vm;
113   parent->childs.push_back(this);
114 }
115
116 ScriptManager::~ScriptManager()
117 {
118   for(SquirrelVMs::iterator i = squirrel_vms.begin();
119       i != squirrel_vms.end(); ++i)
120     sq_release(vm, &(i->vm_obj));
121
122   if(parent != NULL) {
123     parent->childs.erase(
124         std::remove(parent->childs.begin(), parent->childs.end(), this),
125         parent->childs.end());
126   } else {
127 #ifdef ENABLE_SQDBG
128     sq_rdbg_shutdown(debugger);
129 #endif
130     sq_close(vm);
131   }
132 }
133
134 HSQUIRRELVM
135 ScriptManager::create_thread(bool leave_thread_on_stack)
136 {
137   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
138   if(new_vm == NULL)
139     throw SquirrelError(vm, "Couldn't create new VM");
140   sq_setforeignptr(new_vm, (SQUserPointer) this);
141
142   // retrieve reference to thread from stack and increase refcounter
143   HSQOBJECT vm_obj;
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");
147   }
148   sq_addref(vm, &vm_obj);
149
150   if(!leave_thread_on_stack)
151     sq_pop(vm, 1);
152   
153   squirrel_vms.push_back(SquirrelVM(new_vm, vm_obj));
154
155   return new_vm;
156 }
157
158 void
159 ScriptManager::update()
160 {
161 #ifdef ENABLE_SQDBG
162   if(debugger != NULL)
163     sq_rdbg_update(debugger);
164 #endif
165
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);
169     
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;
175       
176       try {
177         if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) {
178           throw SquirrelError(squirrel_vm.vm, "Couldn't resume script");
179         }
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);
184         continue;
185       }
186     }
187         
188     if (vm_state != SQ_VMSTATE_SUSPENDED) {
189       sq_release(vm, &(squirrel_vm.vm_obj));
190       i = squirrel_vms.erase(i);
191     } else {
192       ++i;
193     }
194   }
195 }
196
197 void
198 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupData event, float timeout)
199 {
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) 
206       {
207         squirrel_vm.waiting_for_events = event;
208
209         if(timeout < 0) {
210           squirrel_vm.wakeup_time = -1;
211         } else {
212           squirrel_vm.wakeup_time = game_time + timeout;
213         }
214         return;
215       }
216   }
217 }
218
219 void
220 ScriptManager::fire_wakeup_event(WakeupData  event)
221 {
222   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
223   for(SquirrelVMs::iterator i = squirrel_vms.begin();
224       i != squirrel_vms.end(); ++i) {
225     SquirrelVM& vm = *i;
226     if(vm.waiting_for_events.type == event.type
227         && vm.waiting_for_events.type != NO_EVENT) {
228       vm.wakeup_time = game_time;
229       break;
230     }
231   }
232
233   for(std::vector<ScriptManager*>::iterator i = childs.begin();
234       i != childs.end(); ++i) {
235     ScriptManager* child = *i;
236     child->fire_wakeup_event(event);
237   }
238 }
239
240 void
241 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupEvent event, float timeout)
242 {
243   set_wakeup_event(vm, WakeupData(event), timeout);
244 }
245
246 void
247 ScriptManager::fire_wakeup_event(WakeupEvent event)
248 {
249   fire_wakeup_event(WakeupData(event));
250 }
251
252 ScriptManager::SquirrelVM::SquirrelVM(HSQUIRRELVM arg_vm, HSQOBJECT arg_obj)
253   : vm(arg_vm), vm_obj(arg_obj)
254 {
255   waiting_for_events = WakeupData(NO_EVENT);
256   wakeup_time        = 0;
257 }
258