1// SuperTux
2// Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "squirrel/squirrel_virtual_machine.hpp"
18
19#include <sqstdaux.h>
20#include <sqstdblob.h>
21#include <sqstdmath.h>
22#include <sqstdstring.h>
23#include <cstring>
24#include <stdarg.h>
25#include <stdio.h>
26
27#include "physfs/ifile_stream.hpp"
28#include "scripting/wrapper.hpp"
29#include "squirrel/squirrel_error.hpp"
30#include "squirrel/squirrel_thread_queue.hpp"
31#include "squirrel/squirrel_scheduler.hpp"
32#include "squirrel_util.hpp"
33#include "supertux/console.hpp"
34#include "supertux/globals.hpp"
35#include "util/log.hpp"
36
37#ifdef ENABLE_SQDBG
38# include "../../external/squirrel/sqdbg/sqrdbg.h"
39namespace {
40HSQREMOTEDBG debugger = nullptr;
41} // namespace
42#endif
43
44namespace {
45
46#ifdef __clang__
47__attribute__((__format__ (__printf__, 2, 0)))
48#endif
49void printfunc(HSQUIRRELVM, const char* fmt, ...)
50{
51 char buf[4096];
52 char separator[] = "\n";
53 va_list arglist;
54 va_start(arglist, fmt);
55 vsnprintf(buf, sizeof(buf), fmt, arglist);
56 char* ptr = strtok(buf, separator);
57 while (ptr != nullptr)
58 {
59 ConsoleBuffer::output << "[SCRIPTING] " << ptr << std::endl;
60 ptr = strtok(nullptr, separator);
61 }
62 va_end(arglist);
63}
64
65} // namespace
66
67SquirrelVirtualMachine::SquirrelVirtualMachine(bool enable_debugger) :
68 m_vm(),
69 m_screenswitch_queue(),
70 m_scheduler()
71{
72 sq_setsharedforeignptr(m_vm.get_vm(), this);
73
74 m_screenswitch_queue = std::make_unique<SquirrelThreadQueue>(m_vm);
75 m_scheduler = std::make_unique<SquirrelScheduler>(m_vm);
76
77 if (enable_debugger) {
78#ifdef ENABLE_SQDBG
79 sq_enabledebuginfo(m_vm.get_vm(), SQTrue);
80 debugger = sq_rdbg_init(m_vm.get_vm(), 1234, SQFalse);
81 if (debugger == nullptr)
82 throw SquirrelError(m_vm.get_vm(), "Couldn't initialize squirrel debugger");
83
84 sq_enabledebuginfo(m_vm.get_vm(), SQTrue);
85 log_info << "Waiting for debug client..." << std::endl;
86 if (SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
87 throw SquirrelError(m_vm.get_vm(), "Waiting for debug clients failed");
88 log_info << "debug client connected." << std::endl;
89#endif
90 }
91
92 sq_pushroottable(m_vm.get_vm());
93 if (SQ_FAILED(sqstd_register_bloblib(m_vm.get_vm())))
94 throw SquirrelError(m_vm.get_vm(), "Couldn't register blob lib");
95 if (SQ_FAILED(sqstd_register_mathlib(m_vm.get_vm())))
96 throw SquirrelError(m_vm.get_vm(), "Couldn't register math lib");
97 if (SQ_FAILED(sqstd_register_stringlib(m_vm.get_vm())))
98 throw SquirrelError(m_vm.get_vm(), "Couldn't register string lib");
99
100 // remove rand and srand calls from sqstdmath, we'll provide our own
101 m_vm.delete_table_entry("srand");
102 m_vm.delete_table_entry("rand");
103
104 // register supertux API
105 scripting::register_supertux_wrapper(m_vm.get_vm());
106
107 sq_pop(m_vm.get_vm(), 1);
108
109 // register print function
110 sq_setprintfunc(m_vm.get_vm(), printfunc, printfunc);
111 // register default error handlers
112 sqstd_seterrorhandlers(m_vm.get_vm());
113
114 // try to load default script
115 try {
116 std::string filename = "scripts/default.nut";
117 IFileStream stream(filename);
118 compile_and_run(m_vm.get_vm(), stream, filename);
119 } catch(std::exception& e) {
120 log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
121 }
122}
123
124SquirrelVirtualMachine::~SquirrelVirtualMachine()
125{
126#ifdef ENABLE_SQDBG
127 if (debugger != nullptr) {
128 sq_rdbg_shutdown(debugger);
129 debugger = nullptr;
130 }
131#endif
132}
133
134void
135SquirrelVirtualMachine::update(float dt_sec)
136{
137 update_debugger();
138 m_scheduler->update(g_game_time);
139}
140
141void
142SquirrelVirtualMachine::update_debugger()
143{
144#ifdef ENABLE_SQDBG
145 if (debugger != nullptr)
146 sq_rdbg_update(debugger);
147#endif
148}
149
150void
151SquirrelVirtualMachine::wait_for_seconds(HSQUIRRELVM vm, float seconds)
152{
153 m_scheduler->schedule_thread(vm, g_game_time + seconds);
154}
155
156void
157SquirrelVirtualMachine::wait_for_screenswitch(HSQUIRRELVM vm)
158{
159 m_screenswitch_queue->add(vm);
160}
161
162void
163SquirrelVirtualMachine::wakeup_screenswitch()
164{
165 m_screenswitch_queue->wakeup();
166}
167
168/* EOF */
169