some cleanups and changes to miniswig and scripting code
[supertux.git] / src / scripting / script_interpreter.cpp
1 #include <config.h>
2
3 #include "script_interpreter.hpp"
4
5 #include <stdarg.h>
6 #include <stdexcept>
7 #include <sstream>
8 #include <fstream>
9 #include <sqstdio.h>
10 #include <sqstdaux.h>
11 #include <sqstdblob.h>
12 #include <sqstdsystem.h>
13 #include <sqstdmath.h>
14 #include <sqstdstring.h>
15
16 #include "wrapper.hpp"
17 #include "wrapper_util.hpp"
18 #include "sector.hpp"
19 #include "file_system.hpp"
20 #include "game_session.hpp"
21 #include "resources.hpp"
22 #include "physfs/physfs_stream.hpp"
23 #include "object/text_object.hpp"
24 #include "object/scripted_object.hpp"
25 #include "object/display_effect.hpp"
26 #include "scripting/sound.hpp"
27 #include "scripting/scripted_object.hpp"
28 #include "scripting/display_effect.hpp"
29 #include "scripting/squirrel_error.hpp"
30
31 static void printfunc(HSQUIRRELVM, const char* str, ...)
32 {
33   va_list arglist;
34   va_start(arglist, str);
35   vprintf(str, arglist);
36   va_end(arglist);
37 }
38
39 ScriptInterpreter* ScriptInterpreter::_current = 0;
40
41 ScriptInterpreter::ScriptInterpreter(const std::string& new_working_directory)
42   : working_directory(new_working_directory), sound(0), level(0)
43 {
44   v = sq_open(1024);
45   if(v == 0)
46     throw std::runtime_error("Couldn't initialize squirrel vm");
47
48   // register default error handlers
49   sqstd_seterrorhandlers(v);
50   // register squirrel libs
51   sq_pushroottable(v);
52   if(sqstd_register_bloblib(v) < 0)
53     throw Scripting::SquirrelError(v, "Couldn't register blob lib");
54   if(sqstd_register_iolib(v) < 0)
55     throw Scripting::SquirrelError(v, "Couldn't register io lib");
56   if(sqstd_register_systemlib(v) < 0)
57     throw Scripting::SquirrelError(v, "Couldn't register system lib");
58   if(sqstd_register_mathlib(v) < 0)
59     throw Scripting::SquirrelError(v, "Couldn't register math lib");
60   if(sqstd_register_stringlib(v) < 0)
61     throw Scripting::SquirrelError(v, "Couldn't register string lib");
62
63   // register print function
64   sq_setprintfunc(v, printfunc);
65   
66   // register supertux API
67   Scripting::register_supertux_wrapper(v);
68
69   // expose some "global" objects
70   sound = new Scripting::Sound();
71   expose_object(sound, "Sound");
72   
73   level = new Scripting::Level();
74   expose_object(level, "Level");
75 }
76
77 void
78 ScriptInterpreter::register_sector(Sector* sector)
79 {
80   // expose ScriptedObjects to the script
81   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
82       i != sector->gameobjects.end(); ++i) {
83     GameObject* object = *i;
84     Scripting::ScriptedObject* scripted_object
85       = dynamic_cast<Scripting::ScriptedObject*> (object);
86     if(!scripted_object)
87       continue;
88     
89     expose_object(scripted_object, scripted_object->get_name());
90   }
91   
92   TextObject* text_object = new TextObject();
93   sector->add_object(text_object);
94   Scripting::Text* text = static_cast<Scripting::Text*> (text_object);
95   expose_object(text, "Text");
96   
97   DisplayEffect* display_effect = new DisplayEffect();
98   sector->add_object(display_effect);
99   Scripting::DisplayEffect* display_effect_api
100     = static_cast<Scripting::DisplayEffect*> (display_effect);
101   expose_object(display_effect_api, "DisplayEffect");
102 }
103
104 ScriptInterpreter::~ScriptInterpreter()
105 {
106   sq_close(v);
107   delete sound;
108   delete level;
109 }
110
111 static SQInteger squirrel_read_char(SQUserPointer file)
112 {
113   std::istream* in = reinterpret_cast<std::istream*> (file);
114   char c = in->get();
115   if(in->eof())
116     return 0;    
117   return c;
118 }
119
120 void
121 ScriptInterpreter::run_script(std::istream& in, const std::string& sourcename,
122         bool remove_when_terminated)
123 {
124   printf("Stackbefore:\n");
125   print_squirrel_stack(v);
126   if(sq_compile(v, squirrel_read_char, &in, sourcename.c_str(), true) < 0)
127     throw Scripting::SquirrelError(v, "Couldn't parse script");
128  
129   _current = this;
130   sq_push(v, -2);
131   if(sq_call(v, 1, false) < 0)
132     throw Scripting::SquirrelError(v, "Couldn't start script");
133   _current = 0;
134   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
135     if(remove_when_terminated) {
136       remove_me();
137     }
138     printf("ended.\n");
139     // remove closure from stack
140     sq_pop(v, 1);
141   }
142   printf("After:\n");
143   print_squirrel_stack(v);
144 }
145
146 void
147 ScriptInterpreter::set_wakeup_time(float seconds)
148 {
149   wakeup_timer.start(seconds);
150 }
151
152 void
153 ScriptInterpreter::update(float )
154 {
155   if(!wakeup_timer.check())
156     return;
157   
158   _current = this;
159   if(sq_wakeupvm(v, false, false) < 0)
160     throw Scripting::SquirrelError(v, "Couldn't resume script");
161   _current = 0;
162   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
163     printf("script ended...\n");
164     remove_me();
165   }
166 }
167
168 void
169 ScriptInterpreter::draw(DrawingContext& )
170 {
171 }
172
173 void
174 ScriptInterpreter::add_script_object(Sector* sector, const std::string& name,
175     const std::string& script)
176 {
177   try {
178     std::string workdir = GameSession::current()->get_working_directory();
179     std::auto_ptr<ScriptInterpreter> interpreter(
180                 new ScriptInterpreter(workdir));
181     interpreter->register_sector(sector);
182
183     // load default.nut file if it exists
184     try {
185       std::string filename = workdir + "/default.nut";
186       IFileStream in(filename);
187       interpreter->run_script(in, filename, false);
188     } catch(std::exception& e) {
189       // nothing
190     }
191         
192     std::istringstream in(script);
193     interpreter->run_script(in, name);
194     sector->add_object(interpreter.release());
195   } catch(std::exception& e) {
196     std::cerr << "Couldn't start '" << name << "' script: " << e.what() << "\n";
197   }
198 }
199